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    }