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