001 /*
002 * Copyright 2010-2013 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.Sets;
020 import com.intellij.lang.ASTNode;
021 import org.jetbrains.annotations.NotNull;
022 import org.jetbrains.jet.lang.descriptors.*;
023 import org.jetbrains.jet.lang.descriptors.impl.MutableClassDescriptor;
024 import org.jetbrains.jet.lang.diagnostics.Errors;
025 import org.jetbrains.jet.lang.psi.*;
026 import org.jetbrains.jet.lang.types.DeferredType;
027 import org.jetbrains.jet.lang.types.JetType;
028 import org.jetbrains.jet.lexer.JetKeywordToken;
029 import org.jetbrains.jet.lexer.JetTokens;
030
031 import javax.inject.Inject;
032 import java.util.List;
033 import java.util.Map;
034
035 import static org.jetbrains.jet.lang.diagnostics.Errors.*;
036 import static org.jetbrains.jet.lang.resolve.BindingContext.TYPE;
037
038 public class DeclarationsChecker {
039 @NotNull
040 private BindingTrace trace;
041 @NotNull
042 private ModifiersChecker modifiersChecker;
043
044 @Inject
045 public void setTrace(@NotNull BindingTrace trace) {
046 this.trace = trace;
047 this.modifiersChecker = new ModifiersChecker(trace);
048 }
049
050 public void process(@NotNull BodiesResolveContext bodiesResolveContext) {
051 Map<JetClass, MutableClassDescriptor> classes = bodiesResolveContext.getClasses();
052 for (Map.Entry<JetClass, MutableClassDescriptor> entry : classes.entrySet()) {
053 JetClass aClass = entry.getKey();
054 MutableClassDescriptor classDescriptor = entry.getValue();
055 if (!bodiesResolveContext.completeAnalysisNeeded(aClass)) continue;
056
057 checkClass(aClass, classDescriptor);
058 modifiersChecker.checkModifiersForDeclaration(aClass, classDescriptor);
059 }
060
061 Map<JetObjectDeclaration, MutableClassDescriptor> objects = bodiesResolveContext.getObjects();
062 for (Map.Entry<JetObjectDeclaration, MutableClassDescriptor> entry : objects.entrySet()) {
063 JetObjectDeclaration objectDeclaration = entry.getKey();
064 MutableClassDescriptor objectDescriptor = entry.getValue();
065
066 if (!bodiesResolveContext.completeAnalysisNeeded(objectDeclaration)) continue;
067 checkObject(objectDeclaration);
068 modifiersChecker.checkModifiersForDeclaration(objectDeclaration, objectDescriptor);
069 }
070
071 Map<JetNamedFunction, SimpleFunctionDescriptor> functions = bodiesResolveContext.getFunctions();
072 for (Map.Entry<JetNamedFunction, SimpleFunctionDescriptor> entry : functions.entrySet()) {
073 JetNamedFunction function = entry.getKey();
074 SimpleFunctionDescriptor functionDescriptor = entry.getValue();
075
076 if (!bodiesResolveContext.completeAnalysisNeeded(function)) continue;
077 checkFunction(function, functionDescriptor);
078 modifiersChecker.checkModifiersForDeclaration(function, functionDescriptor);
079 }
080
081 Map<JetProperty, PropertyDescriptor> properties = bodiesResolveContext.getProperties();
082 for (Map.Entry<JetProperty, PropertyDescriptor> entry : properties.entrySet()) {
083 JetProperty property = entry.getKey();
084 PropertyDescriptor propertyDescriptor = entry.getValue();
085
086 if (!bodiesResolveContext.completeAnalysisNeeded(property)) continue;
087 checkProperty(property, propertyDescriptor);
088 modifiersChecker.checkModifiersForDeclaration(property, propertyDescriptor);
089 }
090
091 }
092
093 private void reportErrorIfHasIllegalModifier(JetModifierListOwner declaration) {
094 if (declaration.hasModifier(JetTokens.ENUM_KEYWORD)) {
095 trace.report(ILLEGAL_ENUM_ANNOTATION.on(declaration));
096 }
097 if (declaration.hasModifier(JetTokens.ANNOTATION_KEYWORD)) {
098 trace.report(ILLEGAL_ANNOTATION_KEYWORD.on(declaration));
099 }
100 }
101
102 private void checkObject(JetObjectDeclaration declaration) {
103 reportErrorIfHasIllegalModifier(declaration);
104 }
105
106 private void checkClass(JetClass aClass, MutableClassDescriptor classDescriptor) {
107 checkOpenMembers(classDescriptor);
108 if (aClass.isTrait()) {
109 checkTraitModifiers(aClass);
110 }
111 else if (classDescriptor.getKind() == ClassKind.ENUM_CLASS) {
112 checkEnumModifiers(aClass);
113 }
114 else if (classDescriptor.getKind() == ClassKind.ENUM_ENTRY) {
115 checkEnumEntry(aClass, classDescriptor);
116 }
117 }
118
119 private void checkTraitModifiers(JetClass aClass) {
120 reportErrorIfHasIllegalModifier(aClass);
121 JetModifierList modifierList = aClass.getModifierList();
122 if (modifierList == null) return;
123 if (modifierList.hasModifier(JetTokens.FINAL_KEYWORD)) {
124 trace.report(Errors.TRAIT_CAN_NOT_BE_FINAL.on(modifierList.getModifierNode(JetTokens.FINAL_KEYWORD).getPsi()));
125 }
126 if (modifierList.hasModifier(JetTokens.ABSTRACT_KEYWORD)) {
127 trace.report(Errors.ABSTRACT_MODIFIER_IN_TRAIT.on(aClass));
128 }
129 if (modifierList.hasModifier(JetTokens.OPEN_KEYWORD)) {
130 trace.report(Errors.OPEN_MODIFIER_IN_TRAIT.on(aClass));
131 }
132 }
133
134
135 private void checkOpenMembers(MutableClassDescriptor classDescriptor) {
136 for (CallableMemberDescriptor memberDescriptor : classDescriptor.getDeclaredCallableMembers()) {
137 if (memberDescriptor.getKind() != CallableMemberDescriptor.Kind.DECLARATION) continue;
138 JetNamedDeclaration member = (JetNamedDeclaration) BindingContextUtils.descriptorToDeclaration(trace.getBindingContext(), memberDescriptor);
139 if (member != null && classDescriptor.getModality() == Modality.FINAL && member.hasModifier(JetTokens.OPEN_KEYWORD)) {
140 trace.report(NON_FINAL_MEMBER_IN_FINAL_CLASS.on(member));
141 }
142 }
143 }
144
145 private void checkProperty(JetProperty property, PropertyDescriptor propertyDescriptor) {
146 reportErrorIfHasIllegalModifier(property);
147 DeclarationDescriptor containingDeclaration = propertyDescriptor.getContainingDeclaration();
148 if (containingDeclaration instanceof ClassDescriptor) {
149 checkPropertyAbstractness(property, propertyDescriptor, (ClassDescriptor) containingDeclaration);
150 }
151 else {
152 modifiersChecker.checkIllegalModalityModifiers(property);
153 }
154 checkPropertyInitializer(property, propertyDescriptor);
155 checkAccessors(property, propertyDescriptor);
156 checkDeclaredTypeInPublicMember(property, propertyDescriptor);
157 }
158
159 private void checkDeclaredTypeInPublicMember(JetNamedDeclaration member, CallableMemberDescriptor memberDescriptor) {
160 boolean hasDeferredType;
161 if (member instanceof JetProperty) {
162 hasDeferredType = ((JetProperty) member).getTypeRef() == null && DescriptorResolver.hasBody((JetProperty) member);
163 }
164 else {
165 assert member instanceof JetFunction;
166 JetFunction function = (JetFunction) member;
167 hasDeferredType = function.getReturnTypeRef() == null && function.getBodyExpression() != null && !function.hasBlockBody();
168 }
169 if ((memberDescriptor.getVisibility().isPublicAPI()) && memberDescriptor.getOverriddenDescriptors().size() == 0 && hasDeferredType) {
170 trace.report(PUBLIC_MEMBER_SHOULD_SPECIFY_TYPE.on(member));
171 }
172 }
173
174 private void checkPropertyAbstractness(
175 @NotNull JetProperty property,
176 @NotNull PropertyDescriptor propertyDescriptor,
177 @NotNull ClassDescriptor classDescriptor
178 ) {
179 JetPropertyAccessor getter = property.getGetter();
180 JetPropertyAccessor setter = property.getSetter();
181 JetModifierList modifierList = property.getModifierList();
182 ASTNode abstractNode = modifierList != null ? modifierList.getModifierNode(JetTokens.ABSTRACT_KEYWORD) : null;
183
184 if (abstractNode != null) { //has abstract modifier
185 if (!(classDescriptor.getModality() == Modality.ABSTRACT) && classDescriptor.getKind() != ClassKind.ENUM_CLASS) {
186 String name = property.getName();
187 trace.report(ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS.on(property, name != null ? name : "", classDescriptor));
188 return;
189 }
190 if (classDescriptor.getKind() == ClassKind.TRAIT) {
191 trace.report(ABSTRACT_MODIFIER_IN_TRAIT.on(property));
192 }
193 }
194
195 if (propertyDescriptor.getModality() == Modality.ABSTRACT) {
196 JetType returnType = propertyDescriptor.getReturnType();
197 if (returnType instanceof DeferredType) {
198 returnType = ((DeferredType) returnType).getActualType();
199 }
200
201 JetExpression initializer = property.getInitializer();
202 if (initializer != null) {
203 trace.report(ABSTRACT_PROPERTY_WITH_INITIALIZER.on(initializer));
204 }
205 JetPropertyDelegate delegate = property.getDelegate();
206 if (delegate != null) {
207 trace.report(ABSTRACT_DELEGATED_PROPERTY.on(delegate));
208 }
209 if (getter != null && getter.getBodyExpression() != null) {
210 trace.report(ABSTRACT_PROPERTY_WITH_GETTER.on(getter));
211 }
212 if (setter != null && setter.getBodyExpression() != null) {
213 trace.report(ABSTRACT_PROPERTY_WITH_SETTER.on(setter));
214 }
215 }
216 }
217
218 private void checkPropertyInitializer(
219 @NotNull JetProperty property,
220 @NotNull PropertyDescriptor propertyDescriptor
221 ) {
222 JetPropertyAccessor getter = property.getGetter();
223 JetPropertyAccessor setter = property.getSetter();
224 boolean hasAccessorImplementation = (getter != null && getter.getBodyExpression() != null) ||
225 (setter != null && setter.getBodyExpression() != null);
226
227 if (propertyDescriptor.getModality() == Modality.ABSTRACT) {
228 if (property.getDelegateExpressionOrInitializer() == null && property.getTypeRef() == null) {
229 trace.report(PROPERTY_WITH_NO_TYPE_NO_INITIALIZER.on(property));
230 }
231 return;
232 }
233 DeclarationDescriptor containingDeclaration = propertyDescriptor.getContainingDeclaration();
234 boolean inTrait = containingDeclaration instanceof ClassDescriptor && ((ClassDescriptor)containingDeclaration).getKind() == ClassKind.TRAIT;
235 JetExpression initializer = property.getInitializer();
236 JetPropertyDelegate delegate = property.getDelegate();
237 boolean backingFieldRequired = trace.getBindingContext().get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor);
238
239 if (inTrait && backingFieldRequired && hasAccessorImplementation) {
240 trace.report(BACKING_FIELD_IN_TRAIT.on(property));
241 }
242 if (initializer == null && delegate == null) {
243 boolean error = false;
244 if (backingFieldRequired && !inTrait && !trace.getBindingContext().get(BindingContext.IS_INITIALIZED, propertyDescriptor)) {
245 if (!(containingDeclaration instanceof ClassDescriptor) || hasAccessorImplementation) {
246 error = true;
247 trace.report(MUST_BE_INITIALIZED.on(property));
248 }
249 else {
250 error = true;
251 trace.report(MUST_BE_INITIALIZED_OR_BE_ABSTRACT.on(property));
252 }
253 }
254 if (!error && property.getTypeRef() == null) {
255 trace.report(PROPERTY_WITH_NO_TYPE_NO_INITIALIZER.on(property));
256 }
257 if (inTrait && property.hasModifier(JetTokens.FINAL_KEYWORD) && backingFieldRequired) {
258 trace.report(FINAL_PROPERTY_IN_TRAIT.on(property));
259 }
260 return;
261 }
262 if (inTrait) {
263 if (delegate != null) {
264 trace.report(DELEGATED_PROPERTY_IN_TRAIT.on(delegate));
265 }
266 else {
267 trace.report(PROPERTY_INITIALIZER_IN_TRAIT.on(initializer));
268 }
269 }
270 else if (!backingFieldRequired && delegate == null) {
271 trace.report(PROPERTY_INITIALIZER_NO_BACKING_FIELD.on(initializer));
272 }
273 }
274
275 protected void checkFunction(JetNamedFunction function, SimpleFunctionDescriptor functionDescriptor) {
276 reportErrorIfHasIllegalModifier(function);
277 DeclarationDescriptor containingDescriptor = functionDescriptor.getContainingDeclaration();
278 boolean hasAbstractModifier = function.hasModifier(JetTokens.ABSTRACT_KEYWORD);
279 checkDeclaredTypeInPublicMember(function, functionDescriptor);
280 if (containingDescriptor instanceof ClassDescriptor) {
281 ClassDescriptor classDescriptor = (ClassDescriptor) containingDescriptor;
282 boolean inTrait = classDescriptor.getKind() == ClassKind.TRAIT;
283 boolean inEnum = classDescriptor.getKind() == ClassKind.ENUM_CLASS;
284 boolean inAbstractClass = classDescriptor.getModality() == Modality.ABSTRACT;
285 if (hasAbstractModifier && !inAbstractClass && !inEnum) {
286 trace.report(ABSTRACT_FUNCTION_IN_NON_ABSTRACT_CLASS.on(function, functionDescriptor.getName().asString(), classDescriptor));
287 }
288 if (hasAbstractModifier && inTrait) {
289 trace.report(ABSTRACT_MODIFIER_IN_TRAIT.on(function));
290 }
291 boolean hasBody = function.getBodyExpression() != null;
292 if (hasBody && hasAbstractModifier) {
293 trace.report(ABSTRACT_FUNCTION_WITH_BODY.on(function, functionDescriptor));
294 }
295 if (!hasBody && function.hasModifier(JetTokens.FINAL_KEYWORD) && inTrait) {
296 trace.report(FINAL_FUNCTION_WITH_NO_BODY.on(function, functionDescriptor));
297 }
298 if (!hasBody && !hasAbstractModifier && !inTrait) {
299 trace.report(NON_ABSTRACT_FUNCTION_WITH_NO_BODY.on(function, functionDescriptor));
300 }
301 return;
302 }
303 modifiersChecker.checkIllegalModalityModifiers(function);
304 if (function.getBodyExpression() == null && !hasAbstractModifier) {
305 trace.report(NON_MEMBER_FUNCTION_NO_BODY.on(function, functionDescriptor));
306 }
307 }
308
309 private void checkAccessors(@NotNull JetProperty property, @NotNull PropertyDescriptor propertyDescriptor) {
310 for (JetPropertyAccessor accessor : property.getAccessors()) {
311 modifiersChecker.checkIllegalModalityModifiers(accessor);
312 }
313 JetPropertyAccessor getter = property.getGetter();
314 PropertyGetterDescriptor getterDescriptor = propertyDescriptor.getGetter();
315 JetModifierList getterModifierList = getter != null ? getter.getModifierList() : null;
316 if (getterModifierList != null && getterDescriptor != null) {
317 Map<JetKeywordToken, ASTNode> nodes = ModifiersChecker.getNodesCorrespondingToModifiers(getterModifierList, Sets
318 .newHashSet(JetTokens.PUBLIC_KEYWORD, JetTokens.PROTECTED_KEYWORD, JetTokens.PRIVATE_KEYWORD,
319 JetTokens.INTERNAL_KEYWORD));
320 if (getterDescriptor.getVisibility() != propertyDescriptor.getVisibility()) {
321 for (ASTNode node : nodes.values()) {
322 trace.report(Errors.GETTER_VISIBILITY_DIFFERS_FROM_PROPERTY_VISIBILITY.on(node.getPsi()));
323 }
324 }
325 else {
326 for (ASTNode node : nodes.values()) {
327 trace.report(Errors.REDUNDANT_MODIFIER_IN_GETTER.on(node.getPsi()));
328 }
329 }
330 }
331 }
332
333 private void checkEnumModifiers(JetClass aClass) {
334 if (aClass.hasModifier(JetTokens.OPEN_KEYWORD)) {
335 trace.report(OPEN_MODIFIER_IN_ENUM.on(aClass));
336 }
337 }
338
339 private void checkEnumEntry(JetClass aClass, ClassDescriptor classDescriptor) {
340 DeclarationDescriptor declaration = classDescriptor.getContainingDeclaration().getContainingDeclaration();
341 assert declaration instanceof ClassDescriptor;
342 ClassDescriptor enumClass = (ClassDescriptor) declaration;
343 assert enumClass.getKind() == ClassKind.ENUM_CLASS;
344
345 List<JetDelegationSpecifier> delegationSpecifiers = aClass.getDelegationSpecifiers();
346 ConstructorDescriptor constructor = enumClass.getUnsubstitutedPrimaryConstructor();
347 assert constructor != null;
348 if (!constructor.getValueParameters().isEmpty() && delegationSpecifiers.isEmpty()) {
349 trace.report(ENUM_ENTRY_SHOULD_BE_INITIALIZED.on(aClass, enumClass));
350 }
351
352 for (JetDelegationSpecifier delegationSpecifier : delegationSpecifiers) {
353 JetTypeReference typeReference = delegationSpecifier.getTypeReference();
354 if (typeReference != null) {
355 JetType type = trace.getBindingContext().get(TYPE, typeReference);
356 if (type != null) {
357 JetType enumType = enumClass.getDefaultType();
358 if (!type.getConstructor().equals(enumType.getConstructor())) {
359 trace.report(ENUM_ENTRY_ILLEGAL_TYPE.on(typeReference, enumClass));
360 }
361 }
362 }
363 }
364 }
365 }