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    }