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