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