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