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