001    /*
002     * Copyright 2010-2015 JetBrains s.r.o.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.jetbrains.kotlin.resolve;
018    
019    import com.google.common.collect.Lists;
020    import com.google.common.collect.Maps;
021    import com.google.common.collect.Sets;
022    import com.intellij.lang.ASTNode;
023    import com.intellij.openapi.util.Pair;
024    import com.intellij.psi.PsiElement;
025    import org.jetbrains.annotations.NotNull;
026    import org.jetbrains.annotations.Nullable;
027    import org.jetbrains.kotlin.descriptors.*;
028    import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
029    import org.jetbrains.kotlin.diagnostics.*;
030    import org.jetbrains.kotlin.lexer.JetModifierKeywordToken;
031    import org.jetbrains.kotlin.lexer.JetTokens;
032    import org.jetbrains.kotlin.name.FqName;
033    import org.jetbrains.kotlin.name.Name;
034    import org.jetbrains.kotlin.psi.*;
035    import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
036    import org.jetbrains.kotlin.resolve.constants.StringValue;
037    
038    import java.util.*;
039    
040    import static org.jetbrains.kotlin.diagnostics.Errors.*;
041    import static org.jetbrains.kotlin.lexer.JetTokens.*;
042    import static org.jetbrains.kotlin.psi.JetStubbedPsiUtil.getContainingDeclaration;
043    import static org.jetbrains.kotlin.resolve.DescriptorUtils.isCompanionObject;
044    import static org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumEntry;
045    
046    public class ModifiersChecker {
047        private static final Collection<JetModifierKeywordToken> MODALITY_MODIFIERS =
048                Lists.newArrayList(ABSTRACT_KEYWORD, OPEN_KEYWORD, FINAL_KEYWORD, OVERRIDE_KEYWORD);
049    
050        private static final Collection<JetModifierKeywordToken> VISIBILITY_MODIFIERS =
051                Lists.newArrayList(PRIVATE_KEYWORD, PROTECTED_KEYWORD, PUBLIC_KEYWORD, INTERNAL_KEYWORD);
052    
053        public static void reportIllegalModifiers(
054                @Nullable JetModifierList modifierList,
055                @NotNull Collection<JetModifierKeywordToken> illegalModifiers,
056                @NotNull BindingTrace trace
057        ) {
058            if (modifierList == null) return;
059    
060            for (JetModifierKeywordToken modifierToken : illegalModifiers) {
061                if (modifierList.hasModifier(modifierToken)) {
062                    PsiElement modifierPsi = modifierList.getModifier(modifierToken);
063                    assert modifierPsi != null;
064                    trace.report(ILLEGAL_MODIFIER.on(modifierPsi, modifierToken));
065                }
066            }
067        }
068    
069        public static void checkIncompatibleModifiers(
070                @Nullable JetModifierList modifierList,
071                @NotNull BindingTrace trace,
072                @NotNull Collection<JetModifierKeywordToken> availableModifiers,
073                @NotNull Collection<JetModifierKeywordToken>... availableCombinations
074        ) {
075            if (modifierList == null) return;
076            Collection<JetModifierKeywordToken> presentModifiers = Sets.newLinkedHashSet();
077            for (JetModifierKeywordToken modifier : availableModifiers) {
078                if (modifierList.hasModifier(modifier)) {
079                    presentModifiers.add(modifier);
080                }
081            }
082            checkRepeatedModifiers(modifierList, trace, availableModifiers);
083    
084            if (presentModifiers.size() == 1) {
085                return;
086            }
087            for (Collection<JetModifierKeywordToken> combination : availableCombinations) {
088                if (presentModifiers.containsAll(combination) && combination.containsAll(presentModifiers)) {
089                    return;
090                }
091            }
092            for (JetModifierKeywordToken token : presentModifiers) {
093                trace.report(Errors.INCOMPATIBLE_MODIFIERS.on(modifierList.getModifierNode(token).getPsi(), presentModifiers));
094            }
095        }
096    
097        private static void checkRepeatedModifiers(
098                @NotNull JetModifierList modifierList,
099                @NotNull BindingTrace trace,
100                @NotNull Collection<JetModifierKeywordToken> availableModifiers
101        ) {
102            for (JetModifierKeywordToken token : availableModifiers) {
103                if (!modifierList.hasModifier(token)) continue;
104    
105                List<ASTNode> nodesOfRepeatedTokens = Lists.newArrayList();
106                ASTNode node = modifierList.getNode().getFirstChildNode();
107                while (node != null) {
108                    if (node.getElementType() == token) {
109                        nodesOfRepeatedTokens.add(node);
110                    }
111                    node = node.getTreeNext();
112                }
113                if (nodesOfRepeatedTokens.size() > 1) {
114                    for (ASTNode repeatedToken : nodesOfRepeatedTokens) {
115                        trace.report(REPEATED_MODIFIER.on(repeatedToken.getPsi(), token));
116                    }
117                }
118            }
119        }
120    
121        public static void checkIncompatibleVarianceModifiers(@Nullable JetModifierList modifierList, @NotNull BindingTrace trace) {
122            checkIncompatibleModifiers(modifierList, trace, Arrays.asList(JetTokens.IN_KEYWORD, JetTokens.OUT_KEYWORD));
123        }
124    
125        @NotNull
126        private final BindingTrace trace;
127        @NotNull
128        private final AdditionalCheckerProvider additionalCheckerProvider;
129    
130        public ModifiersChecker(@NotNull BindingTrace trace, @NotNull AdditionalCheckerProvider additionalCheckerProvider) {
131            this.trace = trace;
132            this.additionalCheckerProvider = additionalCheckerProvider;
133        }
134    
135        public static ModifiersChecker create(@NotNull BindingTrace trace, @NotNull AdditionalCheckerProvider provider) {
136            return new ModifiersChecker(trace, provider);
137        }
138    
139        public void checkModifiersForDeclaration(@NotNull JetDeclaration modifierListOwner, @NotNull MemberDescriptor descriptor) {
140            if (modifierListOwner instanceof JetEnumEntry) {
141                reportIllegalModifiers(modifierListOwner, Arrays.asList(MODIFIER_KEYWORDS_ARRAY));
142            }
143            else {
144                checkInnerModifier(modifierListOwner, descriptor);
145                checkDefaultModifier(modifierListOwner);
146                checkModalityModifiers(modifierListOwner);
147                checkVisibilityModifiers(modifierListOwner, descriptor);
148                checkVarianceModifiersOfTypeParameters(modifierListOwner);
149            }
150            checkPlatformNameApplicability(descriptor);
151            runAnnotationCheckers(modifierListOwner, descriptor);
152        }
153    
154        public void checkModifiersForLocalDeclaration(@NotNull JetDeclaration modifierListOwner, @NotNull DeclarationDescriptor descriptor) {
155            reportIllegalModalityModifiers(modifierListOwner);
156            reportIllegalVisibilityModifiers(modifierListOwner);
157            checkPlatformNameApplicability(descriptor);
158            runAnnotationCheckers(modifierListOwner, descriptor);
159        }
160    
161        public void reportIllegalModalityModifiers(@NotNull JetModifierListOwner modifierListOwner) {
162            reportIllegalModifiers(modifierListOwner, MODALITY_MODIFIERS);
163        }
164    
165        public void reportIllegalVisibilityModifiers(@NotNull JetModifierListOwner modifierListOwner) {
166            reportIllegalModifiers(modifierListOwner, VISIBILITY_MODIFIERS);
167        }
168    
169        private void checkModalityModifiers(@NotNull JetModifierListOwner modifierListOwner) {
170            JetModifierList modifierList = modifierListOwner.getModifierList();
171            if (modifierList == null) return;
172    
173            checkRedundantModifier(modifierList, Pair.create(OPEN_KEYWORD, ABSTRACT_KEYWORD), Pair.create(OPEN_KEYWORD, OVERRIDE_KEYWORD));
174    
175            checkCompatibility(modifierList, Arrays.asList(ABSTRACT_KEYWORD, OPEN_KEYWORD, FINAL_KEYWORD),
176                               Arrays.asList(ABSTRACT_KEYWORD, OPEN_KEYWORD));
177    
178            if (modifierListOwner instanceof JetObjectDeclaration) {
179                reportIllegalModalityModifiers(modifierListOwner);
180            }
181            else if (modifierListOwner instanceof JetClassOrObject) {
182                reportIllegalModifiers(modifierListOwner, Collections.singletonList(OVERRIDE_KEYWORD));
183            }
184        }
185    
186        private void checkVisibilityModifiers(@NotNull JetModifierListOwner modifierListOwner, @NotNull DeclarationDescriptor descriptor) {
187            JetModifierList modifierList = modifierListOwner.getModifierList();
188            if (modifierList == null) return;
189    
190            DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
191            if (containingDeclaration instanceof PackageFragmentDescriptor) {
192                if (modifierList.hasModifier(PROTECTED_KEYWORD)) {
193                    trace.report(Errors.PACKAGE_MEMBER_CANNOT_BE_PROTECTED.on(modifierListOwner));
194                }
195            }
196    
197            checkCompatibility(modifierList, VISIBILITY_MODIFIERS);
198        }
199    
200        private void checkInnerModifier(@NotNull JetModifierListOwner modifierListOwner, @NotNull DeclarationDescriptor descriptor) {
201            if (modifierListOwner.hasModifier(INNER_KEYWORD)) {
202                switch (checkIllegalInner(descriptor)) {
203                    case ALLOWED:
204                        break;
205                    case ILLEGAL_POSITION:
206                        reportIllegalModifiers(modifierListOwner, Collections.singletonList(INNER_KEYWORD));
207                        break;
208                    case IN_TRAIT:
209                        trace.report(INNER_CLASS_IN_TRAIT.on(modifierListOwner));
210                        break;
211                    case IN_OBJECT:
212                        trace.report(INNER_CLASS_IN_OBJECT.on(modifierListOwner));
213                        break;
214                }
215                return;
216            }
217            if (modifierListOwner instanceof JetClass && !(modifierListOwner instanceof JetEnumEntry)) {
218                JetClass aClass = (JetClass) modifierListOwner;
219                boolean localEnumError = aClass.isLocal() && aClass.isEnum();
220                if (!localEnumError && isIllegalNestedClass(descriptor)) {
221                    trace.report(NESTED_CLASS_NOT_ALLOWED.on(aClass));
222                }
223            }
224        }
225    
226        public static boolean isIllegalInner(@NotNull DeclarationDescriptor descriptor) {
227            return checkIllegalInner(descriptor) != InnerModifierCheckResult.ALLOWED;
228        }
229    
230        private enum InnerModifierCheckResult {
231            ALLOWED,
232            ILLEGAL_POSITION,
233            IN_TRAIT,
234            IN_OBJECT,
235        }
236    
237        private void checkDefaultModifier(@NotNull JetDeclaration declaration) {
238            if (declaration.hasModifier(COMPANION_KEYWORD) && !isDefaultModifierAllowed(declaration)) {
239                reportIllegalModifiers(declaration, Collections.singletonList(COMPANION_KEYWORD));
240            }
241        }
242    
243        // NOTE: just checks if this is legal context for companion modifier (Companion object descriptor can be created)
244        // COMPANION_OBJECT_NOT_ALLOWED can be reported later
245        public static boolean isDefaultModifierAllowed(@NotNull JetDeclaration declaration) {
246            if (declaration instanceof JetObjectDeclaration) {
247                JetDeclaration containingDeclaration = getContainingDeclaration(declaration);
248                if (containingDeclaration instanceof JetClassOrObject) {
249                    return true;
250                }
251            }
252            return false;
253        }
254    
255        @NotNull
256        private static InnerModifierCheckResult checkIllegalInner(DeclarationDescriptor descriptor) {
257            if (!(descriptor instanceof ClassDescriptor)) return InnerModifierCheckResult.ILLEGAL_POSITION;
258            ClassDescriptor classDescriptor = (ClassDescriptor) descriptor;
259    
260            if (classDescriptor.getKind() != ClassKind.CLASS) return InnerModifierCheckResult.ILLEGAL_POSITION;
261    
262            DeclarationDescriptor containingDeclaration = classDescriptor.getContainingDeclaration();
263            if (!(containingDeclaration instanceof ClassDescriptor)) return InnerModifierCheckResult.ILLEGAL_POSITION;
264    
265            if (DescriptorUtils.isTrait(containingDeclaration)) {
266                return InnerModifierCheckResult.IN_TRAIT;
267            }
268            else if (DescriptorUtils.isObject(containingDeclaration)) {
269                return InnerModifierCheckResult.IN_OBJECT;
270            }
271            else {
272                return InnerModifierCheckResult.ALLOWED;
273            }
274        }
275    
276        private static boolean isIllegalNestedClass(@NotNull DeclarationDescriptor descriptor) {
277            if (!(descriptor instanceof ClassDescriptor)) return false;
278            DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
279            if (!(containingDeclaration instanceof ClassDescriptor)) return false;
280            ClassDescriptor containingClass = (ClassDescriptor) containingDeclaration;
281            return containingClass.isInner() || containingClass.getContainingDeclaration() instanceof FunctionDescriptor;
282        }
283    
284        private void checkPlatformNameApplicability(@NotNull DeclarationDescriptor descriptor) {
285            AnnotationDescriptor annotation = descriptor.getAnnotations().findAnnotation(new FqName("kotlin.platform.platformName"));
286            if (annotation == null) return;
287    
288            JetAnnotationEntry annotationEntry = trace.get(BindingContext.ANNOTATION_DESCRIPTOR_TO_PSI_ELEMENT, annotation);
289            if (annotationEntry == null) return;
290    
291            if (!DescriptorUtils.isTopLevelDeclaration(descriptor) || !(descriptor instanceof FunctionDescriptor)) {
292                trace.report(INAPPLICABLE_ANNOTATION.on(annotationEntry));
293            }
294    
295            Collection<CompileTimeConstant<?>> values = annotation.getAllValueArguments().values();
296            if (!values.isEmpty()) {
297                CompileTimeConstant<?> name = values.iterator().next();
298                if (name instanceof StringValue) {
299                    String value = ((StringValue) name).getValue();
300                    if (value == null || !Name.isValidIdentifier(value)) {
301                        trace.report(ILLEGAL_PLATFORM_NAME.on(annotationEntry, String.valueOf(value)));
302                    }
303                }
304            }
305        }
306    
307        private void checkCompatibility(@Nullable JetModifierList modifierList, Collection<JetModifierKeywordToken> availableModifiers, Collection<JetModifierKeywordToken>... availableCombinations) {
308            checkIncompatibleModifiers(modifierList, trace, availableModifiers, availableCombinations);
309        }
310    
311        private void checkRedundantModifier(@NotNull JetModifierList modifierList, Pair<JetModifierKeywordToken, JetModifierKeywordToken>... redundantBundles) {
312            for (Pair<JetModifierKeywordToken, JetModifierKeywordToken> tokenPair : redundantBundles) {
313                JetModifierKeywordToken redundantModifier = tokenPair.getFirst();
314                JetModifierKeywordToken sufficientModifier = tokenPair.getSecond();
315                if (modifierList.hasModifier(redundantModifier) && modifierList.hasModifier(sufficientModifier)) {
316                    trace.report(Errors.REDUNDANT_MODIFIER.on(modifierList.getModifierNode(redundantModifier).getPsi(), redundantModifier, sufficientModifier));
317                }
318            }
319        }
320    
321        public void reportIllegalModifiers(
322                @NotNull JetModifierListOwner modifierListOwner,
323                @NotNull Collection<JetModifierKeywordToken> illegalModifiers
324        ) {
325            reportIllegalModifiers(modifierListOwner.getModifierList(), illegalModifiers, trace);
326        }
327    
328        @NotNull
329        public static Map<JetModifierKeywordToken, ASTNode> getNodesCorrespondingToModifiers(@NotNull JetModifierList modifierList, @NotNull Collection<JetModifierKeywordToken> possibleModifiers) {
330            Map<JetModifierKeywordToken, ASTNode> nodes = Maps.newHashMap();
331            for (JetModifierKeywordToken modifier : possibleModifiers) {
332                if (modifierList.hasModifier(modifier)) {
333                    nodes.put(modifier, modifierList.getModifierNode(modifier));
334                }
335            }
336            return nodes;
337        }
338    
339        @NotNull
340        public static Modality resolveModalityFromModifiers(@NotNull JetModifierListOwner modifierListOwner, @NotNull Modality defaultModality) {
341            return resolveModalityFromModifiers(modifierListOwner.getModifierList(), defaultModality);
342        }
343    
344        public static Modality resolveModalityFromModifiers(@Nullable JetModifierList modifierList, @NotNull Modality defaultModality) {
345            if (modifierList == null) return defaultModality;
346            boolean hasAbstractModifier = modifierList.hasModifier(ABSTRACT_KEYWORD);
347            boolean hasOverrideModifier = modifierList.hasModifier(OVERRIDE_KEYWORD);
348    
349            if (modifierList.hasModifier(OPEN_KEYWORD)) {
350                if (hasAbstractModifier || defaultModality == Modality.ABSTRACT) {
351                    return Modality.ABSTRACT;
352                }
353                return Modality.OPEN;
354            }
355            if (hasAbstractModifier) {
356                return Modality.ABSTRACT;
357            }
358            boolean hasFinalModifier = modifierList.hasModifier(FINAL_KEYWORD);
359            if (hasOverrideModifier && !hasFinalModifier && !(defaultModality == Modality.ABSTRACT)) {
360                return Modality.OPEN;
361            }
362            if (hasFinalModifier) {
363                return Modality.FINAL;
364            }
365            return defaultModality;
366        }
367    
368        @NotNull
369        public static Visibility resolveVisibilityFromModifiers(@NotNull JetModifierListOwner modifierListOwner, @NotNull Visibility defaultVisibility) {
370            return resolveVisibilityFromModifiers(modifierListOwner.getModifierList(), defaultVisibility);
371        }
372    
373        public static Visibility resolveVisibilityFromModifiers(@Nullable JetModifierList modifierList, @NotNull Visibility defaultVisibility) {
374            if (modifierList == null) return defaultVisibility;
375            if (modifierList.hasModifier(PRIVATE_KEYWORD)) return Visibilities.PRIVATE;
376            if (modifierList.hasModifier(PUBLIC_KEYWORD)) return Visibilities.PUBLIC;
377            if (modifierList.hasModifier(PROTECTED_KEYWORD)) return Visibilities.PROTECTED;
378            if (modifierList.hasModifier(INTERNAL_KEYWORD)) return Visibilities.INTERNAL;
379            return defaultVisibility;
380        }
381    
382        public static boolean isInnerClass(@Nullable JetModifierList modifierList) {
383            return modifierList != null && modifierList.hasModifier(INNER_KEYWORD);
384        }
385    
386        @NotNull
387        public static Visibility getDefaultClassVisibility(@NotNull ClassDescriptor descriptor) {
388            if (isEnumEntry(descriptor)) {
389                return Visibilities.PUBLIC;
390            }
391            if (isCompanionObject(descriptor)) {
392                return ((ClassDescriptor) descriptor.getContainingDeclaration()).getVisibility();
393            }
394            return Visibilities.INTERNAL;
395        }
396    
397        private void runAnnotationCheckers(@NotNull JetDeclaration declaration, @NotNull DeclarationDescriptor descriptor) {
398            for (AnnotationChecker checker : additionalCheckerProvider.getAnnotationCheckers()) {
399                checker.check(declaration, descriptor, trace);
400            }
401        }
402    
403        public void checkVarianceModifiersOfTypeParameters(@NotNull JetModifierListOwner modifierListOwner) {
404            if (!(modifierListOwner instanceof JetTypeParameterListOwner)) return;
405            List<JetTypeParameter> typeParameters = ((JetTypeParameterListOwner) modifierListOwner).getTypeParameters();
406            for (JetTypeParameter typeParameter : typeParameters) {
407                JetModifierList modifierList = typeParameter.getModifierList();
408                checkIncompatibleVarianceModifiers(modifierList, trace);
409            }
410        }
411    }