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.lang.ASTNode;
023    import com.intellij.openapi.util.Pair;
024    import com.intellij.psi.PsiElement;
025    import org.jetbrains.annotations.NotNull;
026    import org.jetbrains.annotations.Nullable;
027    import org.jetbrains.kotlin.descriptors.*;
028    import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
029    import org.jetbrains.kotlin.diagnostics.*;
030    import org.jetbrains.kotlin.lexer.JetModifierKeywordToken;
031    import org.jetbrains.kotlin.lexer.JetTokens;
032    import org.jetbrains.kotlin.name.FqName;
033    import org.jetbrains.kotlin.name.Name;
034    import org.jetbrains.kotlin.psi.*;
035    import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
036    import org.jetbrains.kotlin.resolve.constants.StringValue;
037    
038    import java.util.*;
039    
040    import static org.jetbrains.kotlin.diagnostics.Errors.*;
041    import static org.jetbrains.kotlin.lexer.JetTokens.*;
042    import static org.jetbrains.kotlin.psi.JetStubbedPsiUtil.getContainingDeclaration;
043    import static org.jetbrains.kotlin.resolve.DescriptorUtils.isCompanionObject;
044    import static org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumEntry;
045    
046    public class ModifiersChecker {
047        private static final Collection<JetModifierKeywordToken> MODALITY_MODIFIERS =
048                Lists.newArrayList(ABSTRACT_KEYWORD, OPEN_KEYWORD, FINAL_KEYWORD, OVERRIDE_KEYWORD);
049    
050        private static final Collection<JetModifierKeywordToken> VISIBILITY_MODIFIERS =
051                Lists.newArrayList(PRIVATE_KEYWORD, PROTECTED_KEYWORD, PUBLIC_KEYWORD, INTERNAL_KEYWORD);
052    
053        public static void reportIllegalModifiers(
054                @Nullable JetModifierList modifierList,
055                @NotNull Collection<JetModifierKeywordToken> illegalModifiers,
056                @NotNull BindingTrace trace
057        ) {
058            if (modifierList == null) return;
059    
060            for (JetModifierKeywordToken modifierToken : illegalModifiers) {
061                if (modifierList.hasModifier(modifierToken)) {
062                    PsiElement modifierPsi = modifierList.getModifier(modifierToken);
063                    assert modifierPsi != null;
064                    trace.report(ILLEGAL_MODIFIER.on(modifierPsi, modifierToken));
065                }
066            }
067        }
068    
069        public static void checkIncompatibleModifiers(
070                @Nullable JetModifierList modifierList,
071                @NotNull BindingTrace trace,
072                @NotNull Collection<JetModifierKeywordToken> availableModifiers,
073                @NotNull Collection<JetModifierKeywordToken>... availableCombinations
074        ) {
075            if (modifierList == null) return;
076            Collection<JetModifierKeywordToken> presentModifiers = Sets.newLinkedHashSet();
077            for (JetModifierKeywordToken modifier : availableModifiers) {
078                if (modifierList.hasModifier(modifier)) {
079                    presentModifiers.add(modifier);
080                }
081            }
082            checkRepeatedModifiers(modifierList, trace, availableModifiers);
083    
084            if (presentModifiers.size() == 1) {
085                return;
086            }
087            for (Collection<JetModifierKeywordToken> combination : availableCombinations) {
088                if (presentModifiers.containsAll(combination) && combination.containsAll(presentModifiers)) {
089                    return;
090                }
091            }
092            for (JetModifierKeywordToken token : presentModifiers) {
093                trace.report(Errors.INCOMPATIBLE_MODIFIERS.on(modifierList.getModifierNode(token).getPsi(), presentModifiers));
094            }
095        }
096    
097        private static void checkRepeatedModifiers(
098                @NotNull JetModifierList modifierList,
099                @NotNull BindingTrace trace,
100                @NotNull Collection<JetModifierKeywordToken> availableModifiers
101        ) {
102            for (JetModifierKeywordToken token : availableModifiers) {
103                if (!modifierList.hasModifier(token)) continue;
104    
105                List<ASTNode> nodesOfRepeatedTokens = Lists.newArrayList();
106                ASTNode node = modifierList.getNode().getFirstChildNode();
107                while (node != null) {
108                    if (node.getElementType() == token) {
109                        nodesOfRepeatedTokens.add(node);
110                    }
111                    node = node.getTreeNext();
112                }
113                if (nodesOfRepeatedTokens.size() > 1) {
114                    for (ASTNode repeatedToken : nodesOfRepeatedTokens) {
115                        trace.report(REPEATED_MODIFIER.on(repeatedToken.getPsi(), token));
116                    }
117                }
118            }
119        }
120    
121        public static void checkIncompatibleVarianceModifiers(@Nullable JetModifierList modifierList, @NotNull BindingTrace trace) {
122            checkIncompatibleModifiers(modifierList, trace, Arrays.asList(JetTokens.IN_KEYWORD, JetTokens.OUT_KEYWORD));
123        }
124    
125        @NotNull
126        private final BindingTrace trace;
127        @NotNull
128        private final AdditionalCheckerProvider additionalCheckerProvider;
129    
130        public ModifiersChecker(@NotNull BindingTrace trace, @NotNull AdditionalCheckerProvider additionalCheckerProvider) {
131            this.trace = trace;
132            this.additionalCheckerProvider = additionalCheckerProvider;
133        }
134    
135        public static ModifiersChecker create(@NotNull BindingTrace trace, @NotNull AdditionalCheckerProvider provider) {
136            return new ModifiersChecker(trace, provider);
137        }
138    
139        public void checkModifiersForDeclaration(@NotNull JetDeclaration modifierListOwner, @NotNull MemberDescriptor descriptor) {
140            if (modifierListOwner instanceof JetEnumEntry) {
141                reportIllegalModifiers(modifierListOwner, Arrays.asList(MODIFIER_KEYWORDS_ARRAY));
142            }
143            else {
144                checkInnerModifier(modifierListOwner, descriptor);
145                checkDefaultModifier(modifierListOwner);
146                checkModalityModifiers(modifierListOwner);
147                checkVisibilityModifiers(modifierListOwner, descriptor);
148                checkVarianceModifiers(modifierListOwner);
149                checkVarargsModifiers(modifierListOwner, descriptor);
150            }
151            checkPlatformNameApplicability(descriptor);
152            runDeclarationCheckers(modifierListOwner, descriptor);
153        }
154    
155        private void checkVarargsModifiers(@NotNull JetDeclaration owner, @NotNull MemberDescriptor descriptor) {
156            if (!(owner instanceof JetParameter)) {
157                reportIllegalModifiers(owner, Collections.singleton(VARARG_KEYWORD));
158            }
159        }
160    
161        public void checkModifiersForLocalDeclaration(@NotNull JetDeclaration modifierListOwner, @NotNull DeclarationDescriptor descriptor) {
162            reportIllegalModalityModifiers(modifierListOwner);
163            reportIllegalVisibilityModifiers(modifierListOwner);
164            checkPlatformNameApplicability(descriptor);
165            runDeclarationCheckers(modifierListOwner, descriptor);
166        }
167    
168        public void reportIllegalModalityModifiers(@NotNull JetModifierListOwner modifierListOwner) {
169            reportIllegalModifiers(modifierListOwner, MODALITY_MODIFIERS);
170        }
171    
172        public void reportIllegalVisibilityModifiers(@NotNull JetModifierListOwner modifierListOwner) {
173            reportIllegalModifiers(modifierListOwner, VISIBILITY_MODIFIERS);
174        }
175    
176        private void checkModalityModifiers(@NotNull JetModifierListOwner modifierListOwner) {
177            JetModifierList modifierList = modifierListOwner.getModifierList();
178            if (modifierList == null) return;
179    
180            checkRedundantModifier(modifierList, Pair.create(OPEN_KEYWORD, ABSTRACT_KEYWORD), Pair.create(OPEN_KEYWORD, OVERRIDE_KEYWORD));
181    
182            checkCompatibility(modifierList, Arrays.asList(ABSTRACT_KEYWORD, OPEN_KEYWORD, FINAL_KEYWORD),
183                               Arrays.asList(ABSTRACT_KEYWORD, OPEN_KEYWORD));
184    
185            if (modifierListOwner instanceof JetObjectDeclaration) {
186                reportIllegalModalityModifiers(modifierListOwner);
187            }
188            else if (modifierListOwner instanceof JetClassOrObject) {
189                reportIllegalModifiers(modifierListOwner, Collections.singletonList(OVERRIDE_KEYWORD));
190            }
191        }
192    
193        private void checkVisibilityModifiers(@NotNull JetModifierListOwner modifierListOwner, @NotNull DeclarationDescriptor descriptor) {
194            JetModifierList modifierList = modifierListOwner.getModifierList();
195            if (modifierList == null) return;
196    
197            DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
198            if (containingDeclaration instanceof PackageFragmentDescriptor) {
199                if (modifierList.hasModifier(PROTECTED_KEYWORD)) {
200                    trace.report(Errors.PACKAGE_MEMBER_CANNOT_BE_PROTECTED.on(modifierListOwner));
201                }
202            }
203    
204            checkCompatibility(modifierList, VISIBILITY_MODIFIERS);
205        }
206    
207        private void checkInnerModifier(@NotNull JetModifierListOwner modifierListOwner, @NotNull DeclarationDescriptor descriptor) {
208            if (modifierListOwner.hasModifier(INNER_KEYWORD)) {
209                switch (checkIllegalInner(descriptor)) {
210                    case ALLOWED:
211                        break;
212                    case ILLEGAL_POSITION:
213                        reportIllegalModifiers(modifierListOwner, Collections.singletonList(INNER_KEYWORD));
214                        break;
215                    case IN_TRAIT:
216                        trace.report(INNER_CLASS_IN_TRAIT.on(modifierListOwner));
217                        break;
218                    case IN_OBJECT:
219                        trace.report(INNER_CLASS_IN_OBJECT.on(modifierListOwner));
220                        break;
221                }
222                return;
223            }
224            if (modifierListOwner instanceof JetClass && !(modifierListOwner instanceof JetEnumEntry)) {
225                JetClass aClass = (JetClass) modifierListOwner;
226                boolean localEnumError = aClass.isLocal() && aClass.isEnum();
227                if (!localEnumError && isIllegalNestedClass(descriptor)) {
228                    trace.report(NESTED_CLASS_NOT_ALLOWED.on(aClass));
229                }
230            }
231        }
232    
233        public static boolean isIllegalInner(@NotNull DeclarationDescriptor descriptor) {
234            return checkIllegalInner(descriptor) != InnerModifierCheckResult.ALLOWED;
235        }
236    
237        private enum InnerModifierCheckResult {
238            ALLOWED,
239            ILLEGAL_POSITION,
240            IN_TRAIT,
241            IN_OBJECT,
242        }
243    
244        private void checkDefaultModifier(@NotNull JetDeclaration declaration) {
245            if (declaration.hasModifier(COMPANION_KEYWORD) && !isDefaultModifierAllowed(declaration)) {
246                reportIllegalModifiers(declaration, Collections.singletonList(COMPANION_KEYWORD));
247            }
248        }
249    
250        // NOTE: just checks if this is legal context for companion modifier (Companion object descriptor can be created)
251        // COMPANION_OBJECT_NOT_ALLOWED can be reported later
252        public static boolean isDefaultModifierAllowed(@NotNull JetDeclaration declaration) {
253            if (declaration instanceof JetObjectDeclaration) {
254                JetDeclaration containingDeclaration = getContainingDeclaration(declaration);
255                if (containingDeclaration instanceof JetClassOrObject) {
256                    return true;
257                }
258            }
259            return false;
260        }
261    
262        @NotNull
263        private static InnerModifierCheckResult checkIllegalInner(DeclarationDescriptor descriptor) {
264            if (!(descriptor instanceof ClassDescriptor)) return InnerModifierCheckResult.ILLEGAL_POSITION;
265            ClassDescriptor classDescriptor = (ClassDescriptor) descriptor;
266    
267            if (classDescriptor.getKind() != ClassKind.CLASS) return InnerModifierCheckResult.ILLEGAL_POSITION;
268    
269            DeclarationDescriptor containingDeclaration = classDescriptor.getContainingDeclaration();
270            if (!(containingDeclaration instanceof ClassDescriptor)) return InnerModifierCheckResult.ILLEGAL_POSITION;
271    
272            if (DescriptorUtils.isTrait(containingDeclaration)) {
273                return InnerModifierCheckResult.IN_TRAIT;
274            }
275            else if (DescriptorUtils.isObject(containingDeclaration)) {
276                return InnerModifierCheckResult.IN_OBJECT;
277            }
278            else {
279                return InnerModifierCheckResult.ALLOWED;
280            }
281        }
282    
283        private static boolean isIllegalNestedClass(@NotNull DeclarationDescriptor descriptor) {
284            if (!(descriptor instanceof ClassDescriptor)) return false;
285            DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
286            if (!(containingDeclaration instanceof ClassDescriptor)) return false;
287            ClassDescriptor containingClass = (ClassDescriptor) containingDeclaration;
288            return containingClass.isInner() || containingClass.getContainingDeclaration() instanceof FunctionDescriptor;
289        }
290    
291        private void checkPlatformNameApplicability(@NotNull DeclarationDescriptor descriptor) {
292            AnnotationDescriptor annotation = descriptor.getAnnotations().findAnnotation(new FqName("kotlin.platform.platformName"));
293            if (annotation == null) return;
294    
295            JetAnnotationEntry annotationEntry = trace.get(BindingContext.ANNOTATION_DESCRIPTOR_TO_PSI_ELEMENT, annotation);
296            if (annotationEntry == null) return;
297    
298            if (!DescriptorUtils.isTopLevelDeclaration(descriptor) || !(descriptor instanceof FunctionDescriptor) ||
299                descriptor instanceof ConstructorDescriptor) {
300                trace.report(INAPPLICABLE_ANNOTATION.on(annotationEntry));
301            }
302    
303            Collection<CompileTimeConstant<?>> values = annotation.getAllValueArguments().values();
304            if (!values.isEmpty()) {
305                CompileTimeConstant<?> name = values.iterator().next();
306                if (name instanceof StringValue) {
307                    String value = ((StringValue) name).getValue();
308                    if (value == null || !Name.isValidIdentifier(value)) {
309                        trace.report(ILLEGAL_PLATFORM_NAME.on(annotationEntry, String.valueOf(value)));
310                    }
311                }
312            }
313        }
314    
315        private void checkCompatibility(@Nullable JetModifierList modifierList, Collection<JetModifierKeywordToken> availableModifiers, Collection<JetModifierKeywordToken>... availableCombinations) {
316            checkIncompatibleModifiers(modifierList, trace, availableModifiers, availableCombinations);
317        }
318    
319        private void checkRedundantModifier(@NotNull JetModifierList modifierList, Pair<JetModifierKeywordToken, JetModifierKeywordToken>... redundantBundles) {
320            for (Pair<JetModifierKeywordToken, JetModifierKeywordToken> tokenPair : redundantBundles) {
321                JetModifierKeywordToken redundantModifier = tokenPair.getFirst();
322                JetModifierKeywordToken sufficientModifier = tokenPair.getSecond();
323                if (modifierList.hasModifier(redundantModifier) && modifierList.hasModifier(sufficientModifier)) {
324                    trace.report(Errors.REDUNDANT_MODIFIER.on(modifierList.getModifierNode(redundantModifier).getPsi(), redundantModifier, sufficientModifier));
325                }
326            }
327        }
328    
329        public void reportIllegalModifiers(
330                @NotNull JetModifierListOwner modifierListOwner,
331                @NotNull Collection<JetModifierKeywordToken> illegalModifiers
332        ) {
333            reportIllegalModifiers(modifierListOwner.getModifierList(), illegalModifiers, trace);
334        }
335    
336        @NotNull
337        public static Map<JetModifierKeywordToken, ASTNode> getNodesCorrespondingToModifiers(@NotNull JetModifierList modifierList, @NotNull Collection<JetModifierKeywordToken> possibleModifiers) {
338            Map<JetModifierKeywordToken, ASTNode> nodes = Maps.newHashMap();
339            for (JetModifierKeywordToken modifier : possibleModifiers) {
340                if (modifierList.hasModifier(modifier)) {
341                    nodes.put(modifier, modifierList.getModifierNode(modifier));
342                }
343            }
344            return nodes;
345        }
346    
347        @NotNull
348        public static Modality resolveModalityFromModifiers(@NotNull JetModifierListOwner modifierListOwner, @NotNull Modality defaultModality) {
349            return resolveModalityFromModifiers(modifierListOwner.getModifierList(), defaultModality);
350        }
351    
352        public static Modality resolveModalityFromModifiers(@Nullable JetModifierList modifierList, @NotNull Modality defaultModality) {
353            if (modifierList == null) return defaultModality;
354            boolean hasAbstractModifier = modifierList.hasModifier(ABSTRACT_KEYWORD);
355            boolean hasOverrideModifier = modifierList.hasModifier(OVERRIDE_KEYWORD);
356    
357            if (modifierList.hasModifier(OPEN_KEYWORD)) {
358                if (hasAbstractModifier || defaultModality == Modality.ABSTRACT) {
359                    return Modality.ABSTRACT;
360                }
361                return Modality.OPEN;
362            }
363            if (hasAbstractModifier) {
364                return Modality.ABSTRACT;
365            }
366            boolean hasFinalModifier = modifierList.hasModifier(FINAL_KEYWORD);
367            if (hasOverrideModifier && !hasFinalModifier && !(defaultModality == Modality.ABSTRACT)) {
368                return Modality.OPEN;
369            }
370            if (hasFinalModifier) {
371                return Modality.FINAL;
372            }
373            return defaultModality;
374        }
375    
376        @NotNull
377        public static Visibility resolveVisibilityFromModifiers(@NotNull JetModifierListOwner modifierListOwner, @NotNull Visibility defaultVisibility) {
378            return resolveVisibilityFromModifiers(modifierListOwner.getModifierList(), defaultVisibility);
379        }
380    
381        public static Visibility resolveVisibilityFromModifiers(@Nullable JetModifierList modifierList, @NotNull Visibility defaultVisibility) {
382            if (modifierList == null) return defaultVisibility;
383            if (modifierList.hasModifier(PRIVATE_KEYWORD)) return Visibilities.PRIVATE;
384            if (modifierList.hasModifier(PUBLIC_KEYWORD)) return Visibilities.PUBLIC;
385            if (modifierList.hasModifier(PROTECTED_KEYWORD)) return Visibilities.PROTECTED;
386            if (modifierList.hasModifier(INTERNAL_KEYWORD)) return Visibilities.INTERNAL;
387            return defaultVisibility;
388        }
389    
390        public static boolean isInnerClass(@Nullable JetModifierList modifierList) {
391            return modifierList != null && modifierList.hasModifier(INNER_KEYWORD);
392        }
393    
394        @NotNull
395        public static Visibility getDefaultClassVisibility(@NotNull ClassDescriptor descriptor) {
396            if (isEnumEntry(descriptor) || isCompanionObject(descriptor)) {
397                // should be be accessible where containing class is accessible by default
398                return Visibilities.PUBLIC;
399            }
400            return Visibilities.INTERNAL;
401        }
402    
403        private void runDeclarationCheckers(@NotNull JetDeclaration declaration, @NotNull DeclarationDescriptor descriptor) {
404            for (DeclarationChecker checker : additionalCheckerProvider.getDeclarationCheckers()) {
405                checker.check(declaration, descriptor, trace);
406            }
407        }
408    
409        public void checkVarianceModifiers(@NotNull JetModifierListOwner modifierListOwner) {
410            reportIllegalModifiers(modifierListOwner, Arrays.asList(IN_KEYWORD, OUT_KEYWORD, REIFIED_KEYWORD));
411            if (!(modifierListOwner instanceof JetTypeParameterListOwner)) return;
412            List<JetTypeParameter> typeParameters = ((JetTypeParameterListOwner) modifierListOwner).getTypeParameters();
413            for (JetTypeParameter typeParameter : typeParameters) {
414                JetModifierList modifierList = typeParameter.getModifierList();
415                checkIncompatibleVarianceModifiers(modifierList, trace);
416            }
417        }
418    }