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