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 NamespaceDescriptor) {
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 }