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