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.psi.PsiElement;
023    import org.jetbrains.annotations.NotNull;
024    import org.jetbrains.annotations.Nullable;
025    import org.jetbrains.kotlin.descriptors.*;
026    import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1;
027    import org.jetbrains.kotlin.lexer.JetKeywordToken;
028    import org.jetbrains.kotlin.lexer.JetModifierKeywordToken;
029    import org.jetbrains.kotlin.lexer.JetTokens;
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.JetTokens.*;
036    import static org.jetbrains.kotlin.psi.JetStubbedPsiUtil.getContainingDeclaration;
037    
038    public class ModifiersChecker {
039        private static final Set<JetModifierKeywordToken> MODIFIERS_ILLEGAL_ON_PARAMETERS;
040    
041        static {
042            MODIFIERS_ILLEGAL_ON_PARAMETERS = Sets.newHashSet();
043            MODIFIERS_ILLEGAL_ON_PARAMETERS.addAll(Arrays.asList(JetTokens.MODIFIER_KEYWORDS_ARRAY));
044            MODIFIERS_ILLEGAL_ON_PARAMETERS.remove(JetTokens.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_TRAIT,
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 JetDeclaration declaration) {
062            if (declaration instanceof JetObjectDeclaration) {
063                JetDeclaration containingDeclaration = getContainingDeclaration(declaration);
064                if (containingDeclaration instanceof JetClassOrObject) {
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.isTrait(containingDeclaration)) {
082                return InnerModifierCheckResult.IN_TRAIT;
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() || containingClass.getContainingDeclaration() instanceof FunctionDescriptor;
098        }
099    
100        @NotNull
101        public static Modality resolveModalityFromModifiers(
102                @NotNull JetModifierListOwner modifierListOwner,
103                @NotNull Modality defaultModality
104        ) {
105            return resolveModalityFromModifiers(modifierListOwner.getModifierList(), defaultModality);
106        }
107    
108        @NotNull
109        public static Modality resolveModalityFromModifiers(@Nullable JetModifierList modifierList, @NotNull Modality defaultModality) {
110            if (modifierList == null) return defaultModality;
111            boolean hasAbstractModifier = modifierList.hasModifier(ABSTRACT_KEYWORD);
112            boolean hasOverrideModifier = modifierList.hasModifier(OVERRIDE_KEYWORD);
113    
114            if (modifierList.hasModifier(SEALED_KEYWORD)) {
115                return Modality.SEALED;
116            }
117            if (modifierList.hasModifier(OPEN_KEYWORD)) {
118                if (hasAbstractModifier || defaultModality == Modality.ABSTRACT) {
119                    return Modality.ABSTRACT;
120                }
121                return Modality.OPEN;
122            }
123            if (hasAbstractModifier) {
124                return Modality.ABSTRACT;
125            }
126            boolean hasFinalModifier = modifierList.hasModifier(FINAL_KEYWORD);
127            if (hasOverrideModifier && !hasFinalModifier && !(defaultModality == Modality.ABSTRACT)) {
128                return Modality.OPEN;
129            }
130            if (hasFinalModifier) {
131                return Modality.FINAL;
132            }
133            return defaultModality;
134        }
135    
136        @NotNull
137        public static Visibility resolveVisibilityFromModifiers(
138                @NotNull JetModifierListOwner modifierListOwner,
139                @NotNull Visibility defaultVisibility
140        ) {
141            return resolveVisibilityFromModifiers(modifierListOwner.getModifierList(), defaultVisibility);
142        }
143    
144        public static Visibility resolveVisibilityFromModifiers(@Nullable JetModifierList modifierList, @NotNull Visibility defaultVisibility) {
145            if (modifierList == null) return defaultVisibility;
146            if (modifierList.hasModifier(PRIVATE_KEYWORD)) return Visibilities.PRIVATE;
147            if (modifierList.hasModifier(PUBLIC_KEYWORD)) return Visibilities.PUBLIC;
148            if (modifierList.hasModifier(PROTECTED_KEYWORD)) return Visibilities.PROTECTED;
149            if (modifierList.hasModifier(INTERNAL_KEYWORD)) return Visibilities.INTERNAL;
150            return defaultVisibility;
151        }
152    
153        public static boolean isInnerClass(@Nullable JetModifierList modifierList) {
154            return modifierList != null && modifierList.hasModifier(INNER_KEYWORD);
155        }
156    
157        public class ModifiersCheckingProcedure {
158    
159            @NotNull
160            private final BindingTrace trace;
161    
162            private ModifiersCheckingProcedure(@NotNull BindingTrace trace) {
163                this.trace = trace;
164            }
165    
166            public void checkParameterHasNoValOrVar(
167                    @NotNull JetParameter parameter,
168                    @NotNull DiagnosticFactory1<PsiElement, JetKeywordToken> diagnosticFactory
169            ) {
170                PsiElement valOrVar = parameter.getValOrVarKeyword();
171                if (valOrVar != null) {
172                    trace.report(diagnosticFactory.on(valOrVar, ((JetKeywordToken) valOrVar.getNode().getElementType())));
173                }
174            }
175    
176            public void checkModifiersForDeclaration(@NotNull JetDeclaration modifierListOwner, @NotNull MemberDescriptor descriptor) {
177                checkNestedClassAllowed(modifierListOwner, descriptor);
178                checkTypeParametersModifiers(modifierListOwner);
179                checkModifierListCommon(modifierListOwner, descriptor);
180            }
181    
182            private void checkModifierListCommon(@NotNull JetDeclaration modifierListOwner, @NotNull DeclarationDescriptor descriptor) {
183                AnnotationUseSiteTargetChecker.INSTANCE$.check(modifierListOwner, descriptor, trace);
184                runDeclarationCheckers(modifierListOwner, descriptor);
185                ClassDescriptor classDescriptor = descriptor instanceof ClassDescriptor ? (ClassDescriptor) descriptor : null;
186                annotationChecker.check(modifierListOwner, trace, classDescriptor);
187                ModifierCheckerCore.INSTANCE$.check(modifierListOwner, trace, descriptor);
188            }
189    
190            public void checkModifiersForLocalDeclaration(
191                    @NotNull JetDeclaration modifierListOwner,
192                    @NotNull DeclarationDescriptor descriptor
193            ) {
194                checkModifierListCommon(modifierListOwner, descriptor);
195            }
196    
197            public void checkModifiersForMultiDeclaration(@NotNull JetMultiDeclaration multiDeclaration) {
198                annotationChecker.check(multiDeclaration, trace, null);
199                ModifierCheckerCore.INSTANCE$.check(multiDeclaration, trace, null);
200                for (JetMultiDeclarationEntry multiEntry: multiDeclaration.getEntries()) {
201                    annotationChecker.check(multiEntry, trace, null);
202                    ModifierCheckerCore.INSTANCE$.check(multiEntry, trace, null);
203                    UnderscoreChecker.INSTANCE$.checkNamed(multiEntry, trace);
204                }
205            }
206    
207            private void checkNestedClassAllowed(@NotNull JetModifierListOwner modifierListOwner, @NotNull DeclarationDescriptor descriptor) {
208                if (modifierListOwner.hasModifier(INNER_KEYWORD)) return;
209                if (modifierListOwner instanceof JetClass && !(modifierListOwner instanceof JetEnumEntry)) {
210                    JetClass aClass = (JetClass) modifierListOwner;
211                    boolean localEnumError = aClass.isLocal() && aClass.isEnum();
212                    if (!localEnumError && isIllegalNestedClass(descriptor)) {
213                        trace.report(NESTED_CLASS_NOT_ALLOWED.on(aClass));
214                    }
215                }
216            }
217    
218            @NotNull
219            public Map<JetModifierKeywordToken, PsiElement> getTokensCorrespondingToModifiers(
220                    @NotNull JetModifierList modifierList,
221                    @NotNull Collection<JetModifierKeywordToken> possibleModifiers
222            ) {
223                Map<JetModifierKeywordToken, PsiElement> tokens = Maps.newHashMap();
224                for (JetModifierKeywordToken modifier : possibleModifiers) {
225                    if (modifierList.hasModifier(modifier)) {
226                        tokens.put(modifier, modifierList.getModifier(modifier));
227                    }
228                }
229                return tokens;
230            }
231    
232    
233            private void runDeclarationCheckers(@NotNull JetDeclaration declaration, @NotNull DeclarationDescriptor descriptor) {
234                for (DeclarationChecker checker : declarationCheckers) {
235                    checker.check(declaration, descriptor, trace, trace.getBindingContext());
236                }
237            }
238    
239            public void checkTypeParametersModifiers(@NotNull JetModifierListOwner modifierListOwner) {
240                if (!(modifierListOwner instanceof JetTypeParameterListOwner)) return;
241                List<JetTypeParameter> typeParameters = ((JetTypeParameterListOwner) modifierListOwner).getTypeParameters();
242                for (JetTypeParameter typeParameter : typeParameters) {
243                    ModifierCheckerCore.INSTANCE$.check(typeParameter, trace, null);
244                }
245            }
246        }
247    
248        @NotNull
249        private final AnnotationChecker annotationChecker;
250    
251        @NotNull
252        private final Iterable<DeclarationChecker> declarationCheckers;
253    
254        public ModifiersChecker(@NotNull AnnotationChecker annotationChecker, @NotNull Iterable<DeclarationChecker> declarationCheckers) {
255            this.annotationChecker = annotationChecker;
256            this.declarationCheckers = declarationCheckers;
257        }
258    
259        @NotNull
260        public ModifiersCheckingProcedure withTrace(@NotNull BindingTrace trace) {
261            return new ModifiersCheckingProcedure(trace);
262        }
263    }