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 reportErrorIfHasEnumModifier(JetModifierListOwner declaration) {
094 if (declaration.hasModifier(JetTokens.ENUM_KEYWORD)) {
095 trace.report(ILLEGAL_ENUM_ANNOTATION.on(declaration));
096 }
097 }
098
099 private void checkObject(JetObjectDeclaration declaration) {
100 reportErrorIfHasEnumModifier(declaration);
101 }
102
103 private void checkClass(JetClass aClass, MutableClassDescriptor classDescriptor) {
104 checkOpenMembers(classDescriptor);
105 if (aClass.isTrait()) {
106 checkTraitModifiers(aClass);
107 }
108 else if (classDescriptor.getKind() == ClassKind.ENUM_CLASS) {
109 checkEnumModifiers(aClass);
110 }
111 else if (classDescriptor.getKind() == ClassKind.ENUM_ENTRY) {
112 checkEnumEntry(aClass, classDescriptor);
113 }
114 }
115
116 private void checkTraitModifiers(JetClass aClass) {
117 reportErrorIfHasEnumModifier(aClass);
118 JetModifierList modifierList = aClass.getModifierList();
119 if (modifierList == null) return;
120 if (modifierList.hasModifier(JetTokens.FINAL_KEYWORD)) {
121 trace.report(Errors.TRAIT_CAN_NOT_BE_FINAL.on(modifierList.getModifierNode(JetTokens.FINAL_KEYWORD).getPsi()));
122 }
123 if (modifierList.hasModifier(JetTokens.ABSTRACT_KEYWORD)) {
124 trace.report(Errors.ABSTRACT_MODIFIER_IN_TRAIT.on(aClass));
125 }
126 if (modifierList.hasModifier(JetTokens.OPEN_KEYWORD)) {
127 trace.report(Errors.OPEN_MODIFIER_IN_TRAIT.on(aClass));
128 }
129 }
130
131
132 private void checkOpenMembers(MutableClassDescriptor classDescriptor) {
133 for (CallableMemberDescriptor memberDescriptor : classDescriptor.getDeclaredCallableMembers()) {
134 if (memberDescriptor.getKind() != CallableMemberDescriptor.Kind.DECLARATION) continue;
135 JetNamedDeclaration member = (JetNamedDeclaration) BindingContextUtils.descriptorToDeclaration(trace.getBindingContext(), memberDescriptor);
136 if (member != null && classDescriptor.getModality() == Modality.FINAL && member.hasModifier(JetTokens.OPEN_KEYWORD)) {
137 trace.report(NON_FINAL_MEMBER_IN_FINAL_CLASS.on(member));
138 }
139 }
140 }
141
142 private void checkProperty(JetProperty property, PropertyDescriptor propertyDescriptor) {
143 reportErrorIfHasEnumModifier(property);
144 DeclarationDescriptor containingDeclaration = propertyDescriptor.getContainingDeclaration();
145 if (containingDeclaration instanceof ClassDescriptor) {
146 checkPropertyAbstractness(property, propertyDescriptor, (ClassDescriptor) containingDeclaration);
147 }
148 else {
149 modifiersChecker.checkIllegalModalityModifiers(property);
150 }
151 checkPropertyInitializer(property, propertyDescriptor);
152 checkAccessors(property, propertyDescriptor);
153 checkDeclaredTypeInPublicMember(property, propertyDescriptor);
154 }
155
156 private void checkDeclaredTypeInPublicMember(JetNamedDeclaration member, CallableMemberDescriptor memberDescriptor) {
157 boolean hasDeferredType;
158 if (member instanceof JetProperty) {
159 hasDeferredType = ((JetProperty) member).getTypeRef() == null && DescriptorResolver.hasBody((JetProperty) member);
160 }
161 else {
162 assert member instanceof JetFunction;
163 JetFunction function = (JetFunction) member;
164 hasDeferredType = function.getReturnTypeRef() == null && function.getBodyExpression() != null && !function.hasBlockBody();
165 }
166 if ((memberDescriptor.getVisibility().isPublicAPI()) && memberDescriptor.getOverriddenDescriptors().size() == 0 && hasDeferredType) {
167 trace.report(PUBLIC_MEMBER_SHOULD_SPECIFY_TYPE.on(member));
168 }
169 }
170
171 private void checkPropertyAbstractness(
172 @NotNull JetProperty property,
173 @NotNull PropertyDescriptor propertyDescriptor,
174 @NotNull ClassDescriptor classDescriptor
175 ) {
176 JetPropertyAccessor getter = property.getGetter();
177 JetPropertyAccessor setter = property.getSetter();
178 JetModifierList modifierList = property.getModifierList();
179 ASTNode abstractNode = modifierList != null ? modifierList.getModifierNode(JetTokens.ABSTRACT_KEYWORD) : null;
180
181 if (abstractNode != null) { //has abstract modifier
182 if (!(classDescriptor.getModality() == Modality.ABSTRACT) && classDescriptor.getKind() != ClassKind.ENUM_CLASS) {
183 String name = property.getName();
184 trace.report(ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS.on(property, name != null ? name : "", classDescriptor));
185 return;
186 }
187 if (classDescriptor.getKind() == ClassKind.TRAIT) {
188 trace.report(ABSTRACT_MODIFIER_IN_TRAIT.on(property));
189 }
190 }
191
192 if (propertyDescriptor.getModality() == Modality.ABSTRACT) {
193 JetType returnType = propertyDescriptor.getReturnType();
194 if (returnType instanceof DeferredType) {
195 returnType = ((DeferredType) returnType).getActualType();
196 }
197
198 JetExpression initializer = property.getInitializer();
199 if (initializer != null) {
200 trace.report(ABSTRACT_PROPERTY_WITH_INITIALIZER.on(initializer));
201 }
202 JetPropertyDelegate delegate = property.getDelegate();
203 if (delegate != null) {
204 trace.report(ABSTRACT_DELEGATED_PROPERTY.on(delegate));
205 }
206 if (getter != null && getter.getBodyExpression() != null) {
207 trace.report(ABSTRACT_PROPERTY_WITH_GETTER.on(getter));
208 }
209 if (setter != null && setter.getBodyExpression() != null) {
210 trace.report(ABSTRACT_PROPERTY_WITH_SETTER.on(setter));
211 }
212 }
213 }
214
215 private void checkPropertyInitializer(
216 @NotNull JetProperty property,
217 @NotNull PropertyDescriptor propertyDescriptor
218 ) {
219 JetPropertyAccessor getter = property.getGetter();
220 JetPropertyAccessor setter = property.getSetter();
221 boolean hasAccessorImplementation = (getter != null && getter.getBodyExpression() != null) ||
222 (setter != null && setter.getBodyExpression() != null);
223
224 if (propertyDescriptor.getModality() == Modality.ABSTRACT) {
225 if (property.getDelegateExpressionOrInitializer() == null && property.getTypeRef() == null) {
226 trace.report(PROPERTY_WITH_NO_TYPE_NO_INITIALIZER.on(property));
227 }
228 return;
229 }
230 DeclarationDescriptor containingDeclaration = propertyDescriptor.getContainingDeclaration();
231 boolean inTrait = containingDeclaration instanceof ClassDescriptor && ((ClassDescriptor)containingDeclaration).getKind() == ClassKind.TRAIT;
232 JetExpression initializer = property.getInitializer();
233 JetPropertyDelegate delegate = property.getDelegate();
234 boolean backingFieldRequired = trace.getBindingContext().get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor);
235
236 if (inTrait && backingFieldRequired && hasAccessorImplementation) {
237 trace.report(BACKING_FIELD_IN_TRAIT.on(property));
238 }
239 if (initializer == null && delegate == null) {
240 boolean error = false;
241 if (backingFieldRequired && !inTrait && !trace.getBindingContext().get(BindingContext.IS_INITIALIZED, propertyDescriptor)) {
242 if (!(containingDeclaration instanceof ClassDescriptor) || hasAccessorImplementation) {
243 error = true;
244 trace.report(MUST_BE_INITIALIZED.on(property));
245 }
246 else {
247 error = true;
248 trace.report(MUST_BE_INITIALIZED_OR_BE_ABSTRACT.on(property));
249 }
250 }
251 if (!error && property.getTypeRef() == null) {
252 trace.report(PROPERTY_WITH_NO_TYPE_NO_INITIALIZER.on(property));
253 }
254 if (inTrait && property.hasModifier(JetTokens.FINAL_KEYWORD) && backingFieldRequired) {
255 trace.report(FINAL_PROPERTY_IN_TRAIT.on(property));
256 }
257 return;
258 }
259 if (inTrait) {
260 if (delegate != null) {
261 trace.report(DELEGATED_PROPERTY_IN_TRAIT.on(delegate));
262 }
263 else {
264 trace.report(PROPERTY_INITIALIZER_IN_TRAIT.on(initializer));
265 }
266 }
267 else if (!backingFieldRequired && delegate == null) {
268 trace.report(PROPERTY_INITIALIZER_NO_BACKING_FIELD.on(initializer));
269 }
270 }
271
272 protected void checkFunction(JetNamedFunction function, SimpleFunctionDescriptor functionDescriptor) {
273 reportErrorIfHasEnumModifier(function);
274 DeclarationDescriptor containingDescriptor = functionDescriptor.getContainingDeclaration();
275 boolean hasAbstractModifier = function.hasModifier(JetTokens.ABSTRACT_KEYWORD);
276 checkDeclaredTypeInPublicMember(function, functionDescriptor);
277 if (containingDescriptor instanceof ClassDescriptor) {
278 ClassDescriptor classDescriptor = (ClassDescriptor) containingDescriptor;
279 boolean inTrait = classDescriptor.getKind() == ClassKind.TRAIT;
280 boolean inEnum = classDescriptor.getKind() == ClassKind.ENUM_CLASS;
281 boolean inAbstractClass = classDescriptor.getModality() == Modality.ABSTRACT;
282 if (hasAbstractModifier && !inAbstractClass && !inEnum) {
283 trace.report(ABSTRACT_FUNCTION_IN_NON_ABSTRACT_CLASS.on(function, functionDescriptor.getName().asString(), classDescriptor));
284 }
285 if (hasAbstractModifier && inTrait) {
286 trace.report(ABSTRACT_MODIFIER_IN_TRAIT.on(function));
287 }
288 boolean hasBody = function.getBodyExpression() != null;
289 if (hasBody && hasAbstractModifier) {
290 trace.report(ABSTRACT_FUNCTION_WITH_BODY.on(function, functionDescriptor));
291 }
292 if (!hasBody && function.hasModifier(JetTokens.FINAL_KEYWORD) && inTrait) {
293 trace.report(FINAL_FUNCTION_WITH_NO_BODY.on(function, functionDescriptor));
294 }
295 if (!hasBody && !hasAbstractModifier && !inTrait) {
296 trace.report(NON_ABSTRACT_FUNCTION_WITH_NO_BODY.on(function, functionDescriptor));
297 }
298 return;
299 }
300 modifiersChecker.checkIllegalModalityModifiers(function);
301 if (function.getBodyExpression() == null && !hasAbstractModifier) {
302 trace.report(NON_MEMBER_FUNCTION_NO_BODY.on(function, functionDescriptor));
303 }
304 }
305
306 private void checkAccessors(@NotNull JetProperty property, @NotNull PropertyDescriptor propertyDescriptor) {
307 for (JetPropertyAccessor accessor : property.getAccessors()) {
308 modifiersChecker.checkIllegalModalityModifiers(accessor);
309 }
310 JetPropertyAccessor getter = property.getGetter();
311 PropertyGetterDescriptor getterDescriptor = propertyDescriptor.getGetter();
312 JetModifierList getterModifierList = getter != null ? getter.getModifierList() : null;
313 if (getterModifierList != null && getterDescriptor != null) {
314 Map<JetKeywordToken, ASTNode> nodes = ModifiersChecker.getNodesCorrespondingToModifiers(getterModifierList, Sets
315 .newHashSet(JetTokens.PUBLIC_KEYWORD, JetTokens.PROTECTED_KEYWORD, JetTokens.PRIVATE_KEYWORD,
316 JetTokens.INTERNAL_KEYWORD));
317 if (getterDescriptor.getVisibility() != propertyDescriptor.getVisibility()) {
318 for (ASTNode node : nodes.values()) {
319 trace.report(Errors.GETTER_VISIBILITY_DIFFERS_FROM_PROPERTY_VISIBILITY.on(node.getPsi()));
320 }
321 }
322 else {
323 for (ASTNode node : nodes.values()) {
324 trace.report(Errors.REDUNDANT_MODIFIER_IN_GETTER.on(node.getPsi()));
325 }
326 }
327 }
328 }
329
330 private void checkEnumModifiers(JetClass aClass) {
331 if (aClass.hasModifier(JetTokens.OPEN_KEYWORD)) {
332 trace.report(OPEN_MODIFIER_IN_ENUM.on(aClass));
333 }
334 }
335
336 private void checkEnumEntry(JetClass aClass, ClassDescriptor classDescriptor) {
337 DeclarationDescriptor declaration = classDescriptor.getContainingDeclaration().getContainingDeclaration();
338 assert declaration instanceof ClassDescriptor;
339 ClassDescriptor enumClass = (ClassDescriptor) declaration;
340 assert enumClass.getKind() == ClassKind.ENUM_CLASS;
341
342 List<JetDelegationSpecifier> delegationSpecifiers = aClass.getDelegationSpecifiers();
343 ConstructorDescriptor constructor = enumClass.getUnsubstitutedPrimaryConstructor();
344 assert constructor != null;
345 if (!constructor.getValueParameters().isEmpty() && delegationSpecifiers.isEmpty()) {
346 trace.report(ENUM_ENTRY_SHOULD_BE_INITIALIZED.on(aClass, enumClass));
347 }
348
349 for (JetDelegationSpecifier delegationSpecifier : delegationSpecifiers) {
350 JetTypeReference typeReference = delegationSpecifier.getTypeReference();
351 if (typeReference != null) {
352 JetType type = trace.getBindingContext().get(TYPE, typeReference);
353 if (type != null) {
354 JetType enumType = enumClass.getDefaultType();
355 if (!type.getConstructor().equals(enumType.getConstructor())) {
356 trace.report(ENUM_ENTRY_ILLEGAL_TYPE.on(typeReference, enumClass));
357 }
358 }
359 }
360 }
361 }
362 }