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