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.Lists;
020 import com.google.common.collect.Maps;
021 import com.google.common.collect.Sets;
022 import com.intellij.psi.PsiElement;
023 import org.jetbrains.annotations.NotNull;
024 import org.jetbrains.annotations.Nullable;
025 import org.jetbrains.kotlin.descriptors.*;
026 import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1;
027 import org.jetbrains.kotlin.lexer.JetKeywordToken;
028 import org.jetbrains.kotlin.lexer.JetModifierKeywordToken;
029 import org.jetbrains.kotlin.lexer.JetTokens;
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.JetTokens.*;
036 import static org.jetbrains.kotlin.psi.JetStubbedPsiUtil.getContainingDeclaration;
037
038 public class ModifiersChecker {
039 private static final Set<JetModifierKeywordToken> MODIFIERS_ILLEGAL_ON_PARAMETERS;
040
041 static {
042 MODIFIERS_ILLEGAL_ON_PARAMETERS = Sets.newHashSet();
043 MODIFIERS_ILLEGAL_ON_PARAMETERS.addAll(Arrays.asList(JetTokens.MODIFIER_KEYWORDS_ARRAY));
044 MODIFIERS_ILLEGAL_ON_PARAMETERS.remove(JetTokens.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_TRAIT,
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 JetDeclaration declaration) {
062 if (declaration instanceof JetObjectDeclaration) {
063 JetDeclaration containingDeclaration = getContainingDeclaration(declaration);
064 if (containingDeclaration instanceof JetClassOrObject) {
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.isTrait(containingDeclaration)) {
082 return InnerModifierCheckResult.IN_TRAIT;
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() || containingClass.getContainingDeclaration() instanceof FunctionDescriptor;
098 }
099
100 @NotNull
101 public static Modality resolveModalityFromModifiers(
102 @NotNull JetModifierListOwner modifierListOwner,
103 @NotNull Modality defaultModality
104 ) {
105 return resolveModalityFromModifiers(modifierListOwner.getModifierList(), defaultModality);
106 }
107
108 @NotNull
109 public static Modality resolveModalityFromModifiers(@Nullable JetModifierList modifierList, @NotNull Modality defaultModality) {
110 if (modifierList == null) return defaultModality;
111 boolean hasAbstractModifier = modifierList.hasModifier(ABSTRACT_KEYWORD);
112 boolean hasOverrideModifier = modifierList.hasModifier(OVERRIDE_KEYWORD);
113
114 if (modifierList.hasModifier(SEALED_KEYWORD)) {
115 return Modality.SEALED;
116 }
117 if (modifierList.hasModifier(OPEN_KEYWORD)) {
118 if (hasAbstractModifier || defaultModality == Modality.ABSTRACT) {
119 return Modality.ABSTRACT;
120 }
121 return Modality.OPEN;
122 }
123 if (hasAbstractModifier) {
124 return Modality.ABSTRACT;
125 }
126 boolean hasFinalModifier = modifierList.hasModifier(FINAL_KEYWORD);
127 if (hasOverrideModifier && !hasFinalModifier && !(defaultModality == Modality.ABSTRACT)) {
128 return Modality.OPEN;
129 }
130 if (hasFinalModifier) {
131 return Modality.FINAL;
132 }
133 return defaultModality;
134 }
135
136 @NotNull
137 public static Visibility resolveVisibilityFromModifiers(
138 @NotNull JetModifierListOwner modifierListOwner,
139 @NotNull Visibility defaultVisibility
140 ) {
141 return resolveVisibilityFromModifiers(modifierListOwner.getModifierList(), defaultVisibility);
142 }
143
144 public static Visibility resolveVisibilityFromModifiers(@Nullable JetModifierList modifierList, @NotNull Visibility defaultVisibility) {
145 if (modifierList == null) return defaultVisibility;
146 if (modifierList.hasModifier(PRIVATE_KEYWORD)) return Visibilities.PRIVATE;
147 if (modifierList.hasModifier(PUBLIC_KEYWORD)) return Visibilities.PUBLIC;
148 if (modifierList.hasModifier(PROTECTED_KEYWORD)) return Visibilities.PROTECTED;
149 if (modifierList.hasModifier(INTERNAL_KEYWORD)) return Visibilities.INTERNAL;
150 return defaultVisibility;
151 }
152
153 public static boolean isInnerClass(@Nullable JetModifierList modifierList) {
154 return modifierList != null && modifierList.hasModifier(INNER_KEYWORD);
155 }
156
157 public class ModifiersCheckingProcedure {
158
159 @NotNull
160 private final BindingTrace trace;
161
162 private ModifiersCheckingProcedure(@NotNull BindingTrace trace) {
163 this.trace = trace;
164 }
165
166 public void checkParameterHasNoValOrVar(
167 @NotNull JetParameter parameter,
168 @NotNull DiagnosticFactory1<PsiElement, JetKeywordToken> diagnosticFactory
169 ) {
170 PsiElement valOrVar = parameter.getValOrVarKeyword();
171 if (valOrVar != null) {
172 trace.report(diagnosticFactory.on(valOrVar, ((JetKeywordToken) valOrVar.getNode().getElementType())));
173 }
174 }
175
176 public void checkModifiersForDeclaration(@NotNull JetDeclaration modifierListOwner, @NotNull MemberDescriptor descriptor) {
177 checkNestedClassAllowed(modifierListOwner, descriptor);
178 ModifierCheckerCore.INSTANCE$.check(modifierListOwner, trace, descriptor);
179 checkTypeParametersModifiers(modifierListOwner);
180 AnnotationUseSiteTargetChecker.INSTANCE$.check(modifierListOwner, descriptor, trace);
181 runDeclarationCheckers(modifierListOwner, descriptor);
182 ClassDescriptor classDescriptor = descriptor instanceof ClassDescriptor ? (ClassDescriptor) descriptor : null;
183 annotationChecker.check(modifierListOwner, trace, classDescriptor);
184 }
185
186 public void checkModifiersForLocalDeclaration(
187 @NotNull JetDeclaration modifierListOwner,
188 @NotNull DeclarationDescriptor descriptor
189 ) {
190 AnnotationUseSiteTargetChecker.INSTANCE$.check(modifierListOwner, descriptor, trace);
191 runDeclarationCheckers(modifierListOwner, descriptor);
192 annotationChecker.check(modifierListOwner, trace,
193 descriptor instanceof ClassDescriptor ? (ClassDescriptor) descriptor : null);
194 ModifierCheckerCore.INSTANCE$.check(modifierListOwner, trace, descriptor);
195 }
196
197 private void checkNestedClassAllowed(@NotNull JetModifierListOwner modifierListOwner, @NotNull DeclarationDescriptor descriptor) {
198 if (modifierListOwner.hasModifier(INNER_KEYWORD)) return;
199 if (modifierListOwner instanceof JetClass && !(modifierListOwner instanceof JetEnumEntry)) {
200 JetClass aClass = (JetClass) modifierListOwner;
201 boolean localEnumError = aClass.isLocal() && aClass.isEnum();
202 if (!localEnumError && isIllegalNestedClass(descriptor)) {
203 trace.report(NESTED_CLASS_NOT_ALLOWED.on(aClass));
204 }
205 }
206 }
207
208 @NotNull
209 public Map<JetModifierKeywordToken, PsiElement> getTokensCorrespondingToModifiers(
210 @NotNull JetModifierList modifierList,
211 @NotNull Collection<JetModifierKeywordToken> possibleModifiers
212 ) {
213 Map<JetModifierKeywordToken, PsiElement> tokens = Maps.newHashMap();
214 for (JetModifierKeywordToken modifier : possibleModifiers) {
215 if (modifierList.hasModifier(modifier)) {
216 tokens.put(modifier, modifierList.getModifier(modifier));
217 }
218 }
219 return tokens;
220 }
221
222
223 private void runDeclarationCheckers(@NotNull JetDeclaration declaration, @NotNull DeclarationDescriptor descriptor) {
224 for (DeclarationChecker checker : declarationCheckers) {
225 checker.check(declaration, descriptor, trace, trace.getBindingContext());
226 }
227 }
228
229 public void checkTypeParametersModifiers(@NotNull JetModifierListOwner modifierListOwner) {
230 if (!(modifierListOwner instanceof JetTypeParameterListOwner)) return;
231 List<JetTypeParameter> typeParameters = ((JetTypeParameterListOwner) modifierListOwner).getTypeParameters();
232 for (JetTypeParameter typeParameter : typeParameters) {
233 ModifierCheckerCore.INSTANCE$.check(typeParameter, trace, null);
234 }
235 }
236 }
237
238 @NotNull
239 private final AnnotationChecker annotationChecker;
240
241 @NotNull
242 private final Iterable<DeclarationChecker> declarationCheckers;
243
244 public ModifiersChecker(@NotNull AnnotationChecker annotationChecker, @NotNull Iterable<DeclarationChecker> declarationCheckers) {
245 this.annotationChecker = annotationChecker;
246 this.declarationCheckers = declarationCheckers;
247 }
248
249 @NotNull
250 public ModifiersCheckingProcedure withTrace(@NotNull BindingTrace trace) {
251 return new ModifiersCheckingProcedure(trace);
252 }
253 }