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.Maps; 020 import com.google.common.collect.Sets; 021 import com.intellij.psi.PsiElement; 022 import org.jetbrains.annotations.NotNull; 023 import org.jetbrains.annotations.Nullable; 024 import org.jetbrains.kotlin.descriptors.*; 025 import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1; 026 import org.jetbrains.kotlin.lexer.KtKeywordToken; 027 import org.jetbrains.kotlin.lexer.KtModifierKeywordToken; 028 import org.jetbrains.kotlin.lexer.KtTokens; 029 import org.jetbrains.kotlin.psi.*; 030 031 import java.util.*; 032 033 import static org.jetbrains.kotlin.diagnostics.Errors.NESTED_CLASS_NOT_ALLOWED; 034 import static org.jetbrains.kotlin.lexer.KtTokens.*; 035 import static org.jetbrains.kotlin.psi.KtStubbedPsiUtil.getContainingDeclaration; 036 037 public class ModifiersChecker { 038 private static final Set<KtModifierKeywordToken> MODIFIERS_ILLEGAL_ON_PARAMETERS; 039 040 static { 041 MODIFIERS_ILLEGAL_ON_PARAMETERS = Sets.newHashSet(); 042 MODIFIERS_ILLEGAL_ON_PARAMETERS.addAll(Arrays.asList(KtTokens.MODIFIER_KEYWORDS_ARRAY)); 043 MODIFIERS_ILLEGAL_ON_PARAMETERS.remove(KtTokens.VARARG_KEYWORD); 044 } 045 046 public static boolean isIllegalInner(@NotNull DeclarationDescriptor descriptor) { 047 return checkIllegalInner(descriptor) != InnerModifierCheckResult.ALLOWED; 048 } 049 050 private enum InnerModifierCheckResult { 051 ALLOWED, 052 ILLEGAL_POSITION, 053 IN_INTERFACE, 054 IN_OBJECT, 055 } 056 057 058 // NOTE: just checks if this is legal context for companion modifier (Companion object descriptor can be created) 059 // COMPANION_OBJECT_NOT_ALLOWED can be reported later 060 public static boolean isCompanionModifierAllowed(@NotNull KtDeclaration declaration) { 061 if (declaration instanceof KtObjectDeclaration) { 062 KtDeclaration containingDeclaration = getContainingDeclaration(declaration); 063 if (containingDeclaration instanceof KtClassOrObject) { 064 return true; 065 } 066 } 067 return false; 068 } 069 070 @NotNull 071 private static InnerModifierCheckResult checkIllegalInner(@NotNull DeclarationDescriptor descriptor) { 072 if (!(descriptor instanceof ClassDescriptor)) return InnerModifierCheckResult.ILLEGAL_POSITION; 073 ClassDescriptor classDescriptor = (ClassDescriptor) descriptor; 074 075 if (classDescriptor.getKind() != ClassKind.CLASS) return InnerModifierCheckResult.ILLEGAL_POSITION; 076 077 DeclarationDescriptor containingDeclaration = classDescriptor.getContainingDeclaration(); 078 if (!(containingDeclaration instanceof ClassDescriptor)) return InnerModifierCheckResult.ILLEGAL_POSITION; 079 080 if (DescriptorUtils.isInterface(containingDeclaration)) { 081 return InnerModifierCheckResult.IN_INTERFACE; 082 } 083 else if (DescriptorUtils.isObject(containingDeclaration)) { 084 return InnerModifierCheckResult.IN_OBJECT; 085 } 086 else { 087 return InnerModifierCheckResult.ALLOWED; 088 } 089 } 090 091 private static boolean isIllegalNestedClass(@NotNull DeclarationDescriptor descriptor) { 092 if (!(descriptor instanceof ClassDescriptor)) return false; 093 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration(); 094 if (!(containingDeclaration instanceof ClassDescriptor)) return false; 095 ClassDescriptor containingClass = (ClassDescriptor) containingDeclaration; 096 return containingClass.isInner() || DescriptorUtils.isLocal(containingClass); 097 } 098 099 @NotNull 100 public static Modality resolveMemberModalityFromModifiers( 101 @NotNull KtModifierListOwner modifierListOwner, 102 @NotNull Modality defaultModality 103 ) { 104 return resolveModalityFromModifiers(modifierListOwner.getModifierList(), defaultModality, /* allowSealed = */ false); 105 } 106 107 @NotNull 108 public static Modality resolveModalityFromModifiers( 109 @Nullable KtModifierList modifierList, @NotNull Modality defaultModality, boolean allowSealed 110 ) { 111 if (modifierList == null) return defaultModality; 112 boolean hasAbstractModifier = modifierList.hasModifier(ABSTRACT_KEYWORD); 113 boolean hasOverrideModifier = modifierList.hasModifier(OVERRIDE_KEYWORD); 114 115 if (allowSealed && modifierList.hasModifier(SEALED_KEYWORD)) { 116 return Modality.SEALED; 117 } 118 if (modifierList.hasModifier(OPEN_KEYWORD)) { 119 if (hasAbstractModifier || defaultModality == Modality.ABSTRACT) { 120 return Modality.ABSTRACT; 121 } 122 return Modality.OPEN; 123 } 124 if (hasAbstractModifier) { 125 return Modality.ABSTRACT; 126 } 127 boolean hasFinalModifier = modifierList.hasModifier(FINAL_KEYWORD); 128 if (hasOverrideModifier && !hasFinalModifier && !(defaultModality == Modality.ABSTRACT)) { 129 return Modality.OPEN; 130 } 131 if (hasFinalModifier) { 132 return Modality.FINAL; 133 } 134 return defaultModality; 135 } 136 137 @NotNull 138 public static Visibility resolveVisibilityFromModifiers( 139 @NotNull KtModifierListOwner modifierListOwner, 140 @NotNull Visibility defaultVisibility 141 ) { 142 return resolveVisibilityFromModifiers(modifierListOwner.getModifierList(), defaultVisibility); 143 } 144 145 public static Visibility resolveVisibilityFromModifiers(@Nullable KtModifierList modifierList, @NotNull Visibility defaultVisibility) { 146 if (modifierList == null) return defaultVisibility; 147 if (modifierList.hasModifier(PRIVATE_KEYWORD)) return Visibilities.PRIVATE; 148 if (modifierList.hasModifier(PUBLIC_KEYWORD)) return Visibilities.PUBLIC; 149 if (modifierList.hasModifier(PROTECTED_KEYWORD)) return Visibilities.PROTECTED; 150 if (modifierList.hasModifier(INTERNAL_KEYWORD)) return Visibilities.INTERNAL; 151 return defaultVisibility; 152 } 153 154 public static boolean isInnerClass(@Nullable KtModifierList modifierList) { 155 return modifierList != null && modifierList.hasModifier(INNER_KEYWORD); 156 } 157 158 public class ModifiersCheckingProcedure { 159 160 @NotNull 161 private final BindingTrace trace; 162 163 private ModifiersCheckingProcedure(@NotNull BindingTrace trace) { 164 this.trace = trace; 165 } 166 167 public void checkParameterHasNoValOrVar( 168 @NotNull KtValVarKeywordOwner parameter, 169 @NotNull DiagnosticFactory1<PsiElement, KtKeywordToken> diagnosticFactory 170 ) { 171 PsiElement valOrVar = parameter.getValOrVarKeyword(); 172 if (valOrVar != null) { 173 trace.report(diagnosticFactory.on(valOrVar, ((KtKeywordToken) valOrVar.getNode().getElementType()))); 174 } 175 } 176 177 public void checkModifiersForDeclaration(@NotNull KtDeclaration modifierListOwner, @NotNull MemberDescriptor descriptor) { 178 checkNestedClassAllowed(modifierListOwner, descriptor); 179 checkTypeParametersModifiers(modifierListOwner); 180 checkModifierListCommon(modifierListOwner, descriptor); 181 } 182 183 private void checkModifierListCommon(@NotNull KtDeclaration modifierListOwner, @NotNull DeclarationDescriptor descriptor) { 184 AnnotationUseSiteTargetChecker.INSTANCE.check(modifierListOwner, descriptor, trace); 185 runDeclarationCheckers(modifierListOwner, descriptor); 186 annotationChecker.check(modifierListOwner, trace, descriptor); 187 ModifierCheckerCore.INSTANCE.check(modifierListOwner, trace, descriptor); 188 } 189 190 public void checkModifiersForLocalDeclaration( 191 @NotNull KtDeclaration modifierListOwner, 192 @NotNull DeclarationDescriptor descriptor 193 ) { 194 checkModifierListCommon(modifierListOwner, descriptor); 195 } 196 197 public void checkModifiersForDestructuringDeclaration(@NotNull KtDestructuringDeclaration multiDeclaration) { 198 annotationChecker.check(multiDeclaration, trace, null); 199 ModifierCheckerCore.INSTANCE.check(multiDeclaration, trace, null); 200 for (KtDestructuringDeclarationEntry multiEntry: multiDeclaration.getEntries()) { 201 annotationChecker.check(multiEntry, trace, null); 202 ModifierCheckerCore.INSTANCE.check(multiEntry, trace, null); 203 UnderscoreChecker.INSTANCE.checkNamed(multiEntry, trace); 204 } 205 } 206 207 private void checkNestedClassAllowed(@NotNull KtModifierListOwner modifierListOwner, @NotNull DeclarationDescriptor descriptor) { 208 if (modifierListOwner.hasModifier(INNER_KEYWORD)) return; 209 if (modifierListOwner instanceof KtClass && !(modifierListOwner instanceof KtEnumEntry)) { 210 KtClass aClass = (KtClass) modifierListOwner; 211 boolean localEnumError = aClass.isLocal() && aClass.isEnum(); 212 if (!localEnumError && isIllegalNestedClass(descriptor)) { 213 trace.report(NESTED_CLASS_NOT_ALLOWED.on(aClass)); 214 } 215 } 216 } 217 218 @NotNull 219 public Map<KtModifierKeywordToken, PsiElement> getTokensCorrespondingToModifiers( 220 @NotNull KtModifierList modifierList, 221 @NotNull Collection<KtModifierKeywordToken> possibleModifiers 222 ) { 223 Map<KtModifierKeywordToken, PsiElement> tokens = Maps.newHashMap(); 224 for (KtModifierKeywordToken modifier : possibleModifiers) { 225 if (modifierList.hasModifier(modifier)) { 226 tokens.put(modifier, modifierList.getModifier(modifier)); 227 } 228 } 229 return tokens; 230 } 231 232 233 public void runDeclarationCheckers( 234 @NotNull KtDeclaration declaration, 235 @NotNull DeclarationDescriptor descriptor 236 ) { 237 for (DeclarationChecker checker : declarationCheckers) { 238 checker.check(declaration, descriptor, trace, trace.getBindingContext()); 239 } 240 } 241 242 public void checkTypeParametersModifiers(@NotNull KtModifierListOwner modifierListOwner) { 243 if (!(modifierListOwner instanceof KtTypeParameterListOwner)) return; 244 List<KtTypeParameter> typeParameters = ((KtTypeParameterListOwner) modifierListOwner).getTypeParameters(); 245 for (KtTypeParameter typeParameter : typeParameters) { 246 ModifierCheckerCore.INSTANCE.check(typeParameter, trace, null); 247 } 248 } 249 } 250 251 @NotNull 252 private final AnnotationChecker annotationChecker; 253 254 @NotNull 255 private final Iterable<DeclarationChecker> declarationCheckers; 256 257 public ModifiersChecker(@NotNull AnnotationChecker annotationChecker, @NotNull Iterable<DeclarationChecker> declarationCheckers) { 258 this.annotationChecker = annotationChecker; 259 this.declarationCheckers = declarationCheckers; 260 } 261 262 @NotNull 263 public ModifiersCheckingProcedure withTrace(@NotNull BindingTrace trace) { 264 return new ModifiersCheckingProcedure(trace); 265 } 266 }