001    /*
002     * Copyright 2010-2014 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.jet.lang.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.jet.lang.descriptors.*;
028    import org.jetbrains.jet.lang.diagnostics.Errors;
029    import org.jetbrains.jet.lang.psi.JetClass;
030    import org.jetbrains.jet.lang.psi.JetEnumEntry;
031    import org.jetbrains.jet.lang.psi.JetModifierList;
032    import org.jetbrains.jet.lang.psi.JetModifierListOwner;
033    import org.jetbrains.jet.lexer.JetModifierKeywordToken;
034    
035    import java.util.Collection;
036    import java.util.Collections;
037    import java.util.Map;
038    
039    import static org.jetbrains.jet.lexer.JetTokens.*;
040    
041    public class ModifiersChecker {
042        private static final Collection<JetModifierKeywordToken> MODALITY_MODIFIERS =
043                Lists.newArrayList(ABSTRACT_KEYWORD, OPEN_KEYWORD, FINAL_KEYWORD, OVERRIDE_KEYWORD);
044    
045        private static final Collection<JetModifierKeywordToken> VISIBILITY_MODIFIERS =
046                Lists.newArrayList(PRIVATE_KEYWORD, PROTECTED_KEYWORD, PUBLIC_KEYWORD, INTERNAL_KEYWORD);
047    
048        @NotNull
049        private final BindingTrace trace;
050    
051        public ModifiersChecker(@NotNull BindingTrace trace) {
052            this.trace = trace;
053        }
054    
055        public static ModifiersChecker create(@NotNull BindingTrace trace) {
056            return new ModifiersChecker(trace);
057        }
058    
059        public void checkModifiersForDeclaration(@NotNull JetModifierListOwner modifierListOwner, @NotNull DeclarationDescriptor descriptor) {
060            JetModifierList modifierList = modifierListOwner.getModifierList();
061            checkModalityModifiers(modifierList);
062            checkVisibilityModifiers(modifierList, descriptor);
063            checkInnerModifier(modifierListOwner, descriptor);
064        }
065    
066        public void checkModifiersForLocalDeclaration(@NotNull JetModifierListOwner modifierListOwner) {
067            checkIllegalModalityModifiers(modifierListOwner);
068            checkIllegalVisibilityModifiers(modifierListOwner);
069        }
070    
071        public void checkIllegalModalityModifiers(@NotNull JetModifierListOwner modifierListOwner) {
072            checkIllegalInThisContextModifiers(modifierListOwner.getModifierList(), MODALITY_MODIFIERS);
073        }
074    
075        public void checkIllegalVisibilityModifiers(@NotNull JetModifierListOwner modifierListOwner) {
076            checkIllegalInThisContextModifiers(modifierListOwner.getModifierList(), VISIBILITY_MODIFIERS);
077        }
078    
079        private void checkModalityModifiers(@Nullable JetModifierList modifierList) {
080            if (modifierList == null) return;
081            checkRedundantModifier(modifierList, Pair.create(OPEN_KEYWORD, ABSTRACT_KEYWORD), Pair.create(OPEN_KEYWORD, OVERRIDE_KEYWORD));
082    
083            checkCompatibility(modifierList, Lists.newArrayList(ABSTRACT_KEYWORD, OPEN_KEYWORD, FINAL_KEYWORD),
084                               Lists.newArrayList(ABSTRACT_KEYWORD, OPEN_KEYWORD));
085        }
086    
087        private void checkVisibilityModifiers(@Nullable JetModifierList modifierList, @NotNull DeclarationDescriptor descriptor) {
088            if (modifierList == null) return;
089    
090            DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
091            if (containingDeclaration instanceof PackageFragmentDescriptor) {
092                if (modifierList.hasModifier(PROTECTED_KEYWORD)) {
093                    trace.report(Errors.PACKAGE_MEMBER_CANNOT_BE_PROTECTED.on(modifierList.getModifierNode(PROTECTED_KEYWORD).getPsi()));
094                }
095            }
096    
097            checkCompatibility(modifierList, VISIBILITY_MODIFIERS);
098        }
099    
100        private void checkInnerModifier(@NotNull JetModifierListOwner modifierListOwner, @NotNull DeclarationDescriptor descriptor) {
101            JetModifierList modifierList = modifierListOwner.getModifierList();
102    
103            if (modifierList != null && modifierList.hasModifier(INNER_KEYWORD)) {
104                if (isIllegalInner(descriptor)) {
105                    checkIllegalInThisContextModifiers(modifierList, Collections.singletonList(INNER_KEYWORD));
106                }
107            }
108            else {
109                if (modifierListOwner instanceof JetClass && !(modifierListOwner instanceof JetEnumEntry) && isIllegalNestedClass(descriptor)) {
110                    PsiElement name = ((JetClass) modifierListOwner).getNameIdentifier();
111                    if (name != null) {
112                        trace.report(Errors.NESTED_CLASS_NOT_ALLOWED.on(name));
113                    }
114                }
115            }
116        }
117    
118        private static boolean isIllegalInner(@NotNull DeclarationDescriptor descriptor) {
119            if (!(descriptor instanceof ClassDescriptor)) return true;
120            ClassDescriptor classDescriptor = (ClassDescriptor) descriptor;
121            if (classDescriptor.getKind() != ClassKind.CLASS) return true;
122            DeclarationDescriptor containingDeclaration = classDescriptor.getContainingDeclaration();
123            if (!(containingDeclaration instanceof ClassDescriptor)) return true;
124            return ((ClassDescriptor) containingDeclaration).getKind() == ClassKind.TRAIT;
125        }
126    
127        private static boolean isIllegalNestedClass(@NotNull DeclarationDescriptor descriptor) {
128            if (!(descriptor instanceof ClassDescriptor)) return false;
129            DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
130            if (!(containingDeclaration instanceof ClassDescriptor)) return false;
131            ClassDescriptor containingClass = (ClassDescriptor) containingDeclaration;
132            return containingClass.isInner() || containingClass.getContainingDeclaration() instanceof FunctionDescriptor;
133        }
134    
135        private void checkCompatibility(@Nullable JetModifierList modifierList, Collection<JetModifierKeywordToken> availableModifiers, Collection<JetModifierKeywordToken>... availableCombinations) {
136            if (modifierList == null) return;
137            Collection<JetModifierKeywordToken> presentModifiers = Sets.newLinkedHashSet();
138            for (JetModifierKeywordToken modifier : availableModifiers) {
139                if (modifierList.hasModifier(modifier)) {
140                    presentModifiers.add(modifier);
141                }
142            }
143            if (presentModifiers.size() == 1) {
144                return;
145            }
146            for (Collection<JetModifierKeywordToken> combination : availableCombinations) {
147                if (presentModifiers.containsAll(combination) && combination.containsAll(presentModifiers)) {
148                    return;
149                }
150            }
151            for (JetModifierKeywordToken token : presentModifiers) {
152                trace.report(Errors.INCOMPATIBLE_MODIFIERS.on(modifierList.getModifierNode(token).getPsi(), presentModifiers));
153            }
154        }
155    
156        private void checkRedundantModifier(@NotNull JetModifierList modifierList, Pair<JetModifierKeywordToken, JetModifierKeywordToken>... redundantBundles) {
157            for (Pair<JetModifierKeywordToken, JetModifierKeywordToken> tokenPair : redundantBundles) {
158                JetModifierKeywordToken redundantModifier = tokenPair.getFirst();
159                JetModifierKeywordToken sufficientModifier = tokenPair.getSecond();
160                if (modifierList.hasModifier(redundantModifier) && modifierList.hasModifier(sufficientModifier)) {
161                    trace.report(Errors.REDUNDANT_MODIFIER.on(modifierList.getModifierNode(redundantModifier).getPsi(), redundantModifier, sufficientModifier));
162                }
163            }
164        }
165    
166        public void checkIllegalInThisContextModifiers(@Nullable JetModifierList modifierList, @NotNull Collection<JetModifierKeywordToken> illegalModifiers) {
167            if (modifierList == null) return;
168            for (JetModifierKeywordToken modifier : illegalModifiers) {
169                if (modifierList.hasModifier(modifier)) {
170                    trace.report(Errors.ILLEGAL_MODIFIER.on(modifierList.getModifierNode(modifier).getPsi(), modifier));
171                }
172            }
173        }
174    
175        @NotNull
176        public static Map<JetModifierKeywordToken, ASTNode> getNodesCorrespondingToModifiers(@NotNull JetModifierList modifierList, @NotNull Collection<JetModifierKeywordToken> possibleModifiers) {
177            Map<JetModifierKeywordToken, ASTNode> nodes = Maps.newHashMap();
178            for (JetModifierKeywordToken modifier : possibleModifiers) {
179                if (modifierList.hasModifier(modifier)) {
180                    nodes.put(modifier, modifierList.getModifierNode(modifier));
181                }
182            }
183            return nodes;
184        }
185    
186        @NotNull
187        public static Modality resolveModalityFromModifiers(@NotNull JetModifierListOwner modifierListOwner, @NotNull Modality defaultModality) {
188            return resolveModalityFromModifiers(modifierListOwner.getModifierList(), defaultModality);
189        }
190    
191        public static Modality resolveModalityFromModifiers(@Nullable JetModifierList modifierList, @NotNull Modality defaultModality) {
192            if (modifierList == null) return defaultModality;
193            boolean hasAbstractModifier = modifierList.hasModifier(ABSTRACT_KEYWORD);
194            boolean hasOverrideModifier = modifierList.hasModifier(OVERRIDE_KEYWORD);
195    
196            if (modifierList.hasModifier(OPEN_KEYWORD)) {
197                if (hasAbstractModifier || defaultModality == Modality.ABSTRACT) {
198                    return Modality.ABSTRACT;
199                }
200                return Modality.OPEN;
201            }
202            if (hasAbstractModifier) {
203                return Modality.ABSTRACT;
204            }
205            boolean hasFinalModifier = modifierList.hasModifier(FINAL_KEYWORD);
206            if (hasOverrideModifier && !hasFinalModifier && !(defaultModality == Modality.ABSTRACT)) {
207                return Modality.OPEN;
208            }
209            if (hasFinalModifier) {
210                return Modality.FINAL;
211            }
212            return defaultModality;
213        }
214    
215        @NotNull
216        public static Visibility resolveVisibilityFromModifiers(@NotNull JetModifierListOwner modifierListOwner, @NotNull Visibility defaultVisibility) {
217            return resolveVisibilityFromModifiers(modifierListOwner.getModifierList(), defaultVisibility);
218        }
219    
220        public static Visibility resolveVisibilityFromModifiers(@Nullable JetModifierList modifierList, @NotNull Visibility defaultVisibility) {
221            if (modifierList == null) return defaultVisibility;
222            if (modifierList.hasModifier(PRIVATE_KEYWORD)) return Visibilities.PRIVATE;
223            if (modifierList.hasModifier(PUBLIC_KEYWORD)) return Visibilities.PUBLIC;
224            if (modifierList.hasModifier(PROTECTED_KEYWORD)) return Visibilities.PROTECTED;
225            if (modifierList.hasModifier(INTERNAL_KEYWORD)) return Visibilities.INTERNAL;
226            return defaultVisibility;
227        }
228    
229        public static boolean isInnerClass(@Nullable JetModifierList modifierList) {
230            return modifierList != null && modifierList.hasModifier(INNER_KEYWORD);
231        }
232    
233        @NotNull
234        public static Visibility getDefaultClassVisibility(@NotNull ClassDescriptor descriptor) {
235            ClassKind kind = descriptor.getKind();
236            if (kind == ClassKind.ENUM_ENTRY) {
237                return Visibilities.PUBLIC;
238            }
239            if (kind == ClassKind.CLASS_OBJECT) {
240                return ((ClassDescriptor) descriptor.getContainingDeclaration()).getVisibility();
241            }
242            return Visibilities.INTERNAL;
243        }
244    }