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