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 }