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