001 /*
002 * Copyright 2010-2015 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.kotlin.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.kotlin.descriptors.*;
028 import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
029 import org.jetbrains.kotlin.diagnostics.*;
030 import org.jetbrains.kotlin.lexer.JetModifierKeywordToken;
031 import org.jetbrains.kotlin.lexer.JetTokens;
032 import org.jetbrains.kotlin.name.FqName;
033 import org.jetbrains.kotlin.name.Name;
034 import org.jetbrains.kotlin.psi.*;
035 import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
036 import org.jetbrains.kotlin.resolve.constants.StringValue;
037
038 import java.util.*;
039
040 import static org.jetbrains.kotlin.diagnostics.Errors.*;
041 import static org.jetbrains.kotlin.lexer.JetTokens.*;
042 import static org.jetbrains.kotlin.psi.JetStubbedPsiUtil.getContainingDeclaration;
043 import static org.jetbrains.kotlin.resolve.DescriptorUtils.isCompanionObject;
044 import static org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumEntry;
045
046 public class ModifiersChecker {
047 private static final Collection<JetModifierKeywordToken> MODALITY_MODIFIERS =
048 Lists.newArrayList(ABSTRACT_KEYWORD, OPEN_KEYWORD, FINAL_KEYWORD, OVERRIDE_KEYWORD);
049
050 private static final Collection<JetModifierKeywordToken> VISIBILITY_MODIFIERS =
051 Lists.newArrayList(PRIVATE_KEYWORD, PROTECTED_KEYWORD, PUBLIC_KEYWORD, INTERNAL_KEYWORD);
052
053 public static void reportIllegalModifiers(
054 @Nullable JetModifierList modifierList,
055 @NotNull Collection<JetModifierKeywordToken> illegalModifiers,
056 @NotNull BindingTrace trace
057 ) {
058 if (modifierList == null) return;
059
060 for (JetModifierKeywordToken modifierToken : illegalModifiers) {
061 if (modifierList.hasModifier(modifierToken)) {
062 PsiElement modifierPsi = modifierList.getModifier(modifierToken);
063 assert modifierPsi != null;
064 trace.report(ILLEGAL_MODIFIER.on(modifierPsi, modifierToken));
065 }
066 }
067 }
068
069 public static void checkIncompatibleModifiers(
070 @Nullable JetModifierList modifierList,
071 @NotNull BindingTrace trace,
072 @NotNull Collection<JetModifierKeywordToken> availableModifiers,
073 @NotNull Collection<JetModifierKeywordToken>... availableCombinations
074 ) {
075 if (modifierList == null) return;
076 Collection<JetModifierKeywordToken> presentModifiers = Sets.newLinkedHashSet();
077 for (JetModifierKeywordToken modifier : availableModifiers) {
078 if (modifierList.hasModifier(modifier)) {
079 presentModifiers.add(modifier);
080 }
081 }
082 checkRepeatedModifiers(modifierList, trace, availableModifiers);
083
084 if (presentModifiers.size() == 1) {
085 return;
086 }
087 for (Collection<JetModifierKeywordToken> combination : availableCombinations) {
088 if (presentModifiers.containsAll(combination) && combination.containsAll(presentModifiers)) {
089 return;
090 }
091 }
092 for (JetModifierKeywordToken token : presentModifiers) {
093 trace.report(Errors.INCOMPATIBLE_MODIFIERS.on(modifierList.getModifierNode(token).getPsi(), presentModifiers));
094 }
095 }
096
097 private static void checkRepeatedModifiers(
098 @NotNull JetModifierList modifierList,
099 @NotNull BindingTrace trace,
100 @NotNull Collection<JetModifierKeywordToken> availableModifiers
101 ) {
102 for (JetModifierKeywordToken token : availableModifiers) {
103 if (!modifierList.hasModifier(token)) continue;
104
105 List<ASTNode> nodesOfRepeatedTokens = Lists.newArrayList();
106 ASTNode node = modifierList.getNode().getFirstChildNode();
107 while (node != null) {
108 if (node.getElementType() == token) {
109 nodesOfRepeatedTokens.add(node);
110 }
111 node = node.getTreeNext();
112 }
113 if (nodesOfRepeatedTokens.size() > 1) {
114 for (ASTNode repeatedToken : nodesOfRepeatedTokens) {
115 trace.report(REPEATED_MODIFIER.on(repeatedToken.getPsi(), token));
116 }
117 }
118 }
119 }
120
121 public static void checkIncompatibleVarianceModifiers(@Nullable JetModifierList modifierList, @NotNull BindingTrace trace) {
122 checkIncompatibleModifiers(modifierList, trace, Arrays.asList(JetTokens.IN_KEYWORD, JetTokens.OUT_KEYWORD));
123 }
124
125 @NotNull
126 private final BindingTrace trace;
127 @NotNull
128 private final AdditionalCheckerProvider additionalCheckerProvider;
129
130 public ModifiersChecker(@NotNull BindingTrace trace, @NotNull AdditionalCheckerProvider additionalCheckerProvider) {
131 this.trace = trace;
132 this.additionalCheckerProvider = additionalCheckerProvider;
133 }
134
135 public static ModifiersChecker create(@NotNull BindingTrace trace, @NotNull AdditionalCheckerProvider provider) {
136 return new ModifiersChecker(trace, provider);
137 }
138
139 public void checkModifiersForDeclaration(@NotNull JetDeclaration modifierListOwner, @NotNull MemberDescriptor descriptor) {
140 if (modifierListOwner instanceof JetEnumEntry) {
141 reportIllegalModifiers(modifierListOwner, Arrays.asList(MODIFIER_KEYWORDS_ARRAY));
142 }
143 else {
144 checkInnerModifier(modifierListOwner, descriptor);
145 checkDefaultModifier(modifierListOwner);
146 checkModalityModifiers(modifierListOwner);
147 checkVisibilityModifiers(modifierListOwner, descriptor);
148 checkVarianceModifiers(modifierListOwner);
149 checkVarargsModifiers(modifierListOwner, descriptor);
150 }
151 checkPlatformNameApplicability(descriptor);
152 runDeclarationCheckers(modifierListOwner, descriptor);
153 }
154
155 private void checkVarargsModifiers(@NotNull JetDeclaration owner, @NotNull MemberDescriptor descriptor) {
156 if (!(owner instanceof JetParameter)) {
157 reportIllegalModifiers(owner, Collections.singleton(VARARG_KEYWORD));
158 }
159 }
160
161 public void checkModifiersForLocalDeclaration(@NotNull JetDeclaration modifierListOwner, @NotNull DeclarationDescriptor descriptor) {
162 reportIllegalModalityModifiers(modifierListOwner);
163 reportIllegalVisibilityModifiers(modifierListOwner);
164 checkPlatformNameApplicability(descriptor);
165 runDeclarationCheckers(modifierListOwner, descriptor);
166 }
167
168 public void reportIllegalModalityModifiers(@NotNull JetModifierListOwner modifierListOwner) {
169 reportIllegalModifiers(modifierListOwner, MODALITY_MODIFIERS);
170 }
171
172 public void reportIllegalVisibilityModifiers(@NotNull JetModifierListOwner modifierListOwner) {
173 reportIllegalModifiers(modifierListOwner, VISIBILITY_MODIFIERS);
174 }
175
176 private void checkModalityModifiers(@NotNull JetModifierListOwner modifierListOwner) {
177 JetModifierList modifierList = modifierListOwner.getModifierList();
178 if (modifierList == null) return;
179
180 checkRedundantModifier(modifierList, Pair.create(OPEN_KEYWORD, ABSTRACT_KEYWORD), Pair.create(OPEN_KEYWORD, OVERRIDE_KEYWORD));
181
182 checkCompatibility(modifierList, Arrays.asList(ABSTRACT_KEYWORD, OPEN_KEYWORD, FINAL_KEYWORD),
183 Arrays.asList(ABSTRACT_KEYWORD, OPEN_KEYWORD));
184
185 if (modifierListOwner instanceof JetObjectDeclaration) {
186 reportIllegalModalityModifiers(modifierListOwner);
187 }
188 else if (modifierListOwner instanceof JetClassOrObject) {
189 reportIllegalModifiers(modifierListOwner, Collections.singletonList(OVERRIDE_KEYWORD));
190 }
191 }
192
193 private void checkVisibilityModifiers(@NotNull JetModifierListOwner modifierListOwner, @NotNull DeclarationDescriptor descriptor) {
194 JetModifierList modifierList = modifierListOwner.getModifierList();
195 if (modifierList == null) return;
196
197 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
198 if (containingDeclaration instanceof PackageFragmentDescriptor) {
199 if (modifierList.hasModifier(PROTECTED_KEYWORD)) {
200 trace.report(Errors.PACKAGE_MEMBER_CANNOT_BE_PROTECTED.on(modifierListOwner));
201 }
202 }
203
204 checkCompatibility(modifierList, VISIBILITY_MODIFIERS);
205 }
206
207 private void checkInnerModifier(@NotNull JetModifierListOwner modifierListOwner, @NotNull DeclarationDescriptor descriptor) {
208 if (modifierListOwner.hasModifier(INNER_KEYWORD)) {
209 switch (checkIllegalInner(descriptor)) {
210 case ALLOWED:
211 break;
212 case ILLEGAL_POSITION:
213 reportIllegalModifiers(modifierListOwner, Collections.singletonList(INNER_KEYWORD));
214 break;
215 case IN_TRAIT:
216 trace.report(INNER_CLASS_IN_TRAIT.on(modifierListOwner));
217 break;
218 case IN_OBJECT:
219 trace.report(INNER_CLASS_IN_OBJECT.on(modifierListOwner));
220 break;
221 }
222 return;
223 }
224 if (modifierListOwner instanceof JetClass && !(modifierListOwner instanceof JetEnumEntry)) {
225 JetClass aClass = (JetClass) modifierListOwner;
226 boolean localEnumError = aClass.isLocal() && aClass.isEnum();
227 if (!localEnumError && isIllegalNestedClass(descriptor)) {
228 trace.report(NESTED_CLASS_NOT_ALLOWED.on(aClass));
229 }
230 }
231 }
232
233 public static boolean isIllegalInner(@NotNull DeclarationDescriptor descriptor) {
234 return checkIllegalInner(descriptor) != InnerModifierCheckResult.ALLOWED;
235 }
236
237 private enum InnerModifierCheckResult {
238 ALLOWED,
239 ILLEGAL_POSITION,
240 IN_TRAIT,
241 IN_OBJECT,
242 }
243
244 private void checkDefaultModifier(@NotNull JetDeclaration declaration) {
245 if (declaration.hasModifier(COMPANION_KEYWORD) && !isDefaultModifierAllowed(declaration)) {
246 reportIllegalModifiers(declaration, Collections.singletonList(COMPANION_KEYWORD));
247 }
248 }
249
250 // NOTE: just checks if this is legal context for companion modifier (Companion object descriptor can be created)
251 // COMPANION_OBJECT_NOT_ALLOWED can be reported later
252 public static boolean isDefaultModifierAllowed(@NotNull JetDeclaration declaration) {
253 if (declaration instanceof JetObjectDeclaration) {
254 JetDeclaration containingDeclaration = getContainingDeclaration(declaration);
255 if (containingDeclaration instanceof JetClassOrObject) {
256 return true;
257 }
258 }
259 return false;
260 }
261
262 @NotNull
263 private static InnerModifierCheckResult checkIllegalInner(DeclarationDescriptor descriptor) {
264 if (!(descriptor instanceof ClassDescriptor)) return InnerModifierCheckResult.ILLEGAL_POSITION;
265 ClassDescriptor classDescriptor = (ClassDescriptor) descriptor;
266
267 if (classDescriptor.getKind() != ClassKind.CLASS) return InnerModifierCheckResult.ILLEGAL_POSITION;
268
269 DeclarationDescriptor containingDeclaration = classDescriptor.getContainingDeclaration();
270 if (!(containingDeclaration instanceof ClassDescriptor)) return InnerModifierCheckResult.ILLEGAL_POSITION;
271
272 if (DescriptorUtils.isTrait(containingDeclaration)) {
273 return InnerModifierCheckResult.IN_TRAIT;
274 }
275 else if (DescriptorUtils.isObject(containingDeclaration)) {
276 return InnerModifierCheckResult.IN_OBJECT;
277 }
278 else {
279 return InnerModifierCheckResult.ALLOWED;
280 }
281 }
282
283 private static boolean isIllegalNestedClass(@NotNull DeclarationDescriptor descriptor) {
284 if (!(descriptor instanceof ClassDescriptor)) return false;
285 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
286 if (!(containingDeclaration instanceof ClassDescriptor)) return false;
287 ClassDescriptor containingClass = (ClassDescriptor) containingDeclaration;
288 return containingClass.isInner() || containingClass.getContainingDeclaration() instanceof FunctionDescriptor;
289 }
290
291 private void checkPlatformNameApplicability(@NotNull DeclarationDescriptor descriptor) {
292 AnnotationDescriptor annotation = descriptor.getAnnotations().findAnnotation(new FqName("kotlin.platform.platformName"));
293 if (annotation == null) return;
294
295 JetAnnotationEntry annotationEntry = trace.get(BindingContext.ANNOTATION_DESCRIPTOR_TO_PSI_ELEMENT, annotation);
296 if (annotationEntry == null) return;
297
298 if (!DescriptorUtils.isTopLevelDeclaration(descriptor) || !(descriptor instanceof FunctionDescriptor) ||
299 descriptor instanceof ConstructorDescriptor) {
300 trace.report(INAPPLICABLE_ANNOTATION.on(annotationEntry));
301 }
302
303 Collection<CompileTimeConstant<?>> values = annotation.getAllValueArguments().values();
304 if (!values.isEmpty()) {
305 CompileTimeConstant<?> name = values.iterator().next();
306 if (name instanceof StringValue) {
307 String value = ((StringValue) name).getValue();
308 if (value == null || !Name.isValidIdentifier(value)) {
309 trace.report(ILLEGAL_PLATFORM_NAME.on(annotationEntry, String.valueOf(value)));
310 }
311 }
312 }
313 }
314
315 private void checkCompatibility(@Nullable JetModifierList modifierList, Collection<JetModifierKeywordToken> availableModifiers, Collection<JetModifierKeywordToken>... availableCombinations) {
316 checkIncompatibleModifiers(modifierList, trace, availableModifiers, availableCombinations);
317 }
318
319 private void checkRedundantModifier(@NotNull JetModifierList modifierList, Pair<JetModifierKeywordToken, JetModifierKeywordToken>... redundantBundles) {
320 for (Pair<JetModifierKeywordToken, JetModifierKeywordToken> tokenPair : redundantBundles) {
321 JetModifierKeywordToken redundantModifier = tokenPair.getFirst();
322 JetModifierKeywordToken sufficientModifier = tokenPair.getSecond();
323 if (modifierList.hasModifier(redundantModifier) && modifierList.hasModifier(sufficientModifier)) {
324 trace.report(Errors.REDUNDANT_MODIFIER.on(modifierList.getModifierNode(redundantModifier).getPsi(), redundantModifier, sufficientModifier));
325 }
326 }
327 }
328
329 public void reportIllegalModifiers(
330 @NotNull JetModifierListOwner modifierListOwner,
331 @NotNull Collection<JetModifierKeywordToken> illegalModifiers
332 ) {
333 reportIllegalModifiers(modifierListOwner.getModifierList(), illegalModifiers, trace);
334 }
335
336 @NotNull
337 public static Map<JetModifierKeywordToken, ASTNode> getNodesCorrespondingToModifiers(@NotNull JetModifierList modifierList, @NotNull Collection<JetModifierKeywordToken> possibleModifiers) {
338 Map<JetModifierKeywordToken, ASTNode> nodes = Maps.newHashMap();
339 for (JetModifierKeywordToken modifier : possibleModifiers) {
340 if (modifierList.hasModifier(modifier)) {
341 nodes.put(modifier, modifierList.getModifierNode(modifier));
342 }
343 }
344 return nodes;
345 }
346
347 @NotNull
348 public static Modality resolveModalityFromModifiers(@NotNull JetModifierListOwner modifierListOwner, @NotNull Modality defaultModality) {
349 return resolveModalityFromModifiers(modifierListOwner.getModifierList(), defaultModality);
350 }
351
352 public static Modality resolveModalityFromModifiers(@Nullable JetModifierList modifierList, @NotNull Modality defaultModality) {
353 if (modifierList == null) return defaultModality;
354 boolean hasAbstractModifier = modifierList.hasModifier(ABSTRACT_KEYWORD);
355 boolean hasOverrideModifier = modifierList.hasModifier(OVERRIDE_KEYWORD);
356
357 if (modifierList.hasModifier(OPEN_KEYWORD)) {
358 if (hasAbstractModifier || defaultModality == Modality.ABSTRACT) {
359 return Modality.ABSTRACT;
360 }
361 return Modality.OPEN;
362 }
363 if (hasAbstractModifier) {
364 return Modality.ABSTRACT;
365 }
366 boolean hasFinalModifier = modifierList.hasModifier(FINAL_KEYWORD);
367 if (hasOverrideModifier && !hasFinalModifier && !(defaultModality == Modality.ABSTRACT)) {
368 return Modality.OPEN;
369 }
370 if (hasFinalModifier) {
371 return Modality.FINAL;
372 }
373 return defaultModality;
374 }
375
376 @NotNull
377 public static Visibility resolveVisibilityFromModifiers(@NotNull JetModifierListOwner modifierListOwner, @NotNull Visibility defaultVisibility) {
378 return resolveVisibilityFromModifiers(modifierListOwner.getModifierList(), defaultVisibility);
379 }
380
381 public static Visibility resolveVisibilityFromModifiers(@Nullable JetModifierList modifierList, @NotNull Visibility defaultVisibility) {
382 if (modifierList == null) return defaultVisibility;
383 if (modifierList.hasModifier(PRIVATE_KEYWORD)) return Visibilities.PRIVATE;
384 if (modifierList.hasModifier(PUBLIC_KEYWORD)) return Visibilities.PUBLIC;
385 if (modifierList.hasModifier(PROTECTED_KEYWORD)) return Visibilities.PROTECTED;
386 if (modifierList.hasModifier(INTERNAL_KEYWORD)) return Visibilities.INTERNAL;
387 return defaultVisibility;
388 }
389
390 public static boolean isInnerClass(@Nullable JetModifierList modifierList) {
391 return modifierList != null && modifierList.hasModifier(INNER_KEYWORD);
392 }
393
394 @NotNull
395 public static Visibility getDefaultClassVisibility(@NotNull ClassDescriptor descriptor) {
396 if (isEnumEntry(descriptor) || isCompanionObject(descriptor)) {
397 // should be be accessible where containing class is accessible by default
398 return Visibilities.PUBLIC;
399 }
400 return Visibilities.INTERNAL;
401 }
402
403 private void runDeclarationCheckers(@NotNull JetDeclaration declaration, @NotNull DeclarationDescriptor descriptor) {
404 for (DeclarationChecker checker : additionalCheckerProvider.getDeclarationCheckers()) {
405 checker.check(declaration, descriptor, trace);
406 }
407 }
408
409 public void checkVarianceModifiers(@NotNull JetModifierListOwner modifierListOwner) {
410 reportIllegalModifiers(modifierListOwner, Arrays.asList(IN_KEYWORD, OUT_KEYWORD, REIFIED_KEYWORD));
411 if (!(modifierListOwner instanceof JetTypeParameterListOwner)) return;
412 List<JetTypeParameter> typeParameters = ((JetTypeParameterListOwner) modifierListOwner).getTypeParameters();
413 for (JetTypeParameter typeParameter : typeParameters) {
414 JetModifierList modifierList = typeParameter.getModifierList();
415 checkIncompatibleVarianceModifiers(modifierList, trace);
416 }
417 }
418 }