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.Errors;
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    
043    public class ModifiersChecker {
044        private static final Collection<JetModifierKeywordToken> MODALITY_MODIFIERS =
045                Lists.newArrayList(ABSTRACT_KEYWORD, OPEN_KEYWORD, FINAL_KEYWORD, OVERRIDE_KEYWORD);
046    
047        private static final Collection<JetModifierKeywordToken> VISIBILITY_MODIFIERS =
048                Lists.newArrayList(PRIVATE_KEYWORD, PROTECTED_KEYWORD, PUBLIC_KEYWORD, INTERNAL_KEYWORD);
049    
050        public static void reportIllegalModifiers(
051                @Nullable JetModifierList modifierList,
052                @NotNull Collection<JetModifierKeywordToken> illegalModifiers,
053                @NotNull BindingTrace trace
054        ) {
055            if (modifierList == null) return;
056    
057            for (JetModifierKeywordToken modifierToken : illegalModifiers) {
058                if (modifierList.hasModifier(modifierToken)) {
059                    PsiElement modifierPsi = modifierList.getModifier(modifierToken);
060                    assert modifierPsi != null;
061                    trace.report(ILLEGAL_MODIFIER.on(modifierPsi, modifierToken));
062                }
063            }
064        }
065    
066        public static void checkIncompatibleModifiers(
067                @Nullable JetModifierList modifierList,
068                @NotNull BindingTrace trace,
069                @NotNull Collection<JetModifierKeywordToken> availableModifiers,
070                @NotNull Collection<JetModifierKeywordToken>... availableCombinations
071        ) {
072            if (modifierList == null) return;
073            Collection<JetModifierKeywordToken> presentModifiers = Sets.newLinkedHashSet();
074            for (JetModifierKeywordToken modifier : availableModifiers) {
075                if (modifierList.hasModifier(modifier)) {
076                    presentModifiers.add(modifier);
077                }
078            }
079            checkRepeatedModifiers(modifierList, trace, availableModifiers);
080    
081            if (presentModifiers.size() == 1) {
082                return;
083            }
084            for (Collection<JetModifierKeywordToken> combination : availableCombinations) {
085                if (presentModifiers.containsAll(combination) && combination.containsAll(presentModifiers)) {
086                    return;
087                }
088            }
089            for (JetModifierKeywordToken token : presentModifiers) {
090                trace.report(Errors.INCOMPATIBLE_MODIFIERS.on(modifierList.getModifierNode(token).getPsi(), presentModifiers));
091            }
092        }
093    
094        private static void checkRepeatedModifiers(
095                @NotNull JetModifierList modifierList,
096                @NotNull BindingTrace trace,
097                @NotNull Collection<JetModifierKeywordToken> availableModifiers
098        ) {
099            for (JetModifierKeywordToken token : availableModifiers) {
100                if (!modifierList.hasModifier(token)) continue;
101    
102                List<ASTNode> nodesOfRepeatedTokens = Lists.newArrayList();
103                ASTNode node = modifierList.getNode().getFirstChildNode();
104                while (node != null) {
105                    if (node.getElementType() == token) {
106                        nodesOfRepeatedTokens.add(node);
107                    }
108                    node = node.getTreeNext();
109                }
110                if (nodesOfRepeatedTokens.size() > 1) {
111                    for (ASTNode repeatedToken : nodesOfRepeatedTokens) {
112                        trace.report(REPEATED_MODIFIER.on(repeatedToken.getPsi(), token));
113                    }
114                }
115            }
116        }
117    
118        public static void checkIncompatibleVarianceModifiers(@Nullable JetModifierList modifierList, @NotNull BindingTrace trace) {
119            checkIncompatibleModifiers(modifierList, trace, Arrays.asList(JetTokens.IN_KEYWORD, JetTokens.OUT_KEYWORD));
120        }
121    
122        @NotNull
123        private final BindingTrace trace;
124        @NotNull
125        private final AdditionalCheckerProvider additionalCheckerProvider;
126    
127        public ModifiersChecker(@NotNull BindingTrace trace, @NotNull AdditionalCheckerProvider additionalCheckerProvider) {
128            this.trace = trace;
129            this.additionalCheckerProvider = additionalCheckerProvider;
130        }
131    
132        public static ModifiersChecker create(@NotNull BindingTrace trace, @NotNull AdditionalCheckerProvider provider) {
133            return new ModifiersChecker(trace, provider);
134        }
135    
136        public void checkModifiersForDeclaration(@NotNull JetDeclaration modifierListOwner, @NotNull MemberDescriptor descriptor) {
137            if (modifierListOwner instanceof JetEnumEntry) {
138                checkIllegalInThisContextModifiers(modifierListOwner, Arrays.asList(MODIFIER_KEYWORDS_ARRAY));
139            }
140            else {
141                checkInnerModifier(modifierListOwner, descriptor);
142                checkModalityModifiers(modifierListOwner);
143                checkVisibilityModifiers(modifierListOwner, descriptor);
144                checkVarianceModifiersOfTypeParameters(modifierListOwner);
145            }
146            checkPlatformNameApplicability(descriptor);
147            runAnnotationCheckers(modifierListOwner, descriptor);
148        }
149    
150        public void checkModifiersForLocalDeclaration(@NotNull JetDeclaration modifierListOwner, @NotNull DeclarationDescriptor descriptor) {
151            checkIllegalModalityModifiers(modifierListOwner);
152            checkIllegalVisibilityModifiers(modifierListOwner);
153            checkPlatformNameApplicability(descriptor);
154            runAnnotationCheckers(modifierListOwner, descriptor);
155        }
156    
157        public void checkIllegalModalityModifiers(@NotNull JetModifierListOwner modifierListOwner) {
158            checkIllegalInThisContextModifiers(modifierListOwner, MODALITY_MODIFIERS);
159        }
160    
161        public void checkIllegalVisibilityModifiers(@NotNull JetModifierListOwner modifierListOwner) {
162            checkIllegalInThisContextModifiers(modifierListOwner, VISIBILITY_MODIFIERS);
163        }
164    
165        private void checkModalityModifiers(@NotNull JetModifierListOwner modifierListOwner) {
166            JetModifierList modifierList = modifierListOwner.getModifierList();
167            if (modifierList == null) return;
168    
169            checkRedundantModifier(modifierList, Pair.create(OPEN_KEYWORD, ABSTRACT_KEYWORD), Pair.create(OPEN_KEYWORD, OVERRIDE_KEYWORD));
170    
171            checkCompatibility(modifierList, Arrays.asList(ABSTRACT_KEYWORD, OPEN_KEYWORD, FINAL_KEYWORD),
172                               Arrays.asList(ABSTRACT_KEYWORD, OPEN_KEYWORD));
173    
174            if (modifierListOwner.getParent() instanceof JetClassObject || modifierListOwner instanceof JetObjectDeclaration) {
175                checkIllegalModalityModifiers(modifierListOwner);
176            }
177            else if (modifierListOwner instanceof JetClassOrObject) {
178                checkIllegalInThisContextModifiers(modifierListOwner, Collections.singletonList(OVERRIDE_KEYWORD));
179            }
180        }
181    
182        private void checkVisibilityModifiers(@NotNull JetModifierListOwner modifierListOwner, @NotNull DeclarationDescriptor descriptor) {
183            JetModifierList modifierList = modifierListOwner.getModifierList();
184            if (modifierList == null) return;
185    
186            DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
187            if (containingDeclaration instanceof PackageFragmentDescriptor) {
188                if (modifierList.hasModifier(PROTECTED_KEYWORD)) {
189                    trace.report(Errors.PACKAGE_MEMBER_CANNOT_BE_PROTECTED.on(modifierListOwner));
190                }
191            }
192    
193            checkCompatibility(modifierList, VISIBILITY_MODIFIERS);
194        }
195    
196        private void checkInnerModifier(@NotNull JetModifierListOwner modifierListOwner, @NotNull DeclarationDescriptor descriptor) {
197            if (modifierListOwner.hasModifier(INNER_KEYWORD)) {
198                if (isIllegalInner(descriptor)) {
199                    checkIllegalInThisContextModifiers(modifierListOwner, Collections.singletonList(INNER_KEYWORD));
200                }
201                return;
202            }
203            if (modifierListOwner instanceof JetClass && !(modifierListOwner instanceof JetEnumEntry)) {
204                JetClass aClass = (JetClass) modifierListOwner;
205                boolean localEnumError = aClass.isLocal() && aClass.isEnum();
206                if (!localEnumError && isIllegalNestedClass(descriptor)) {
207                    trace.report(NESTED_CLASS_NOT_ALLOWED.on(aClass));
208                }
209            }
210        }
211    
212        public static boolean isIllegalInner(@NotNull DeclarationDescriptor descriptor) {
213            if (!(descriptor instanceof ClassDescriptor)) return true;
214            ClassDescriptor classDescriptor = (ClassDescriptor) descriptor;
215            if (classDescriptor.getKind() != ClassKind.CLASS) return true;
216            DeclarationDescriptor containingDeclaration = classDescriptor.getContainingDeclaration();
217            if (!(containingDeclaration instanceof ClassDescriptor)) return true;
218            return ((ClassDescriptor) containingDeclaration).getKind() == ClassKind.TRAIT;
219        }
220    
221        private static boolean isIllegalNestedClass(@NotNull DeclarationDescriptor descriptor) {
222            if (!(descriptor instanceof ClassDescriptor)) return false;
223            DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
224            if (!(containingDeclaration instanceof ClassDescriptor)) return false;
225            ClassDescriptor containingClass = (ClassDescriptor) containingDeclaration;
226            return containingClass.isInner() || containingClass.getContainingDeclaration() instanceof FunctionDescriptor;
227        }
228    
229        private void checkPlatformNameApplicability(@NotNull DeclarationDescriptor descriptor) {
230            AnnotationDescriptor annotation = descriptor.getAnnotations().findAnnotation(new FqName("kotlin.platform.platformName"));
231            if (annotation == null) return;
232    
233            JetAnnotationEntry annotationEntry = trace.get(BindingContext.ANNOTATION_DESCRIPTOR_TO_PSI_ELEMENT, annotation);
234            if (annotationEntry == null) return;
235    
236            if (!DescriptorUtils.isTopLevelDeclaration(descriptor) || !(descriptor instanceof FunctionDescriptor)) {
237                trace.report(INAPPLICABLE_ANNOTATION.on(annotationEntry));
238            }
239    
240            Collection<CompileTimeConstant<?>> values = annotation.getAllValueArguments().values();
241            if (!values.isEmpty()) {
242                CompileTimeConstant<?> name = values.iterator().next();
243                if (name instanceof StringValue) {
244                    String value = ((StringValue) name).getValue();
245                    if (value == null || !Name.isValidIdentifier(value)) {
246                        trace.report(ILLEGAL_PLATFORM_NAME.on(annotationEntry, String.valueOf(value)));
247                    }
248                }
249            }
250        }
251    
252        private void checkCompatibility(@Nullable JetModifierList modifierList, Collection<JetModifierKeywordToken> availableModifiers, Collection<JetModifierKeywordToken>... availableCombinations) {
253            checkIncompatibleModifiers(modifierList, trace, availableModifiers, availableCombinations);
254        }
255    
256        private void checkRedundantModifier(@NotNull JetModifierList modifierList, Pair<JetModifierKeywordToken, JetModifierKeywordToken>... redundantBundles) {
257            for (Pair<JetModifierKeywordToken, JetModifierKeywordToken> tokenPair : redundantBundles) {
258                JetModifierKeywordToken redundantModifier = tokenPair.getFirst();
259                JetModifierKeywordToken sufficientModifier = tokenPair.getSecond();
260                if (modifierList.hasModifier(redundantModifier) && modifierList.hasModifier(sufficientModifier)) {
261                    trace.report(Errors.REDUNDANT_MODIFIER.on(modifierList.getModifierNode(redundantModifier).getPsi(), redundantModifier, sufficientModifier));
262                }
263            }
264        }
265    
266        public void checkIllegalInThisContextModifiers(
267                @NotNull JetModifierListOwner modifierListOwner,
268                @NotNull Collection<JetModifierKeywordToken> illegalModifiers
269        ) {
270            reportIllegalModifiers(modifierListOwner.getModifierList(), illegalModifiers, trace);
271        }
272    
273        @NotNull
274        public static Map<JetModifierKeywordToken, ASTNode> getNodesCorrespondingToModifiers(@NotNull JetModifierList modifierList, @NotNull Collection<JetModifierKeywordToken> possibleModifiers) {
275            Map<JetModifierKeywordToken, ASTNode> nodes = Maps.newHashMap();
276            for (JetModifierKeywordToken modifier : possibleModifiers) {
277                if (modifierList.hasModifier(modifier)) {
278                    nodes.put(modifier, modifierList.getModifierNode(modifier));
279                }
280            }
281            return nodes;
282        }
283    
284        @NotNull
285        public static Modality resolveModalityFromModifiers(@NotNull JetModifierListOwner modifierListOwner, @NotNull Modality defaultModality) {
286            return resolveModalityFromModifiers(modifierListOwner.getModifierList(), defaultModality);
287        }
288    
289        public static Modality resolveModalityFromModifiers(@Nullable JetModifierList modifierList, @NotNull Modality defaultModality) {
290            if (modifierList == null) return defaultModality;
291            boolean hasAbstractModifier = modifierList.hasModifier(ABSTRACT_KEYWORD);
292            boolean hasOverrideModifier = modifierList.hasModifier(OVERRIDE_KEYWORD);
293    
294            if (modifierList.hasModifier(OPEN_KEYWORD)) {
295                if (hasAbstractModifier || defaultModality == Modality.ABSTRACT) {
296                    return Modality.ABSTRACT;
297                }
298                return Modality.OPEN;
299            }
300            if (hasAbstractModifier) {
301                return Modality.ABSTRACT;
302            }
303            boolean hasFinalModifier = modifierList.hasModifier(FINAL_KEYWORD);
304            if (hasOverrideModifier && !hasFinalModifier && !(defaultModality == Modality.ABSTRACT)) {
305                return Modality.OPEN;
306            }
307            if (hasFinalModifier) {
308                return Modality.FINAL;
309            }
310            return defaultModality;
311        }
312    
313        @NotNull
314        public static Visibility resolveVisibilityFromModifiers(@NotNull JetModifierListOwner modifierListOwner, @NotNull Visibility defaultVisibility) {
315            return resolveVisibilityFromModifiers(modifierListOwner.getModifierList(), defaultVisibility);
316        }
317    
318        public static Visibility resolveVisibilityFromModifiers(@Nullable JetModifierList modifierList, @NotNull Visibility defaultVisibility) {
319            if (modifierList == null) return defaultVisibility;
320            if (modifierList.hasModifier(PRIVATE_KEYWORD)) return Visibilities.PRIVATE;
321            if (modifierList.hasModifier(PUBLIC_KEYWORD)) return Visibilities.PUBLIC;
322            if (modifierList.hasModifier(PROTECTED_KEYWORD)) return Visibilities.PROTECTED;
323            if (modifierList.hasModifier(INTERNAL_KEYWORD)) return Visibilities.INTERNAL;
324            return defaultVisibility;
325        }
326    
327        public static boolean isInnerClass(@Nullable JetModifierList modifierList) {
328            return modifierList != null && modifierList.hasModifier(INNER_KEYWORD);
329        }
330    
331        @NotNull
332        public static Visibility getDefaultClassVisibility(@NotNull ClassDescriptor descriptor) {
333            ClassKind kind = descriptor.getKind();
334            if (kind == ClassKind.ENUM_ENTRY) {
335                return Visibilities.PUBLIC;
336            }
337            if (kind == ClassKind.CLASS_OBJECT) {
338                return ((ClassDescriptor) descriptor.getContainingDeclaration()).getVisibility();
339            }
340            return Visibilities.INTERNAL;
341        }
342    
343        private void runAnnotationCheckers(@NotNull JetDeclaration declaration, @NotNull DeclarationDescriptor descriptor) {
344            for (AnnotationChecker checker : additionalCheckerProvider.getAnnotationCheckers()) {
345                checker.check(declaration, descriptor, trace);
346            }
347        }
348    
349        public void checkVarianceModifiersOfTypeParameters(@NotNull JetModifierListOwner modifierListOwner) {
350            if (!(modifierListOwner instanceof JetTypeParameterListOwner)) return;
351            List<JetTypeParameter> typeParameters = ((JetTypeParameterListOwner) modifierListOwner).getTypeParameters();
352            for (JetTypeParameter typeParameter : typeParameters) {
353                JetModifierList modifierList = typeParameter.getModifierList();
354                checkIncompatibleVarianceModifiers(modifierList, trace);
355            }
356        }
357    }