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.Errors; 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 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 public static void checkIncompatibleModifiers( 067 @Nullable JetModifierList modifierList, 068 @NotNull BindingTrace trace, 069 @NotNull Collection<JetModifierKeywordToken> availableModifiers, 070 @NotNull Collection<JetModifierKeywordToken>... availableCombinations 071 ) { 072 if (modifierList == null) return; 073 Collection<JetModifierKeywordToken> presentModifiers = Sets.newLinkedHashSet(); 074 for (JetModifierKeywordToken modifier : availableModifiers) { 075 if (modifierList.hasModifier(modifier)) { 076 presentModifiers.add(modifier); 077 } 078 } 079 checkRepeatedModifiers(modifierList, trace, availableModifiers); 080 081 if (presentModifiers.size() == 1) { 082 return; 083 } 084 for (Collection<JetModifierKeywordToken> combination : availableCombinations) { 085 if (presentModifiers.containsAll(combination) && combination.containsAll(presentModifiers)) { 086 return; 087 } 088 } 089 for (JetModifierKeywordToken token : presentModifiers) { 090 trace.report(Errors.INCOMPATIBLE_MODIFIERS.on(modifierList.getModifierNode(token).getPsi(), presentModifiers)); 091 } 092 } 093 094 private static void checkRepeatedModifiers( 095 @NotNull JetModifierList modifierList, 096 @NotNull BindingTrace trace, 097 @NotNull Collection<JetModifierKeywordToken> availableModifiers 098 ) { 099 for (JetModifierKeywordToken token : availableModifiers) { 100 if (!modifierList.hasModifier(token)) continue; 101 102 List<ASTNode> nodesOfRepeatedTokens = Lists.newArrayList(); 103 ASTNode node = modifierList.getNode().getFirstChildNode(); 104 while (node != null) { 105 if (node.getElementType() == token) { 106 nodesOfRepeatedTokens.add(node); 107 } 108 node = node.getTreeNext(); 109 } 110 if (nodesOfRepeatedTokens.size() > 1) { 111 for (ASTNode repeatedToken : nodesOfRepeatedTokens) { 112 trace.report(REPEATED_MODIFIER.on(repeatedToken.getPsi(), token)); 113 } 114 } 115 } 116 } 117 118 public static void checkIncompatibleVarianceModifiers(@Nullable JetModifierList modifierList, @NotNull BindingTrace trace) { 119 checkIncompatibleModifiers(modifierList, trace, Arrays.asList(JetTokens.IN_KEYWORD, JetTokens.OUT_KEYWORD)); 120 } 121 122 @NotNull 123 private final BindingTrace trace; 124 @NotNull 125 private final AdditionalCheckerProvider additionalCheckerProvider; 126 127 public ModifiersChecker(@NotNull BindingTrace trace, @NotNull AdditionalCheckerProvider additionalCheckerProvider) { 128 this.trace = trace; 129 this.additionalCheckerProvider = additionalCheckerProvider; 130 } 131 132 public static ModifiersChecker create(@NotNull BindingTrace trace, @NotNull AdditionalCheckerProvider provider) { 133 return new ModifiersChecker(trace, provider); 134 } 135 136 public void checkModifiersForDeclaration(@NotNull JetDeclaration modifierListOwner, @NotNull MemberDescriptor descriptor) { 137 if (modifierListOwner instanceof JetEnumEntry) { 138 checkIllegalInThisContextModifiers(modifierListOwner, Arrays.asList(MODIFIER_KEYWORDS_ARRAY)); 139 } 140 else { 141 checkInnerModifier(modifierListOwner, descriptor); 142 checkModalityModifiers(modifierListOwner); 143 checkVisibilityModifiers(modifierListOwner, descriptor); 144 checkVarianceModifiersOfTypeParameters(modifierListOwner); 145 } 146 checkPlatformNameApplicability(descriptor); 147 runAnnotationCheckers(modifierListOwner, descriptor); 148 } 149 150 public void checkModifiersForLocalDeclaration(@NotNull JetDeclaration modifierListOwner, @NotNull DeclarationDescriptor descriptor) { 151 checkIllegalModalityModifiers(modifierListOwner); 152 checkIllegalVisibilityModifiers(modifierListOwner); 153 checkPlatformNameApplicability(descriptor); 154 runAnnotationCheckers(modifierListOwner, descriptor); 155 } 156 157 public void checkIllegalModalityModifiers(@NotNull JetModifierListOwner modifierListOwner) { 158 checkIllegalInThisContextModifiers(modifierListOwner, MODALITY_MODIFIERS); 159 } 160 161 public void checkIllegalVisibilityModifiers(@NotNull JetModifierListOwner modifierListOwner) { 162 checkIllegalInThisContextModifiers(modifierListOwner, VISIBILITY_MODIFIERS); 163 } 164 165 private void checkModalityModifiers(@NotNull JetModifierListOwner modifierListOwner) { 166 JetModifierList modifierList = modifierListOwner.getModifierList(); 167 if (modifierList == null) return; 168 169 checkRedundantModifier(modifierList, Pair.create(OPEN_KEYWORD, ABSTRACT_KEYWORD), Pair.create(OPEN_KEYWORD, OVERRIDE_KEYWORD)); 170 171 checkCompatibility(modifierList, Arrays.asList(ABSTRACT_KEYWORD, OPEN_KEYWORD, FINAL_KEYWORD), 172 Arrays.asList(ABSTRACT_KEYWORD, OPEN_KEYWORD)); 173 174 if (modifierListOwner.getParent() instanceof JetClassObject || modifierListOwner instanceof JetObjectDeclaration) { 175 checkIllegalModalityModifiers(modifierListOwner); 176 } 177 else if (modifierListOwner instanceof JetClassOrObject) { 178 checkIllegalInThisContextModifiers(modifierListOwner, Collections.singletonList(OVERRIDE_KEYWORD)); 179 } 180 } 181 182 private void checkVisibilityModifiers(@NotNull JetModifierListOwner modifierListOwner, @NotNull DeclarationDescriptor descriptor) { 183 JetModifierList modifierList = modifierListOwner.getModifierList(); 184 if (modifierList == null) return; 185 186 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration(); 187 if (containingDeclaration instanceof PackageFragmentDescriptor) { 188 if (modifierList.hasModifier(PROTECTED_KEYWORD)) { 189 trace.report(Errors.PACKAGE_MEMBER_CANNOT_BE_PROTECTED.on(modifierListOwner)); 190 } 191 } 192 193 checkCompatibility(modifierList, VISIBILITY_MODIFIERS); 194 } 195 196 private void checkInnerModifier(@NotNull JetModifierListOwner modifierListOwner, @NotNull DeclarationDescriptor descriptor) { 197 if (modifierListOwner.hasModifier(INNER_KEYWORD)) { 198 if (isIllegalInner(descriptor)) { 199 checkIllegalInThisContextModifiers(modifierListOwner, Collections.singletonList(INNER_KEYWORD)); 200 } 201 return; 202 } 203 if (modifierListOwner instanceof JetClass && !(modifierListOwner instanceof JetEnumEntry)) { 204 JetClass aClass = (JetClass) modifierListOwner; 205 boolean localEnumError = aClass.isLocal() && aClass.isEnum(); 206 if (!localEnumError && isIllegalNestedClass(descriptor)) { 207 trace.report(NESTED_CLASS_NOT_ALLOWED.on(aClass)); 208 } 209 } 210 } 211 212 public static boolean isIllegalInner(@NotNull DeclarationDescriptor descriptor) { 213 if (!(descriptor instanceof ClassDescriptor)) return true; 214 ClassDescriptor classDescriptor = (ClassDescriptor) descriptor; 215 if (classDescriptor.getKind() != ClassKind.CLASS) return true; 216 DeclarationDescriptor containingDeclaration = classDescriptor.getContainingDeclaration(); 217 if (!(containingDeclaration instanceof ClassDescriptor)) return true; 218 return ((ClassDescriptor) containingDeclaration).getKind() == ClassKind.TRAIT; 219 } 220 221 private static boolean isIllegalNestedClass(@NotNull DeclarationDescriptor descriptor) { 222 if (!(descriptor instanceof ClassDescriptor)) return false; 223 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration(); 224 if (!(containingDeclaration instanceof ClassDescriptor)) return false; 225 ClassDescriptor containingClass = (ClassDescriptor) containingDeclaration; 226 return containingClass.isInner() || containingClass.getContainingDeclaration() instanceof FunctionDescriptor; 227 } 228 229 private void checkPlatformNameApplicability(@NotNull DeclarationDescriptor descriptor) { 230 AnnotationDescriptor annotation = descriptor.getAnnotations().findAnnotation(new FqName("kotlin.platform.platformName")); 231 if (annotation == null) return; 232 233 JetAnnotationEntry annotationEntry = trace.get(BindingContext.ANNOTATION_DESCRIPTOR_TO_PSI_ELEMENT, annotation); 234 if (annotationEntry == null) return; 235 236 if (!DescriptorUtils.isTopLevelDeclaration(descriptor) || !(descriptor instanceof FunctionDescriptor)) { 237 trace.report(INAPPLICABLE_ANNOTATION.on(annotationEntry)); 238 } 239 240 Collection<CompileTimeConstant<?>> values = annotation.getAllValueArguments().values(); 241 if (!values.isEmpty()) { 242 CompileTimeConstant<?> name = values.iterator().next(); 243 if (name instanceof StringValue) { 244 String value = ((StringValue) name).getValue(); 245 if (value == null || !Name.isValidIdentifier(value)) { 246 trace.report(ILLEGAL_PLATFORM_NAME.on(annotationEntry, String.valueOf(value))); 247 } 248 } 249 } 250 } 251 252 private void checkCompatibility(@Nullable JetModifierList modifierList, Collection<JetModifierKeywordToken> availableModifiers, Collection<JetModifierKeywordToken>... availableCombinations) { 253 checkIncompatibleModifiers(modifierList, trace, availableModifiers, availableCombinations); 254 } 255 256 private void checkRedundantModifier(@NotNull JetModifierList modifierList, Pair<JetModifierKeywordToken, JetModifierKeywordToken>... redundantBundles) { 257 for (Pair<JetModifierKeywordToken, JetModifierKeywordToken> tokenPair : redundantBundles) { 258 JetModifierKeywordToken redundantModifier = tokenPair.getFirst(); 259 JetModifierKeywordToken sufficientModifier = tokenPair.getSecond(); 260 if (modifierList.hasModifier(redundantModifier) && modifierList.hasModifier(sufficientModifier)) { 261 trace.report(Errors.REDUNDANT_MODIFIER.on(modifierList.getModifierNode(redundantModifier).getPsi(), redundantModifier, sufficientModifier)); 262 } 263 } 264 } 265 266 public void checkIllegalInThisContextModifiers( 267 @NotNull JetModifierListOwner modifierListOwner, 268 @NotNull Collection<JetModifierKeywordToken> illegalModifiers 269 ) { 270 reportIllegalModifiers(modifierListOwner.getModifierList(), illegalModifiers, trace); 271 } 272 273 @NotNull 274 public static Map<JetModifierKeywordToken, ASTNode> getNodesCorrespondingToModifiers(@NotNull JetModifierList modifierList, @NotNull Collection<JetModifierKeywordToken> possibleModifiers) { 275 Map<JetModifierKeywordToken, ASTNode> nodes = Maps.newHashMap(); 276 for (JetModifierKeywordToken modifier : possibleModifiers) { 277 if (modifierList.hasModifier(modifier)) { 278 nodes.put(modifier, modifierList.getModifierNode(modifier)); 279 } 280 } 281 return nodes; 282 } 283 284 @NotNull 285 public static Modality resolveModalityFromModifiers(@NotNull JetModifierListOwner modifierListOwner, @NotNull Modality defaultModality) { 286 return resolveModalityFromModifiers(modifierListOwner.getModifierList(), defaultModality); 287 } 288 289 public static Modality resolveModalityFromModifiers(@Nullable JetModifierList modifierList, @NotNull Modality defaultModality) { 290 if (modifierList == null) return defaultModality; 291 boolean hasAbstractModifier = modifierList.hasModifier(ABSTRACT_KEYWORD); 292 boolean hasOverrideModifier = modifierList.hasModifier(OVERRIDE_KEYWORD); 293 294 if (modifierList.hasModifier(OPEN_KEYWORD)) { 295 if (hasAbstractModifier || defaultModality == Modality.ABSTRACT) { 296 return Modality.ABSTRACT; 297 } 298 return Modality.OPEN; 299 } 300 if (hasAbstractModifier) { 301 return Modality.ABSTRACT; 302 } 303 boolean hasFinalModifier = modifierList.hasModifier(FINAL_KEYWORD); 304 if (hasOverrideModifier && !hasFinalModifier && !(defaultModality == Modality.ABSTRACT)) { 305 return Modality.OPEN; 306 } 307 if (hasFinalModifier) { 308 return Modality.FINAL; 309 } 310 return defaultModality; 311 } 312 313 @NotNull 314 public static Visibility resolveVisibilityFromModifiers(@NotNull JetModifierListOwner modifierListOwner, @NotNull Visibility defaultVisibility) { 315 return resolveVisibilityFromModifiers(modifierListOwner.getModifierList(), defaultVisibility); 316 } 317 318 public static Visibility resolveVisibilityFromModifiers(@Nullable JetModifierList modifierList, @NotNull Visibility defaultVisibility) { 319 if (modifierList == null) return defaultVisibility; 320 if (modifierList.hasModifier(PRIVATE_KEYWORD)) return Visibilities.PRIVATE; 321 if (modifierList.hasModifier(PUBLIC_KEYWORD)) return Visibilities.PUBLIC; 322 if (modifierList.hasModifier(PROTECTED_KEYWORD)) return Visibilities.PROTECTED; 323 if (modifierList.hasModifier(INTERNAL_KEYWORD)) return Visibilities.INTERNAL; 324 return defaultVisibility; 325 } 326 327 public static boolean isInnerClass(@Nullable JetModifierList modifierList) { 328 return modifierList != null && modifierList.hasModifier(INNER_KEYWORD); 329 } 330 331 @NotNull 332 public static Visibility getDefaultClassVisibility(@NotNull ClassDescriptor descriptor) { 333 ClassKind kind = descriptor.getKind(); 334 if (kind == ClassKind.ENUM_ENTRY) { 335 return Visibilities.PUBLIC; 336 } 337 if (kind == ClassKind.CLASS_OBJECT) { 338 return ((ClassDescriptor) descriptor.getContainingDeclaration()).getVisibility(); 339 } 340 return Visibilities.INTERNAL; 341 } 342 343 private void runAnnotationCheckers(@NotNull JetDeclaration declaration, @NotNull DeclarationDescriptor descriptor) { 344 for (AnnotationChecker checker : additionalCheckerProvider.getAnnotationCheckers()) { 345 checker.check(declaration, descriptor, trace); 346 } 347 } 348 349 public void checkVarianceModifiersOfTypeParameters(@NotNull JetModifierListOwner modifierListOwner) { 350 if (!(modifierListOwner instanceof JetTypeParameterListOwner)) return; 351 List<JetTypeParameter> typeParameters = ((JetTypeParameterListOwner) modifierListOwner).getTypeParameters(); 352 for (JetTypeParameter typeParameter : typeParameters) { 353 JetModifierList modifierList = typeParameter.getModifierList(); 354 checkIncompatibleVarianceModifiers(modifierList, trace); 355 } 356 } 357 }