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 }