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