001 /*
002 * Copyright 2010-2014 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.jet.lang.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.lang.ASTNode;
023 import com.intellij.openapi.util.Pair;
024 import com.intellij.psi.PsiElement;
025 import org.jetbrains.annotations.NotNull;
026 import org.jetbrains.annotations.Nullable;
027 import org.jetbrains.jet.lang.descriptors.*;
028 import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
029 import org.jetbrains.jet.lang.diagnostics.Errors;
030 import org.jetbrains.jet.lang.psi.*;
031 import org.jetbrains.jet.lang.resolve.constants.*;
032 import org.jetbrains.jet.lang.resolve.name.FqName;
033 import org.jetbrains.jet.lang.resolve.name.Name;
034 import org.jetbrains.jet.lexer.JetModifierKeywordToken;
035
036 import java.util.Collection;
037 import java.util.Collections;
038 import java.util.Map;
039
040 import static org.jetbrains.jet.lang.diagnostics.Errors.ILLEGAL_MODIFIER;
041 import static org.jetbrains.jet.lang.diagnostics.Errors.ILLEGAL_PLATFORM_NAME;
042 import static org.jetbrains.jet.lang.diagnostics.Errors.INAPPLICABLE_ANNOTATION;
043 import static org.jetbrains.jet.lexer.JetTokens.*;
044
045 public class ModifiersChecker {
046 private static final Collection<JetModifierKeywordToken> MODALITY_MODIFIERS =
047 Lists.newArrayList(ABSTRACT_KEYWORD, OPEN_KEYWORD, FINAL_KEYWORD, OVERRIDE_KEYWORD);
048
049 private static final Collection<JetModifierKeywordToken> VISIBILITY_MODIFIERS =
050 Lists.newArrayList(PRIVATE_KEYWORD, PROTECTED_KEYWORD, PUBLIC_KEYWORD, INTERNAL_KEYWORD);
051
052 public static void reportIllegalModifiers(
053 @Nullable JetModifierList modifierList,
054 @NotNull Collection<JetModifierKeywordToken> illegalModifiers,
055 @NotNull BindingTrace trace
056 ) {
057 if (modifierList == null) return;
058
059 for (JetModifierKeywordToken modifierToken : illegalModifiers) {
060 if (modifierList.hasModifier(modifierToken)) {
061 PsiElement modifierPsi = modifierList.getModifier(modifierToken);
062 assert modifierPsi != null;
063 trace.report(ILLEGAL_MODIFIER.on(modifierPsi, modifierToken));
064 }
065 }
066 }
067
068 @NotNull
069 private final BindingTrace trace;
070
071 public ModifiersChecker(@NotNull BindingTrace trace) {
072 this.trace = trace;
073 }
074
075 public static ModifiersChecker create(@NotNull BindingTrace trace) {
076 return new ModifiersChecker(trace);
077 }
078
079 public void checkModifiersForDeclaration(@NotNull JetModifierListOwner modifierListOwner, @NotNull DeclarationDescriptor descriptor) {
080 JetModifierList modifierList = modifierListOwner.getModifierList();
081 checkModalityModifiers(modifierList);
082 checkVisibilityModifiers(modifierList, descriptor);
083 checkInnerModifier(modifierListOwner, descriptor);
084 checkPlatformNameApplicability(descriptor);
085 }
086
087 public void checkModifiersForLocalDeclaration(@NotNull JetModifierListOwner modifierListOwner, @NotNull DeclarationDescriptor descriptor) {
088 checkIllegalModalityModifiers(modifierListOwner);
089 checkIllegalVisibilityModifiers(modifierListOwner);
090 checkPlatformNameApplicability(descriptor);
091 }
092
093 public void checkIllegalModalityModifiers(@NotNull JetModifierListOwner modifierListOwner) {
094 checkIllegalInThisContextModifiers(modifierListOwner.getModifierList(), MODALITY_MODIFIERS);
095 }
096
097 public void checkIllegalVisibilityModifiers(@NotNull JetModifierListOwner modifierListOwner) {
098 checkIllegalInThisContextModifiers(modifierListOwner.getModifierList(), VISIBILITY_MODIFIERS);
099 }
100
101 private void checkModalityModifiers(@Nullable JetModifierList modifierList) {
102 if (modifierList == null) return;
103 checkRedundantModifier(modifierList, Pair.create(OPEN_KEYWORD, ABSTRACT_KEYWORD), Pair.create(OPEN_KEYWORD, OVERRIDE_KEYWORD));
104
105 checkCompatibility(modifierList, Lists.newArrayList(ABSTRACT_KEYWORD, OPEN_KEYWORD, FINAL_KEYWORD),
106 Lists.newArrayList(ABSTRACT_KEYWORD, OPEN_KEYWORD));
107 }
108
109 private void checkVisibilityModifiers(@Nullable JetModifierList modifierList, @NotNull DeclarationDescriptor descriptor) {
110 if (modifierList == null) return;
111
112 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
113 if (containingDeclaration instanceof PackageFragmentDescriptor) {
114 if (modifierList.hasModifier(PROTECTED_KEYWORD)) {
115 trace.report(Errors.PACKAGE_MEMBER_CANNOT_BE_PROTECTED.on(modifierList.getModifierNode(PROTECTED_KEYWORD).getPsi()));
116 }
117 }
118
119 checkCompatibility(modifierList, VISIBILITY_MODIFIERS);
120 }
121
122 private void checkInnerModifier(@NotNull JetModifierListOwner modifierListOwner, @NotNull DeclarationDescriptor descriptor) {
123 JetModifierList modifierList = modifierListOwner.getModifierList();
124
125 if (modifierList != null && modifierList.hasModifier(INNER_KEYWORD)) {
126 if (isIllegalInner(descriptor)) {
127 checkIllegalInThisContextModifiers(modifierList, Collections.singletonList(INNER_KEYWORD));
128 }
129 }
130 else {
131 if (modifierListOwner instanceof JetClass && !(modifierListOwner instanceof JetEnumEntry) && isIllegalNestedClass(descriptor)) {
132 PsiElement name = ((JetClass) modifierListOwner).getNameIdentifier();
133 if (name != null) {
134 trace.report(Errors.NESTED_CLASS_NOT_ALLOWED.on(name));
135 }
136 }
137 }
138 }
139
140 private static boolean isIllegalInner(@NotNull DeclarationDescriptor descriptor) {
141 if (!(descriptor instanceof ClassDescriptor)) return true;
142 ClassDescriptor classDescriptor = (ClassDescriptor) descriptor;
143 if (classDescriptor.getKind() != ClassKind.CLASS) return true;
144 DeclarationDescriptor containingDeclaration = classDescriptor.getContainingDeclaration();
145 if (!(containingDeclaration instanceof ClassDescriptor)) return true;
146 return ((ClassDescriptor) containingDeclaration).getKind() == ClassKind.TRAIT;
147 }
148
149 private static boolean isIllegalNestedClass(@NotNull DeclarationDescriptor descriptor) {
150 if (!(descriptor instanceof ClassDescriptor)) return false;
151 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
152 if (!(containingDeclaration instanceof ClassDescriptor)) return false;
153 ClassDescriptor containingClass = (ClassDescriptor) containingDeclaration;
154 return containingClass.isInner() || containingClass.getContainingDeclaration() instanceof FunctionDescriptor;
155 }
156
157 private void checkPlatformNameApplicability(@NotNull DeclarationDescriptor descriptor) {
158 if (descriptor instanceof PropertyDescriptor) {
159 PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
160 if (propertyDescriptor.getGetter() != null) {
161 checkPlatformNameApplicability(propertyDescriptor.getGetter());
162 }
163 if (propertyDescriptor.getSetter() != null) {
164 checkPlatformNameApplicability(propertyDescriptor.getSetter());
165 }
166 }
167
168 AnnotationDescriptor annotation = descriptor.getAnnotations().findAnnotation(new FqName("kotlin.platform.platformName"));
169 if (annotation == null) return;
170
171 JetAnnotationEntry annotationEntry = trace.get(BindingContext.ANNOTATION_DESCRIPTOR_TO_PSI_ELEMENT, annotation);
172 if (annotationEntry == null) return;
173
174 if (!DescriptorUtils.isTopLevelDeclaration(descriptor) || !(descriptor instanceof FunctionDescriptor)) {
175 trace.report(INAPPLICABLE_ANNOTATION.on(annotationEntry));
176 }
177
178 Collection<CompileTimeConstant<?>> values = annotation.getAllValueArguments().values();
179 if (!values.isEmpty()) {
180 CompileTimeConstant<?> name = values.iterator().next();
181 if (name instanceof StringValue) {
182 String value = ((StringValue) name).getValue();
183 if (value == null || !Name.isValidIdentifier(value)) {
184 trace.report(ILLEGAL_PLATFORM_NAME.on(annotationEntry, String.valueOf(value)));
185 }
186 }
187 }
188 }
189
190 private void checkCompatibility(@Nullable JetModifierList modifierList, Collection<JetModifierKeywordToken> availableModifiers, Collection<JetModifierKeywordToken>... availableCombinations) {
191 if (modifierList == null) return;
192 Collection<JetModifierKeywordToken> presentModifiers = Sets.newLinkedHashSet();
193 for (JetModifierKeywordToken modifier : availableModifiers) {
194 if (modifierList.hasModifier(modifier)) {
195 presentModifiers.add(modifier);
196 }
197 }
198 if (presentModifiers.size() == 1) {
199 return;
200 }
201 for (Collection<JetModifierKeywordToken> combination : availableCombinations) {
202 if (presentModifiers.containsAll(combination) && combination.containsAll(presentModifiers)) {
203 return;
204 }
205 }
206 for (JetModifierKeywordToken token : presentModifiers) {
207 trace.report(Errors.INCOMPATIBLE_MODIFIERS.on(modifierList.getModifierNode(token).getPsi(), presentModifiers));
208 }
209 }
210
211 private void checkRedundantModifier(@NotNull JetModifierList modifierList, Pair<JetModifierKeywordToken, JetModifierKeywordToken>... redundantBundles) {
212 for (Pair<JetModifierKeywordToken, JetModifierKeywordToken> tokenPair : redundantBundles) {
213 JetModifierKeywordToken redundantModifier = tokenPair.getFirst();
214 JetModifierKeywordToken sufficientModifier = tokenPair.getSecond();
215 if (modifierList.hasModifier(redundantModifier) && modifierList.hasModifier(sufficientModifier)) {
216 trace.report(Errors.REDUNDANT_MODIFIER.on(modifierList.getModifierNode(redundantModifier).getPsi(), redundantModifier, sufficientModifier));
217 }
218 }
219 }
220
221 public void checkIllegalInThisContextModifiers(
222 @Nullable JetModifierList modifierList,
223 @NotNull Collection<JetModifierKeywordToken> illegalModifiers
224 ) {
225 reportIllegalModifiers(modifierList, illegalModifiers, trace);
226 }
227
228 @NotNull
229 public static Map<JetModifierKeywordToken, ASTNode> getNodesCorrespondingToModifiers(@NotNull JetModifierList modifierList, @NotNull Collection<JetModifierKeywordToken> possibleModifiers) {
230 Map<JetModifierKeywordToken, ASTNode> nodes = Maps.newHashMap();
231 for (JetModifierKeywordToken modifier : possibleModifiers) {
232 if (modifierList.hasModifier(modifier)) {
233 nodes.put(modifier, modifierList.getModifierNode(modifier));
234 }
235 }
236 return nodes;
237 }
238
239 @NotNull
240 public static Modality resolveModalityFromModifiers(@NotNull JetModifierListOwner modifierListOwner, @NotNull Modality defaultModality) {
241 return resolveModalityFromModifiers(modifierListOwner.getModifierList(), defaultModality);
242 }
243
244 public static Modality resolveModalityFromModifiers(@Nullable JetModifierList modifierList, @NotNull Modality defaultModality) {
245 if (modifierList == null) return defaultModality;
246 boolean hasAbstractModifier = modifierList.hasModifier(ABSTRACT_KEYWORD);
247 boolean hasOverrideModifier = modifierList.hasModifier(OVERRIDE_KEYWORD);
248
249 if (modifierList.hasModifier(OPEN_KEYWORD)) {
250 if (hasAbstractModifier || defaultModality == Modality.ABSTRACT) {
251 return Modality.ABSTRACT;
252 }
253 return Modality.OPEN;
254 }
255 if (hasAbstractModifier) {
256 return Modality.ABSTRACT;
257 }
258 boolean hasFinalModifier = modifierList.hasModifier(FINAL_KEYWORD);
259 if (hasOverrideModifier && !hasFinalModifier && !(defaultModality == Modality.ABSTRACT)) {
260 return Modality.OPEN;
261 }
262 if (hasFinalModifier) {
263 return Modality.FINAL;
264 }
265 return defaultModality;
266 }
267
268 @NotNull
269 public static Visibility resolveVisibilityFromModifiers(@NotNull JetModifierListOwner modifierListOwner, @NotNull Visibility defaultVisibility) {
270 return resolveVisibilityFromModifiers(modifierListOwner.getModifierList(), defaultVisibility);
271 }
272
273 public static Visibility resolveVisibilityFromModifiers(@Nullable JetModifierList modifierList, @NotNull Visibility defaultVisibility) {
274 if (modifierList == null) return defaultVisibility;
275 if (modifierList.hasModifier(PRIVATE_KEYWORD)) return Visibilities.PRIVATE;
276 if (modifierList.hasModifier(PUBLIC_KEYWORD)) return Visibilities.PUBLIC;
277 if (modifierList.hasModifier(PROTECTED_KEYWORD)) return Visibilities.PROTECTED;
278 if (modifierList.hasModifier(INTERNAL_KEYWORD)) return Visibilities.INTERNAL;
279 return defaultVisibility;
280 }
281
282 public static boolean isInnerClass(@Nullable JetModifierList modifierList) {
283 return modifierList != null && modifierList.hasModifier(INNER_KEYWORD);
284 }
285
286 @NotNull
287 public static Visibility getDefaultClassVisibility(@NotNull ClassDescriptor descriptor) {
288 ClassKind kind = descriptor.getKind();
289 if (kind == ClassKind.ENUM_ENTRY) {
290 return Visibilities.PUBLIC;
291 }
292 if (kind == ClassKind.CLASS_OBJECT) {
293 return ((ClassDescriptor) descriptor.getContainingDeclaration()).getVisibility();
294 }
295 return Visibilities.INTERNAL;
296 }
297 }