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 checkVarianceModifiersOfTypeParameters(modifierListOwner); 149 } 150 checkPlatformNameApplicability(descriptor); 151 runAnnotationCheckers(modifierListOwner, descriptor); 152 } 153 154 public void checkModifiersForLocalDeclaration(@NotNull JetDeclaration modifierListOwner, @NotNull DeclarationDescriptor descriptor) { 155 reportIllegalModalityModifiers(modifierListOwner); 156 reportIllegalVisibilityModifiers(modifierListOwner); 157 checkPlatformNameApplicability(descriptor); 158 runAnnotationCheckers(modifierListOwner, descriptor); 159 } 160 161 public void reportIllegalModalityModifiers(@NotNull JetModifierListOwner modifierListOwner) { 162 reportIllegalModifiers(modifierListOwner, MODALITY_MODIFIERS); 163 } 164 165 public void reportIllegalVisibilityModifiers(@NotNull JetModifierListOwner modifierListOwner) { 166 reportIllegalModifiers(modifierListOwner, VISIBILITY_MODIFIERS); 167 } 168 169 private void checkModalityModifiers(@NotNull JetModifierListOwner modifierListOwner) { 170 JetModifierList modifierList = modifierListOwner.getModifierList(); 171 if (modifierList == null) return; 172 173 checkRedundantModifier(modifierList, Pair.create(OPEN_KEYWORD, ABSTRACT_KEYWORD), Pair.create(OPEN_KEYWORD, OVERRIDE_KEYWORD)); 174 175 checkCompatibility(modifierList, Arrays.asList(ABSTRACT_KEYWORD, OPEN_KEYWORD, FINAL_KEYWORD), 176 Arrays.asList(ABSTRACT_KEYWORD, OPEN_KEYWORD)); 177 178 if (modifierListOwner instanceof JetObjectDeclaration) { 179 reportIllegalModalityModifiers(modifierListOwner); 180 } 181 else if (modifierListOwner instanceof JetClassOrObject) { 182 reportIllegalModifiers(modifierListOwner, Collections.singletonList(OVERRIDE_KEYWORD)); 183 } 184 } 185 186 private void checkVisibilityModifiers(@NotNull JetModifierListOwner modifierListOwner, @NotNull DeclarationDescriptor descriptor) { 187 JetModifierList modifierList = modifierListOwner.getModifierList(); 188 if (modifierList == null) return; 189 190 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration(); 191 if (containingDeclaration instanceof PackageFragmentDescriptor) { 192 if (modifierList.hasModifier(PROTECTED_KEYWORD)) { 193 trace.report(Errors.PACKAGE_MEMBER_CANNOT_BE_PROTECTED.on(modifierListOwner)); 194 } 195 } 196 197 checkCompatibility(modifierList, VISIBILITY_MODIFIERS); 198 } 199 200 private void checkInnerModifier(@NotNull JetModifierListOwner modifierListOwner, @NotNull DeclarationDescriptor descriptor) { 201 if (modifierListOwner.hasModifier(INNER_KEYWORD)) { 202 switch (checkIllegalInner(descriptor)) { 203 case ALLOWED: 204 break; 205 case ILLEGAL_POSITION: 206 reportIllegalModifiers(modifierListOwner, Collections.singletonList(INNER_KEYWORD)); 207 break; 208 case IN_TRAIT: 209 trace.report(INNER_CLASS_IN_TRAIT.on(modifierListOwner)); 210 break; 211 case IN_OBJECT: 212 trace.report(INNER_CLASS_IN_OBJECT.on(modifierListOwner)); 213 break; 214 } 215 return; 216 } 217 if (modifierListOwner instanceof JetClass && !(modifierListOwner instanceof JetEnumEntry)) { 218 JetClass aClass = (JetClass) modifierListOwner; 219 boolean localEnumError = aClass.isLocal() && aClass.isEnum(); 220 if (!localEnumError && isIllegalNestedClass(descriptor)) { 221 trace.report(NESTED_CLASS_NOT_ALLOWED.on(aClass)); 222 } 223 } 224 } 225 226 public static boolean isIllegalInner(@NotNull DeclarationDescriptor descriptor) { 227 return checkIllegalInner(descriptor) != InnerModifierCheckResult.ALLOWED; 228 } 229 230 private enum InnerModifierCheckResult { 231 ALLOWED, 232 ILLEGAL_POSITION, 233 IN_TRAIT, 234 IN_OBJECT, 235 } 236 237 private void checkDefaultModifier(@NotNull JetDeclaration declaration) { 238 if (declaration.hasModifier(COMPANION_KEYWORD) && !isDefaultModifierAllowed(declaration)) { 239 reportIllegalModifiers(declaration, Collections.singletonList(COMPANION_KEYWORD)); 240 } 241 } 242 243 // NOTE: just checks if this is legal context for companion modifier (Companion object descriptor can be created) 244 // COMPANION_OBJECT_NOT_ALLOWED can be reported later 245 public static boolean isDefaultModifierAllowed(@NotNull JetDeclaration declaration) { 246 if (declaration instanceof JetObjectDeclaration) { 247 JetDeclaration containingDeclaration = getContainingDeclaration(declaration); 248 if (containingDeclaration instanceof JetClassOrObject) { 249 return true; 250 } 251 } 252 return false; 253 } 254 255 @NotNull 256 private static InnerModifierCheckResult checkIllegalInner(DeclarationDescriptor descriptor) { 257 if (!(descriptor instanceof ClassDescriptor)) return InnerModifierCheckResult.ILLEGAL_POSITION; 258 ClassDescriptor classDescriptor = (ClassDescriptor) descriptor; 259 260 if (classDescriptor.getKind() != ClassKind.CLASS) return InnerModifierCheckResult.ILLEGAL_POSITION; 261 262 DeclarationDescriptor containingDeclaration = classDescriptor.getContainingDeclaration(); 263 if (!(containingDeclaration instanceof ClassDescriptor)) return InnerModifierCheckResult.ILLEGAL_POSITION; 264 265 if (DescriptorUtils.isTrait(containingDeclaration)) { 266 return InnerModifierCheckResult.IN_TRAIT; 267 } 268 else if (DescriptorUtils.isObject(containingDeclaration)) { 269 return InnerModifierCheckResult.IN_OBJECT; 270 } 271 else { 272 return InnerModifierCheckResult.ALLOWED; 273 } 274 } 275 276 private static boolean isIllegalNestedClass(@NotNull DeclarationDescriptor descriptor) { 277 if (!(descriptor instanceof ClassDescriptor)) return false; 278 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration(); 279 if (!(containingDeclaration instanceof ClassDescriptor)) return false; 280 ClassDescriptor containingClass = (ClassDescriptor) containingDeclaration; 281 return containingClass.isInner() || containingClass.getContainingDeclaration() instanceof FunctionDescriptor; 282 } 283 284 private void checkPlatformNameApplicability(@NotNull DeclarationDescriptor descriptor) { 285 AnnotationDescriptor annotation = descriptor.getAnnotations().findAnnotation(new FqName("kotlin.platform.platformName")); 286 if (annotation == null) return; 287 288 JetAnnotationEntry annotationEntry = trace.get(BindingContext.ANNOTATION_DESCRIPTOR_TO_PSI_ELEMENT, annotation); 289 if (annotationEntry == null) return; 290 291 if (!DescriptorUtils.isTopLevelDeclaration(descriptor) || !(descriptor instanceof FunctionDescriptor)) { 292 trace.report(INAPPLICABLE_ANNOTATION.on(annotationEntry)); 293 } 294 295 Collection<CompileTimeConstant<?>> values = annotation.getAllValueArguments().values(); 296 if (!values.isEmpty()) { 297 CompileTimeConstant<?> name = values.iterator().next(); 298 if (name instanceof StringValue) { 299 String value = ((StringValue) name).getValue(); 300 if (value == null || !Name.isValidIdentifier(value)) { 301 trace.report(ILLEGAL_PLATFORM_NAME.on(annotationEntry, String.valueOf(value))); 302 } 303 } 304 } 305 } 306 307 private void checkCompatibility(@Nullable JetModifierList modifierList, Collection<JetModifierKeywordToken> availableModifiers, Collection<JetModifierKeywordToken>... availableCombinations) { 308 checkIncompatibleModifiers(modifierList, trace, availableModifiers, availableCombinations); 309 } 310 311 private void checkRedundantModifier(@NotNull JetModifierList modifierList, Pair<JetModifierKeywordToken, JetModifierKeywordToken>... redundantBundles) { 312 for (Pair<JetModifierKeywordToken, JetModifierKeywordToken> tokenPair : redundantBundles) { 313 JetModifierKeywordToken redundantModifier = tokenPair.getFirst(); 314 JetModifierKeywordToken sufficientModifier = tokenPair.getSecond(); 315 if (modifierList.hasModifier(redundantModifier) && modifierList.hasModifier(sufficientModifier)) { 316 trace.report(Errors.REDUNDANT_MODIFIER.on(modifierList.getModifierNode(redundantModifier).getPsi(), redundantModifier, sufficientModifier)); 317 } 318 } 319 } 320 321 public void reportIllegalModifiers( 322 @NotNull JetModifierListOwner modifierListOwner, 323 @NotNull Collection<JetModifierKeywordToken> illegalModifiers 324 ) { 325 reportIllegalModifiers(modifierListOwner.getModifierList(), illegalModifiers, trace); 326 } 327 328 @NotNull 329 public static Map<JetModifierKeywordToken, ASTNode> getNodesCorrespondingToModifiers(@NotNull JetModifierList modifierList, @NotNull Collection<JetModifierKeywordToken> possibleModifiers) { 330 Map<JetModifierKeywordToken, ASTNode> nodes = Maps.newHashMap(); 331 for (JetModifierKeywordToken modifier : possibleModifiers) { 332 if (modifierList.hasModifier(modifier)) { 333 nodes.put(modifier, modifierList.getModifierNode(modifier)); 334 } 335 } 336 return nodes; 337 } 338 339 @NotNull 340 public static Modality resolveModalityFromModifiers(@NotNull JetModifierListOwner modifierListOwner, @NotNull Modality defaultModality) { 341 return resolveModalityFromModifiers(modifierListOwner.getModifierList(), defaultModality); 342 } 343 344 public static Modality resolveModalityFromModifiers(@Nullable JetModifierList modifierList, @NotNull Modality defaultModality) { 345 if (modifierList == null) return defaultModality; 346 boolean hasAbstractModifier = modifierList.hasModifier(ABSTRACT_KEYWORD); 347 boolean hasOverrideModifier = modifierList.hasModifier(OVERRIDE_KEYWORD); 348 349 if (modifierList.hasModifier(OPEN_KEYWORD)) { 350 if (hasAbstractModifier || defaultModality == Modality.ABSTRACT) { 351 return Modality.ABSTRACT; 352 } 353 return Modality.OPEN; 354 } 355 if (hasAbstractModifier) { 356 return Modality.ABSTRACT; 357 } 358 boolean hasFinalModifier = modifierList.hasModifier(FINAL_KEYWORD); 359 if (hasOverrideModifier && !hasFinalModifier && !(defaultModality == Modality.ABSTRACT)) { 360 return Modality.OPEN; 361 } 362 if (hasFinalModifier) { 363 return Modality.FINAL; 364 } 365 return defaultModality; 366 } 367 368 @NotNull 369 public static Visibility resolveVisibilityFromModifiers(@NotNull JetModifierListOwner modifierListOwner, @NotNull Visibility defaultVisibility) { 370 return resolveVisibilityFromModifiers(modifierListOwner.getModifierList(), defaultVisibility); 371 } 372 373 public static Visibility resolveVisibilityFromModifiers(@Nullable JetModifierList modifierList, @NotNull Visibility defaultVisibility) { 374 if (modifierList == null) return defaultVisibility; 375 if (modifierList.hasModifier(PRIVATE_KEYWORD)) return Visibilities.PRIVATE; 376 if (modifierList.hasModifier(PUBLIC_KEYWORD)) return Visibilities.PUBLIC; 377 if (modifierList.hasModifier(PROTECTED_KEYWORD)) return Visibilities.PROTECTED; 378 if (modifierList.hasModifier(INTERNAL_KEYWORD)) return Visibilities.INTERNAL; 379 return defaultVisibility; 380 } 381 382 public static boolean isInnerClass(@Nullable JetModifierList modifierList) { 383 return modifierList != null && modifierList.hasModifier(INNER_KEYWORD); 384 } 385 386 @NotNull 387 public static Visibility getDefaultClassVisibility(@NotNull ClassDescriptor descriptor) { 388 if (isEnumEntry(descriptor)) { 389 return Visibilities.PUBLIC; 390 } 391 if (isCompanionObject(descriptor)) { 392 return ((ClassDescriptor) descriptor.getContainingDeclaration()).getVisibility(); 393 } 394 return Visibilities.INTERNAL; 395 } 396 397 private void runAnnotationCheckers(@NotNull JetDeclaration declaration, @NotNull DeclarationDescriptor descriptor) { 398 for (AnnotationChecker checker : additionalCheckerProvider.getAnnotationCheckers()) { 399 checker.check(declaration, descriptor, trace); 400 } 401 } 402 403 public void checkVarianceModifiersOfTypeParameters(@NotNull JetModifierListOwner modifierListOwner) { 404 if (!(modifierListOwner instanceof JetTypeParameterListOwner)) return; 405 List<JetTypeParameter> typeParameters = ((JetTypeParameterListOwner) modifierListOwner).getTypeParameters(); 406 for (JetTypeParameter typeParameter : typeParameters) { 407 JetModifierList modifierList = typeParameter.getModifierList(); 408 checkIncompatibleVarianceModifiers(modifierList, trace); 409 } 410 } 411 }