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.JetModifierList;
031 import org.jetbrains.jet.lang.psi.JetModifierListOwner;
032 import org.jetbrains.jet.lexer.JetKeywordToken;
033 import org.jetbrains.jet.lexer.JetToken;
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<JetKeywordToken> MODALITY_MODIFIERS =
043 Lists.newArrayList(ABSTRACT_KEYWORD, OPEN_KEYWORD, FINAL_KEYWORD, OVERRIDE_KEYWORD);
044
045 private static final Collection<JetKeywordToken> 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.<JetToken>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 NamespaceDescriptor) {
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 && 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<JetKeywordToken> availableModifiers, Collection<JetToken>... availableCombinations) {
136 if (modifierList == null) return;
137 Collection<JetKeywordToken> presentModifiers = Sets.newLinkedHashSet();
138 for (JetKeywordToken modifier : availableModifiers) {
139 if (modifierList.hasModifier(modifier)) {
140 presentModifiers.add(modifier);
141 }
142 }
143 if (presentModifiers.size() == 1) {
144 return;
145 }
146 for (Collection<JetToken> combination : availableCombinations) {
147 if (presentModifiers.containsAll(combination) && combination.containsAll(presentModifiers)) {
148 return;
149 }
150 }
151 for (JetKeywordToken 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<JetKeywordToken, JetKeywordToken>... redundantBundles) {
157 for (Pair<JetKeywordToken, JetKeywordToken> tokenPair : redundantBundles) {
158 JetKeywordToken redundantModifier = tokenPair.getFirst();
159 JetKeywordToken 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<JetKeywordToken> illegalModifiers) {
167 if (modifierList == null) return;
168 for (JetKeywordToken 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<JetKeywordToken, ASTNode> getNodesCorrespondingToModifiers(@NotNull JetModifierList modifierList, @NotNull Collection<JetKeywordToken> possibleModifiers) {
177 Map<JetKeywordToken, ASTNode> nodes = Maps.newHashMap();
178 for (JetKeywordToken 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) {
217 return resolveVisibilityFromModifiers(modifierListOwner, Visibilities.INTERNAL);
218 }
219
220 @NotNull
221 public static Visibility resolveVisibilityFromModifiers(@NotNull JetModifierListOwner modifierListOwner, @NotNull Visibility defaultVisibility) {
222 return resolveVisibilityFromModifiers(modifierListOwner.getModifierList(), defaultVisibility);
223 }
224
225 public static Visibility resolveVisibilityFromModifiers(@Nullable JetModifierList modifierList, @NotNull Visibility defaultVisibility) {
226 if (modifierList == null) return defaultVisibility;
227 if (modifierList.hasModifier(PRIVATE_KEYWORD)) return Visibilities.PRIVATE;
228 if (modifierList.hasModifier(PUBLIC_KEYWORD)) return Visibilities.PUBLIC;
229 if (modifierList.hasModifier(PROTECTED_KEYWORD)) return Visibilities.PROTECTED;
230 if (modifierList.hasModifier(INTERNAL_KEYWORD)) return Visibilities.INTERNAL;
231 return defaultVisibility;
232 }
233
234 public static boolean isInnerClass(@Nullable JetModifierList modifierList) {
235 return modifierList != null && modifierList.hasModifier(INNER_KEYWORD);
236 }
237
238 @NotNull
239 public static Visibility getDefaultClassVisibility(@NotNull ClassDescriptor descriptor) {
240 ClassKind kind = descriptor.getKind();
241 if (kind == ClassKind.ENUM_ENTRY) {
242 return Visibilities.PRIVATE;
243 }
244 return Visibilities.INTERNAL;
245 }
246
247 @NotNull
248 public static Visibility getDefaultVisibilityForObjectPropertyDescriptor(@NotNull ClassDescriptor objectClassDescriptor) {
249 if (objectClassDescriptor.getKind() == ClassKind.ENUM_ENTRY) {
250 return Visibilities.PUBLIC;
251 }
252 return Visibilities.INTERNAL;
253 }
254 }