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.Maps;
020    import com.google.common.collect.Sets;
021    import com.intellij.psi.PsiElement;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.kotlin.config.LanguageVersionSettings;
025    import org.jetbrains.kotlin.descriptors.*;
026    import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1;
027    import org.jetbrains.kotlin.extensions.DeclarationAttributeAltererExtension;
028    import org.jetbrains.kotlin.lexer.KtKeywordToken;
029    import org.jetbrains.kotlin.lexer.KtModifierKeywordToken;
030    import org.jetbrains.kotlin.lexer.KtTokens;
031    import org.jetbrains.kotlin.psi.*;
032    import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker;
033    import org.jetbrains.kotlin.resolve.checkers.PublishedApiUsageChecker;
034    import org.jetbrains.kotlin.resolve.checkers.UnderscoreChecker;
035    
036    import java.util.*;
037    
038    import static org.jetbrains.kotlin.diagnostics.Errors.NESTED_CLASS_NOT_ALLOWED;
039    import static org.jetbrains.kotlin.lexer.KtTokens.*;
040    import static org.jetbrains.kotlin.psi.KtStubbedPsiUtil.getContainingDeclaration;
041    
042    public class ModifiersChecker {
043        private static final Set<KtModifierKeywordToken> MODIFIERS_ILLEGAL_ON_PARAMETERS;
044    
045        static {
046            MODIFIERS_ILLEGAL_ON_PARAMETERS = Sets.newHashSet();
047            MODIFIERS_ILLEGAL_ON_PARAMETERS.addAll(Arrays.asList(KtTokens.MODIFIER_KEYWORDS_ARRAY));
048            MODIFIERS_ILLEGAL_ON_PARAMETERS.remove(KtTokens.VARARG_KEYWORD);
049        }
050    
051        public static boolean isIllegalInner(@NotNull DeclarationDescriptor descriptor) {
052            return checkIllegalInner(descriptor) != InnerModifierCheckResult.ALLOWED;
053        }
054    
055        private enum InnerModifierCheckResult {
056            ALLOWED,
057            ILLEGAL_POSITION,
058            IN_INTERFACE,
059            IN_OBJECT,
060        }
061    
062    
063        // NOTE: just checks if this is legal context for companion modifier (Companion object descriptor can be created)
064        // COMPANION_OBJECT_NOT_ALLOWED can be reported later
065        public static boolean isCompanionModifierAllowed(@NotNull KtDeclaration declaration) {
066            if (declaration instanceof KtObjectDeclaration) {
067                KtDeclaration containingDeclaration = getContainingDeclaration(declaration);
068                if (containingDeclaration instanceof KtClassOrObject) {
069                    return true;
070                }
071            }
072            return false;
073        }
074    
075        @NotNull
076        private static InnerModifierCheckResult checkIllegalInner(@NotNull DeclarationDescriptor descriptor) {
077            if (!(descriptor instanceof ClassDescriptor)) return InnerModifierCheckResult.ILLEGAL_POSITION;
078            ClassDescriptor classDescriptor = (ClassDescriptor) descriptor;
079    
080            if (classDescriptor.getKind() != ClassKind.CLASS) return InnerModifierCheckResult.ILLEGAL_POSITION;
081    
082            DeclarationDescriptor containingDeclaration = classDescriptor.getContainingDeclaration();
083            if (!(containingDeclaration instanceof ClassDescriptor)) return InnerModifierCheckResult.ILLEGAL_POSITION;
084    
085            if (DescriptorUtils.isInterface(containingDeclaration)) {
086                return InnerModifierCheckResult.IN_INTERFACE;
087            }
088            else if (DescriptorUtils.isObject(containingDeclaration)) {
089                return InnerModifierCheckResult.IN_OBJECT;
090            }
091            else {
092                return InnerModifierCheckResult.ALLOWED;
093            }
094        }
095    
096        private static boolean isIllegalNestedClass(@NotNull DeclarationDescriptor descriptor) {
097            if (!(descriptor instanceof ClassDescriptor)) return false;
098            DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
099            if (!(containingDeclaration instanceof ClassDescriptor)) return false;
100            ClassDescriptor containingClass = (ClassDescriptor) containingDeclaration;
101            return containingClass.isInner() || DescriptorUtils.isLocal(containingClass);
102        }
103    
104        @NotNull
105        public static Modality resolveMemberModalityFromModifiers(
106                @Nullable KtModifierListOwner modifierListOwner,
107                @NotNull Modality defaultModality,
108                @NotNull BindingContext bindingContext,
109                @Nullable DeclarationDescriptor containingDescriptor
110        ) {
111            return resolveModalityFromModifiers(modifierListOwner, defaultModality,
112                                                bindingContext, containingDescriptor, /* allowSealed = */ false);
113        }
114    
115        @NotNull
116        public static Modality resolveModalityFromModifiers(
117                @Nullable KtModifierListOwner modifierListOwner,
118                @NotNull Modality defaultModality,
119                @NotNull BindingContext bindingContext,
120                @Nullable DeclarationDescriptor containingDescriptor,
121                boolean allowSealed
122        ) {
123            KtModifierList modifierList = (modifierListOwner != null) ? modifierListOwner.getModifierList() : null;
124            Modality modality = resolveModalityFromModifiers(modifierList, defaultModality, allowSealed);
125    
126            if (modifierListOwner != null) {
127                Collection<DeclarationAttributeAltererExtension> extensions =
128                        DeclarationAttributeAltererExtension.Companion.getInstances(modifierListOwner.getProject());
129    
130                DeclarationDescriptor descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, modifierListOwner);
131                for (DeclarationAttributeAltererExtension extension : extensions) {
132                    Modality newModality = extension.refineDeclarationModality(
133                            modifierListOwner, descriptor, containingDescriptor, modality, bindingContext);
134    
135                    if (newModality != null) {
136                        modality = newModality;
137                        break;
138                    }
139                }
140            }
141    
142            return modality;
143        }
144    
145        @NotNull
146        private static Modality resolveModalityFromModifiers(
147                @Nullable KtModifierList modifierList,
148                @NotNull Modality defaultModality,
149                boolean allowSealed
150        ) {
151            if (modifierList == null) return defaultModality;
152            boolean hasAbstractModifier = modifierList.hasModifier(ABSTRACT_KEYWORD);
153            boolean hasOverrideModifier = modifierList.hasModifier(OVERRIDE_KEYWORD);
154    
155            if (allowSealed && modifierList.hasModifier(SEALED_KEYWORD)) {
156                return Modality.SEALED;
157            }
158            if (modifierList.hasModifier(OPEN_KEYWORD)) {
159                if (hasAbstractModifier || defaultModality == Modality.ABSTRACT) {
160                    return Modality.ABSTRACT;
161                }
162                return Modality.OPEN;
163            }
164            if (hasAbstractModifier) {
165                return Modality.ABSTRACT;
166            }
167            boolean hasFinalModifier = modifierList.hasModifier(FINAL_KEYWORD);
168            if (hasOverrideModifier && !hasFinalModifier && !(defaultModality == Modality.ABSTRACT)) {
169                return Modality.OPEN;
170            }
171            if (hasFinalModifier) {
172                return Modality.FINAL;
173            }
174            return defaultModality;
175        }
176    
177        @NotNull
178        public static Visibility resolveVisibilityFromModifiers(
179                @NotNull KtModifierListOwner modifierListOwner,
180                @NotNull Visibility defaultVisibility
181        ) {
182            return resolveVisibilityFromModifiers(modifierListOwner.getModifierList(), defaultVisibility);
183        }
184    
185        public static Visibility resolveVisibilityFromModifiers(@Nullable KtModifierList modifierList, @NotNull Visibility defaultVisibility) {
186            if (modifierList == null) return defaultVisibility;
187            if (modifierList.hasModifier(PRIVATE_KEYWORD)) return Visibilities.PRIVATE;
188            if (modifierList.hasModifier(PUBLIC_KEYWORD)) return Visibilities.PUBLIC;
189            if (modifierList.hasModifier(PROTECTED_KEYWORD)) return Visibilities.PROTECTED;
190            if (modifierList.hasModifier(INTERNAL_KEYWORD)) return Visibilities.INTERNAL;
191            return defaultVisibility;
192        }
193    
194        public static boolean isInnerClass(@Nullable KtModifierList modifierList) {
195            return modifierList != null && modifierList.hasModifier(INNER_KEYWORD);
196        }
197    
198        public class ModifiersCheckingProcedure {
199    
200            @NotNull
201            private final BindingTrace trace;
202            @NotNull
203            private final LanguageVersionSettings languageVersionSettings;
204    
205            private ModifiersCheckingProcedure(@NotNull BindingTrace trace, LanguageVersionSettings languageVersionSettings) {
206                this.trace = trace;
207                this.languageVersionSettings = languageVersionSettings;
208            }
209    
210            public void checkParameterHasNoValOrVar(
211                    @NotNull KtValVarKeywordOwner parameter,
212                    @NotNull DiagnosticFactory1<PsiElement, KtKeywordToken> diagnosticFactory
213            ) {
214                PsiElement valOrVar = parameter.getValOrVarKeyword();
215                if (valOrVar != null) {
216                    trace.report(diagnosticFactory.on(valOrVar, ((KtKeywordToken) valOrVar.getNode().getElementType())));
217                }
218            }
219    
220            public void checkModifiersForDeclaration(@NotNull KtDeclaration modifierListOwner, @NotNull MemberDescriptor descriptor) {
221                checkNestedClassAllowed(modifierListOwner, descriptor);
222                checkTypeParametersModifiers(modifierListOwner);
223                checkModifierListCommon(modifierListOwner, descriptor);
224            }
225    
226            private void checkModifierListCommon(@NotNull KtDeclaration modifierListOwner, @NotNull DeclarationDescriptor descriptor) {
227                AnnotationUseSiteTargetChecker.INSTANCE.check(modifierListOwner, descriptor, trace);
228                runDeclarationCheckers(modifierListOwner, descriptor);
229                annotationChecker.check(modifierListOwner, trace, descriptor);
230                ModifierCheckerCore.INSTANCE.check(modifierListOwner, trace, descriptor, languageVersionSettings);
231            }
232    
233            public void checkModifiersForLocalDeclaration(
234                    @NotNull KtDeclaration modifierListOwner,
235                    @NotNull DeclarationDescriptor descriptor
236            ) {
237                checkModifierListCommon(modifierListOwner, descriptor);
238            }
239    
240            public void checkModifiersForDestructuringDeclaration(@NotNull KtDestructuringDeclaration multiDeclaration) {
241                annotationChecker.check(multiDeclaration, trace, null);
242                ModifierCheckerCore.INSTANCE.check(multiDeclaration, trace, null, languageVersionSettings);
243                for (KtDestructuringDeclarationEntry multiEntry: multiDeclaration.getEntries()) {
244                    annotationChecker.check(multiEntry, trace, null);
245                    ModifierCheckerCore.INSTANCE.check(multiEntry, trace, null, languageVersionSettings);
246                    UnderscoreChecker.INSTANCE.checkNamed(multiEntry, trace, languageVersionSettings, /* allowSingleUnderscore = */ true);
247                }
248            }
249    
250            private void checkNestedClassAllowed(@NotNull KtModifierListOwner modifierListOwner, @NotNull DeclarationDescriptor descriptor) {
251                if (modifierListOwner.hasModifier(INNER_KEYWORD)) return;
252                if (modifierListOwner instanceof KtClass && !(modifierListOwner instanceof KtEnumEntry)) {
253                    KtClass aClass = (KtClass) modifierListOwner;
254                    boolean localEnumError = aClass.isLocal() && aClass.isEnum();
255                    if (!localEnumError && isIllegalNestedClass(descriptor)) {
256                        trace.report(NESTED_CLASS_NOT_ALLOWED.on(aClass));
257                    }
258                }
259            }
260    
261            @NotNull
262            public Map<KtModifierKeywordToken, PsiElement> getTokensCorrespondingToModifiers(
263                    @NotNull KtModifierList modifierList,
264                    @NotNull Collection<KtModifierKeywordToken> possibleModifiers
265            ) {
266                Map<KtModifierKeywordToken, PsiElement> tokens = Maps.newHashMap();
267                for (KtModifierKeywordToken modifier : possibleModifiers) {
268                    if (modifierList.hasModifier(modifier)) {
269                        tokens.put(modifier, modifierList.getModifier(modifier));
270                    }
271                }
272                return tokens;
273            }
274    
275    
276            public void runDeclarationCheckers(
277                    @NotNull KtDeclaration declaration,
278                    @NotNull DeclarationDescriptor descriptor
279            ) {
280                for (DeclarationChecker checker : declarationCheckers) {
281                    checker.check(declaration, descriptor, trace, trace.getBindingContext(), languageVersionSettings);
282                }
283                OperatorModifierChecker.INSTANCE.check(declaration, descriptor, trace, languageVersionSettings);
284                PublishedApiUsageChecker.INSTANCE.check(declaration, descriptor, trace);
285            }
286    
287            public void checkTypeParametersModifiers(@NotNull KtModifierListOwner modifierListOwner) {
288                if (!(modifierListOwner instanceof KtTypeParameterListOwner)) return;
289                List<KtTypeParameter> typeParameters = ((KtTypeParameterListOwner) modifierListOwner).getTypeParameters();
290                for (KtTypeParameter typeParameter : typeParameters) {
291                    ModifierCheckerCore.INSTANCE.check(typeParameter, trace, null, languageVersionSettings);
292                }
293            }
294        }
295    
296        @NotNull
297        private final AnnotationChecker annotationChecker;
298    
299        @NotNull
300        private final Iterable<DeclarationChecker> declarationCheckers;
301    
302        @NotNull
303        private final LanguageVersionSettings languageVersionSettings;
304    
305        public ModifiersChecker(
306                @NotNull AnnotationChecker annotationChecker,
307                @NotNull Iterable<DeclarationChecker> declarationCheckers,
308                @NotNull LanguageVersionSettings languageVersionSettings
309        ) {
310            this.annotationChecker = annotationChecker;
311            this.declarationCheckers = declarationCheckers;
312            this.languageVersionSettings = languageVersionSettings;
313        }
314    
315        @NotNull
316        public ModifiersCheckingProcedure withTrace(@NotNull BindingTrace trace) {
317            return new ModifiersCheckingProcedure(trace, languageVersionSettings);
318        }
319    }