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                ModifierCheckerCore.INSTANCE$.check(modifierListOwner, trace, descriptor);
179                checkTypeParametersModifiers(modifierListOwner);
180                AnnotationUseSiteTargetChecker.INSTANCE$.check(modifierListOwner, descriptor, trace);
181                runDeclarationCheckers(modifierListOwner, descriptor);
182                ClassDescriptor classDescriptor = descriptor instanceof ClassDescriptor ? (ClassDescriptor) descriptor : null;
183                annotationChecker.check(modifierListOwner, trace, classDescriptor);
184            }
185    
186            public void checkModifiersForLocalDeclaration(
187                    @NotNull JetDeclaration modifierListOwner,
188                    @NotNull DeclarationDescriptor descriptor
189            ) {
190                AnnotationUseSiteTargetChecker.INSTANCE$.check(modifierListOwner, descriptor, trace);
191                runDeclarationCheckers(modifierListOwner, descriptor);
192                annotationChecker.check(modifierListOwner, trace,
193                                                  descriptor instanceof ClassDescriptor ? (ClassDescriptor) descriptor : null);
194                ModifierCheckerCore.INSTANCE$.check(modifierListOwner, trace, descriptor);
195            }
196    
197            private void checkNestedClassAllowed(@NotNull JetModifierListOwner modifierListOwner, @NotNull DeclarationDescriptor descriptor) {
198                if (modifierListOwner.hasModifier(INNER_KEYWORD)) return;
199                if (modifierListOwner instanceof JetClass && !(modifierListOwner instanceof JetEnumEntry)) {
200                    JetClass aClass = (JetClass) modifierListOwner;
201                    boolean localEnumError = aClass.isLocal() && aClass.isEnum();
202                    if (!localEnumError && isIllegalNestedClass(descriptor)) {
203                        trace.report(NESTED_CLASS_NOT_ALLOWED.on(aClass));
204                    }
205                }
206            }
207    
208            @NotNull
209            public Map<JetModifierKeywordToken, PsiElement> getTokensCorrespondingToModifiers(
210                    @NotNull JetModifierList modifierList,
211                    @NotNull Collection<JetModifierKeywordToken> possibleModifiers
212            ) {
213                Map<JetModifierKeywordToken, PsiElement> tokens = Maps.newHashMap();
214                for (JetModifierKeywordToken modifier : possibleModifiers) {
215                    if (modifierList.hasModifier(modifier)) {
216                        tokens.put(modifier, modifierList.getModifier(modifier));
217                    }
218                }
219                return tokens;
220            }
221    
222    
223            private void runDeclarationCheckers(@NotNull JetDeclaration declaration, @NotNull DeclarationDescriptor descriptor) {
224                for (DeclarationChecker checker : declarationCheckers) {
225                    checker.check(declaration, descriptor, trace, trace.getBindingContext());
226                }
227            }
228    
229            public void checkTypeParametersModifiers(@NotNull JetModifierListOwner modifierListOwner) {
230                if (!(modifierListOwner instanceof JetTypeParameterListOwner)) return;
231                List<JetTypeParameter> typeParameters = ((JetTypeParameterListOwner) modifierListOwner).getTypeParameters();
232                for (JetTypeParameter typeParameter : typeParameters) {
233                    ModifierCheckerCore.INSTANCE$.check(typeParameter, trace, null);
234                }
235            }
236        }
237    
238        @NotNull
239        private final AnnotationChecker annotationChecker;
240    
241        @NotNull
242        private final Iterable<DeclarationChecker> declarationCheckers;
243    
244        public ModifiersChecker(@NotNull AnnotationChecker annotationChecker, @NotNull Iterable<DeclarationChecker> declarationCheckers) {
245            this.annotationChecker = annotationChecker;
246            this.declarationCheckers = declarationCheckers;
247        }
248    
249        @NotNull
250        public ModifiersCheckingProcedure withTrace(@NotNull BindingTrace trace) {
251            return new ModifiersCheckingProcedure(trace);
252        }
253    }