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    }