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.descriptors.annotations.AnnotationDescriptor;
029 import org.jetbrains.jet.lang.diagnostics.Errors;
030 import org.jetbrains.jet.lang.psi.*;
031 import org.jetbrains.jet.lang.resolve.constants.*;
032 import org.jetbrains.jet.lang.resolve.name.FqName;
033 import org.jetbrains.jet.lang.resolve.name.Name;
034 import org.jetbrains.jet.lexer.JetModifierKeywordToken;
035
036 import java.util.Collection;
037 import java.util.Collections;
038 import java.util.Map;
039
040 import static org.jetbrains.jet.lang.diagnostics.Errors.*;
041 import static org.jetbrains.jet.lexer.JetTokens.*;
042
043 public class ModifiersChecker {
044 private static final Collection<JetModifierKeywordToken> MODALITY_MODIFIERS =
045 Lists.newArrayList(ABSTRACT_KEYWORD, OPEN_KEYWORD, FINAL_KEYWORD, OVERRIDE_KEYWORD);
046
047 private static final Collection<JetModifierKeywordToken> VISIBILITY_MODIFIERS =
048 Lists.newArrayList(PRIVATE_KEYWORD, PROTECTED_KEYWORD, PUBLIC_KEYWORD, INTERNAL_KEYWORD);
049
050 public static void reportIllegalModifiers(
051 @Nullable JetModifierList modifierList,
052 @NotNull Collection<JetModifierKeywordToken> illegalModifiers,
053 @NotNull BindingTrace trace
054 ) {
055 if (modifierList == null) return;
056
057 for (JetModifierKeywordToken modifierToken : illegalModifiers) {
058 if (modifierList.hasModifier(modifierToken)) {
059 PsiElement modifierPsi = modifierList.getModifier(modifierToken);
060 assert modifierPsi != null;
061 trace.report(ILLEGAL_MODIFIER.on(modifierPsi, modifierToken));
062 }
063 }
064 }
065
066 @NotNull
067 private final BindingTrace trace;
068 @NotNull
069 private AdditionalCheckerProvider additionalCheckerProvider;
070
071 public ModifiersChecker(@NotNull BindingTrace trace, @NotNull AdditionalCheckerProvider provider) {
072 this.trace = trace;
073 additionalCheckerProvider = provider;
074 }
075
076 public static ModifiersChecker create(@NotNull BindingTrace trace, @NotNull AdditionalCheckerProvider provider) {
077 return new ModifiersChecker(trace, provider);
078 }
079
080 public void checkModifiersForDeclaration(@NotNull JetDeclaration modifierListOwner, @NotNull MemberDescriptor descriptor) {
081 JetModifierList modifierList = modifierListOwner.getModifierList();
082 checkModalityModifiers(modifierList);
083 checkVisibilityModifiers(modifierListOwner, descriptor);
084 checkInnerModifier(modifierListOwner, descriptor);
085 checkPlatformNameApplicability(descriptor);
086 runAnnotationCheckers(modifierListOwner, descriptor);
087 }
088
089 public void checkModifiersForLocalDeclaration(@NotNull JetDeclaration modifierListOwner, @NotNull DeclarationDescriptor descriptor) {
090 checkIllegalModalityModifiers(modifierListOwner);
091 checkIllegalVisibilityModifiers(modifierListOwner);
092 checkPlatformNameApplicability(descriptor);
093 runAnnotationCheckers(modifierListOwner, descriptor);
094 }
095
096 public void checkIllegalModalityModifiers(@NotNull JetModifierListOwner modifierListOwner) {
097 checkIllegalInThisContextModifiers(modifierListOwner.getModifierList(), MODALITY_MODIFIERS);
098 }
099
100 public void checkIllegalVisibilityModifiers(@NotNull JetModifierListOwner modifierListOwner) {
101 checkIllegalInThisContextModifiers(modifierListOwner.getModifierList(), VISIBILITY_MODIFIERS);
102 }
103
104 private void checkModalityModifiers(@Nullable JetModifierList modifierList) {
105 if (modifierList == null) return;
106 checkRedundantModifier(modifierList, Pair.create(OPEN_KEYWORD, ABSTRACT_KEYWORD), Pair.create(OPEN_KEYWORD, OVERRIDE_KEYWORD));
107
108 checkCompatibility(modifierList, Lists.newArrayList(ABSTRACT_KEYWORD, OPEN_KEYWORD, FINAL_KEYWORD),
109 Lists.newArrayList(ABSTRACT_KEYWORD, OPEN_KEYWORD));
110 }
111
112 private void checkVisibilityModifiers(@NotNull JetModifierListOwner modifierListOwner, @NotNull DeclarationDescriptor descriptor) {
113 JetModifierList modifierList = modifierListOwner.getModifierList();
114 if (modifierList == null) return;
115
116 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
117 if (containingDeclaration instanceof PackageFragmentDescriptor) {
118 if (modifierList.hasModifier(PROTECTED_KEYWORD)) {
119 trace.report(Errors.PACKAGE_MEMBER_CANNOT_BE_PROTECTED.on(modifierListOwner));
120 }
121 }
122
123 checkCompatibility(modifierList, VISIBILITY_MODIFIERS);
124 }
125
126 private void checkInnerModifier(@NotNull JetModifierListOwner modifierListOwner, @NotNull DeclarationDescriptor descriptor) {
127 if (modifierListOwner.hasModifier(INNER_KEYWORD)) {
128 if (isIllegalInner(descriptor)) {
129 checkIllegalInThisContextModifiers(modifierListOwner.getModifierList(), Collections.singletonList(INNER_KEYWORD));
130 }
131 return;
132 }
133 if (modifierListOwner instanceof JetClass && !(modifierListOwner instanceof JetEnumEntry)) {
134 JetClass aClass = (JetClass) modifierListOwner;
135 boolean localEnumError = aClass.isLocal() && aClass.isEnum();
136 if (!localEnumError && isIllegalNestedClass(descriptor)) {
137 trace.report(NESTED_CLASS_NOT_ALLOWED.on(aClass));
138 }
139 }
140 }
141
142 private static boolean isIllegalInner(@NotNull DeclarationDescriptor descriptor) {
143 if (!(descriptor instanceof ClassDescriptor)) return true;
144 ClassDescriptor classDescriptor = (ClassDescriptor) descriptor;
145 if (classDescriptor.getKind() != ClassKind.CLASS) return true;
146 DeclarationDescriptor containingDeclaration = classDescriptor.getContainingDeclaration();
147 if (!(containingDeclaration instanceof ClassDescriptor)) return true;
148 return ((ClassDescriptor) containingDeclaration).getKind() == ClassKind.TRAIT;
149 }
150
151 private static boolean isIllegalNestedClass(@NotNull DeclarationDescriptor descriptor) {
152 if (!(descriptor instanceof ClassDescriptor)) return false;
153 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
154 if (!(containingDeclaration instanceof ClassDescriptor)) return false;
155 ClassDescriptor containingClass = (ClassDescriptor) containingDeclaration;
156 return containingClass.isInner() || containingClass.getContainingDeclaration() instanceof FunctionDescriptor;
157 }
158
159 private void checkPlatformNameApplicability(@NotNull DeclarationDescriptor descriptor) {
160 if (descriptor instanceof PropertyDescriptor) {
161 PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
162 if (propertyDescriptor.getGetter() != null) {
163 checkPlatformNameApplicability(propertyDescriptor.getGetter());
164 }
165 if (propertyDescriptor.getSetter() != null) {
166 checkPlatformNameApplicability(propertyDescriptor.getSetter());
167 }
168 }
169
170 AnnotationDescriptor annotation = descriptor.getAnnotations().findAnnotation(new FqName("kotlin.platform.platformName"));
171 if (annotation == null) return;
172
173 JetAnnotationEntry annotationEntry = trace.get(BindingContext.ANNOTATION_DESCRIPTOR_TO_PSI_ELEMENT, annotation);
174 if (annotationEntry == null) return;
175
176 if (!DescriptorUtils.isTopLevelDeclaration(descriptor) || !(descriptor instanceof FunctionDescriptor)) {
177 trace.report(INAPPLICABLE_ANNOTATION.on(annotationEntry));
178 }
179
180 Collection<CompileTimeConstant<?>> values = annotation.getAllValueArguments().values();
181 if (!values.isEmpty()) {
182 CompileTimeConstant<?> name = values.iterator().next();
183 if (name instanceof StringValue) {
184 String value = ((StringValue) name).getValue();
185 if (value == null || !Name.isValidIdentifier(value)) {
186 trace.report(ILLEGAL_PLATFORM_NAME.on(annotationEntry, String.valueOf(value)));
187 }
188 }
189 }
190 }
191
192 private void checkCompatibility(@Nullable JetModifierList modifierList, Collection<JetModifierKeywordToken> availableModifiers, Collection<JetModifierKeywordToken>... availableCombinations) {
193 if (modifierList == null) return;
194 Collection<JetModifierKeywordToken> presentModifiers = Sets.newLinkedHashSet();
195 for (JetModifierKeywordToken modifier : availableModifiers) {
196 if (modifierList.hasModifier(modifier)) {
197 presentModifiers.add(modifier);
198 }
199 }
200 if (presentModifiers.size() == 1) {
201 return;
202 }
203 for (Collection<JetModifierKeywordToken> combination : availableCombinations) {
204 if (presentModifiers.containsAll(combination) && combination.containsAll(presentModifiers)) {
205 return;
206 }
207 }
208 for (JetModifierKeywordToken token : presentModifiers) {
209 trace.report(Errors.INCOMPATIBLE_MODIFIERS.on(modifierList.getModifierNode(token).getPsi(), presentModifiers));
210 }
211 }
212
213 private void checkRedundantModifier(@NotNull JetModifierList modifierList, Pair<JetModifierKeywordToken, JetModifierKeywordToken>... redundantBundles) {
214 for (Pair<JetModifierKeywordToken, JetModifierKeywordToken> tokenPair : redundantBundles) {
215 JetModifierKeywordToken redundantModifier = tokenPair.getFirst();
216 JetModifierKeywordToken sufficientModifier = tokenPair.getSecond();
217 if (modifierList.hasModifier(redundantModifier) && modifierList.hasModifier(sufficientModifier)) {
218 trace.report(Errors.REDUNDANT_MODIFIER.on(modifierList.getModifierNode(redundantModifier).getPsi(), redundantModifier, sufficientModifier));
219 }
220 }
221 }
222
223 public void checkIllegalInThisContextModifiers(
224 @Nullable JetModifierList modifierList,
225 @NotNull Collection<JetModifierKeywordToken> illegalModifiers
226 ) {
227 reportIllegalModifiers(modifierList, illegalModifiers, trace);
228 }
229
230 @NotNull
231 public static Map<JetModifierKeywordToken, ASTNode> getNodesCorrespondingToModifiers(@NotNull JetModifierList modifierList, @NotNull Collection<JetModifierKeywordToken> possibleModifiers) {
232 Map<JetModifierKeywordToken, ASTNode> nodes = Maps.newHashMap();
233 for (JetModifierKeywordToken modifier : possibleModifiers) {
234 if (modifierList.hasModifier(modifier)) {
235 nodes.put(modifier, modifierList.getModifierNode(modifier));
236 }
237 }
238 return nodes;
239 }
240
241 @NotNull
242 public static Modality resolveModalityFromModifiers(@NotNull JetModifierListOwner modifierListOwner, @NotNull Modality defaultModality) {
243 return resolveModalityFromModifiers(modifierListOwner.getModifierList(), defaultModality);
244 }
245
246 public static Modality resolveModalityFromModifiers(@Nullable JetModifierList modifierList, @NotNull Modality defaultModality) {
247 if (modifierList == null) return defaultModality;
248 boolean hasAbstractModifier = modifierList.hasModifier(ABSTRACT_KEYWORD);
249 boolean hasOverrideModifier = modifierList.hasModifier(OVERRIDE_KEYWORD);
250
251 if (modifierList.hasModifier(OPEN_KEYWORD)) {
252 if (hasAbstractModifier || defaultModality == Modality.ABSTRACT) {
253 return Modality.ABSTRACT;
254 }
255 return Modality.OPEN;
256 }
257 if (hasAbstractModifier) {
258 return Modality.ABSTRACT;
259 }
260 boolean hasFinalModifier = modifierList.hasModifier(FINAL_KEYWORD);
261 if (hasOverrideModifier && !hasFinalModifier && !(defaultModality == Modality.ABSTRACT)) {
262 return Modality.OPEN;
263 }
264 if (hasFinalModifier) {
265 return Modality.FINAL;
266 }
267 return defaultModality;
268 }
269
270 @NotNull
271 public static Visibility resolveVisibilityFromModifiers(@NotNull JetModifierListOwner modifierListOwner, @NotNull Visibility defaultVisibility) {
272 return resolveVisibilityFromModifiers(modifierListOwner.getModifierList(), defaultVisibility);
273 }
274
275 public static Visibility resolveVisibilityFromModifiers(@Nullable JetModifierList modifierList, @NotNull Visibility defaultVisibility) {
276 if (modifierList == null) return defaultVisibility;
277 if (modifierList.hasModifier(PRIVATE_KEYWORD)) return Visibilities.PRIVATE;
278 if (modifierList.hasModifier(PUBLIC_KEYWORD)) return Visibilities.PUBLIC;
279 if (modifierList.hasModifier(PROTECTED_KEYWORD)) return Visibilities.PROTECTED;
280 if (modifierList.hasModifier(INTERNAL_KEYWORD)) return Visibilities.INTERNAL;
281 return defaultVisibility;
282 }
283
284 public static boolean isInnerClass(@Nullable JetModifierList modifierList) {
285 return modifierList != null && modifierList.hasModifier(INNER_KEYWORD);
286 }
287
288 @NotNull
289 public static Visibility getDefaultClassVisibility(@NotNull ClassDescriptor descriptor) {
290 ClassKind kind = descriptor.getKind();
291 if (kind == ClassKind.ENUM_ENTRY) {
292 return Visibilities.PUBLIC;
293 }
294 if (kind == ClassKind.CLASS_OBJECT) {
295 return ((ClassDescriptor) descriptor.getContainingDeclaration()).getVisibility();
296 }
297 return Visibilities.INTERNAL;
298 }
299
300 private void runAnnotationCheckers(@NotNull JetDeclaration declaration, @NotNull DeclarationDescriptor descriptor) {
301 for (AnnotationChecker checker : additionalCheckerProvider.getAnnotationCheckers()) {
302 checker.check(declaration, descriptor, trace);
303 }
304 }
305 }