001    /*
002     * Copyright 2010-2014 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.Multimap;
020    import com.google.common.collect.Sets;
021    import com.intellij.lang.ASTNode;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.jet.lang.descriptors.*;
025    import org.jetbrains.jet.lang.diagnostics.Errors;
026    import org.jetbrains.jet.lang.psi.*;
027    import org.jetbrains.jet.lang.types.*;
028    import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
029    import org.jetbrains.jet.lexer.JetModifierKeywordToken;
030    import org.jetbrains.jet.lexer.JetTokens;
031    
032    import javax.inject.Inject;
033    import java.util.*;
034    
035    import static org.jetbrains.jet.lang.diagnostics.Errors.*;
036    import static org.jetbrains.jet.lang.resolve.BindingContext.TYPE;
037    import static org.jetbrains.jet.lang.resolve.BindingContext.TYPE_PARAMETER;
038    
039    public class DeclarationsChecker {
040        @NotNull
041        private BindingTrace trace;
042        @NotNull
043        private ModifiersChecker modifiersChecker;
044        @NotNull
045        private DescriptorResolver descriptorResolver;
046    
047        @Inject
048        public void setTrace(@NotNull BindingTrace trace) {
049            this.trace = trace;
050            this.modifiersChecker = new ModifiersChecker(trace);
051        }
052    
053        @Inject
054        public void setDescriptorResolver(@NotNull DescriptorResolver descriptorResolver) {
055            this.descriptorResolver = descriptorResolver;
056        }
057    
058        public void process(@NotNull BodiesResolveContext bodiesResolveContext) {
059            for (JetFile file : bodiesResolveContext.getFiles()) {
060                checkModifiersAndAnnotationsInPackageDirective(file);
061            }
062    
063            Map<JetClassOrObject, ClassDescriptorWithResolutionScopes> classes = bodiesResolveContext.getDeclaredClasses();
064            for (Map.Entry<JetClassOrObject, ClassDescriptorWithResolutionScopes> entry : classes.entrySet()) {
065                JetClassOrObject classOrObject = entry.getKey();
066                ClassDescriptorWithResolutionScopes classDescriptor = entry.getValue();
067                if (!bodiesResolveContext.completeAnalysisNeeded(classOrObject)) continue;
068    
069                checkSupertypesForConsistency(classDescriptor);
070                checkTypesInClassHeader(classOrObject);
071    
072                if (classOrObject instanceof JetClass) {
073                    JetClass jetClass = (JetClass) classOrObject;
074                    checkClass(bodiesResolveContext, jetClass, classDescriptor);
075                    descriptorResolver.checkNamesInConstraints(
076                            jetClass, classDescriptor, classDescriptor.getScopeForClassHeaderResolution(), trace);
077                }
078                else if (classOrObject instanceof JetObjectDeclaration) {
079                    checkObject((JetObjectDeclaration) classOrObject);
080                }
081    
082                modifiersChecker.checkModifiersForDeclaration(classOrObject, classDescriptor);
083            }
084    
085            Map<JetNamedFunction, SimpleFunctionDescriptor> functions = bodiesResolveContext.getFunctions();
086            for (Map.Entry<JetNamedFunction, SimpleFunctionDescriptor> entry : functions.entrySet()) {
087                JetNamedFunction function = entry.getKey();
088                SimpleFunctionDescriptor functionDescriptor = entry.getValue();
089    
090                if (!bodiesResolveContext.completeAnalysisNeeded(function)) continue;
091                checkFunction(function, functionDescriptor);
092                modifiersChecker.checkModifiersForDeclaration(function, functionDescriptor);
093            }
094    
095            Map<JetProperty, PropertyDescriptor> properties = bodiesResolveContext.getProperties();
096            for (Map.Entry<JetProperty, PropertyDescriptor> entry : properties.entrySet()) {
097                JetProperty property = entry.getKey();
098                PropertyDescriptor propertyDescriptor = entry.getValue();
099    
100                if (!bodiesResolveContext.completeAnalysisNeeded(property)) continue;
101                checkProperty(property, propertyDescriptor);
102                modifiersChecker.checkModifiersForDeclaration(property, propertyDescriptor);
103            }
104    
105        }
106    
107        private void reportErrorIfHasIllegalModifier(JetModifierListOwner declaration) {
108            if (declaration.hasModifier(JetTokens.ENUM_KEYWORD)) {
109                trace.report(ILLEGAL_ENUM_ANNOTATION.on(declaration));
110            }
111            if (declaration.hasModifier(JetTokens.ANNOTATION_KEYWORD)) {
112                trace.report(ILLEGAL_ANNOTATION_KEYWORD.on(declaration));
113            }
114        }
115    
116        private void checkModifiersAndAnnotationsInPackageDirective(JetFile file) {
117            JetPackageDirective packageDirective = file.getPackageDirective();
118            if (packageDirective == null) return;
119    
120            JetModifierList modifierList = packageDirective.getModifierList();
121            if (modifierList == null) return;
122    
123            for (JetAnnotationEntry annotationEntry : modifierList.getAnnotationEntries()) {
124                JetConstructorCalleeExpression calleeExpression = annotationEntry.getCalleeExpression();
125                if (calleeExpression != null) {
126                    JetReferenceExpression reference = calleeExpression.getConstructorReferenceExpression();
127                    if (reference != null) {
128                        trace.report(UNRESOLVED_REFERENCE.on(reference, reference));
129                    }
130                }
131            }
132    
133            ModifiersChecker.reportIllegalModifiers(modifierList, Arrays.asList(JetTokens.MODIFIER_KEYWORDS_ARRAY), trace);
134        }
135    
136        private void checkTypesInClassHeader(@NotNull JetClassOrObject classOrObject) {
137            for (JetDelegationSpecifier delegationSpecifier : classOrObject.getDelegationSpecifiers()) {
138                checkBoundsForTypeInClassHeader(delegationSpecifier.getTypeReference());
139            }
140    
141            if (!(classOrObject instanceof JetClass)) return;
142            JetClass jetClass = (JetClass) classOrObject;
143    
144            for (JetTypeParameter jetTypeParameter : jetClass.getTypeParameters()) {
145                checkBoundsForTypeInClassHeader(jetTypeParameter.getExtendsBound());
146                checkFinalUpperBounds(jetTypeParameter.getExtendsBound(), false);
147            }
148    
149            for (JetTypeConstraint constraint : jetClass.getTypeConstraints()) {
150                checkBoundsForTypeInClassHeader(constraint.getBoundTypeReference());
151                checkFinalUpperBounds(constraint.getBoundTypeReference(), constraint.isClassObjectConstraint());
152            }
153        }
154    
155        private void checkBoundsForTypeInClassHeader(@Nullable JetTypeReference typeReference) {
156            if (typeReference != null) {
157                JetType type = trace.getBindingContext().get(TYPE, typeReference);
158                if (type != null) {
159                    DescriptorResolver.checkBounds(typeReference, type, trace);
160                }
161            }
162        }
163    
164        private void checkFinalUpperBounds(@Nullable JetTypeReference typeReference, boolean isClassObjectConstraint) {
165            if (typeReference != null) {
166                JetType type = trace.getBindingContext().get(TYPE, typeReference);
167                if (type != null) {
168                    DescriptorResolver.checkUpperBoundType(typeReference, type, isClassObjectConstraint, trace);
169                }
170            }
171        }
172    
173        private void checkSupertypesForConsistency(@NotNull ClassDescriptor classDescriptor) {
174            Multimap<TypeConstructor, TypeProjection> multimap = SubstitutionUtils
175                    .buildDeepSubstitutionMultimap(classDescriptor.getDefaultType());
176            for (Map.Entry<TypeConstructor, Collection<TypeProjection>> entry : multimap.asMap().entrySet()) {
177                Collection<TypeProjection> projections = entry.getValue();
178                if (projections.size() > 1) {
179                    TypeConstructor typeConstructor = entry.getKey();
180                    DeclarationDescriptor declarationDescriptor = typeConstructor.getDeclarationDescriptor();
181                    assert declarationDescriptor instanceof TypeParameterDescriptor : declarationDescriptor;
182                    TypeParameterDescriptor typeParameterDescriptor = (TypeParameterDescriptor) declarationDescriptor;
183    
184                    // Immediate arguments of supertypes cannot be projected
185                    Set<JetType> conflictingTypes = Sets.newLinkedHashSet();
186                    for (TypeProjection projection : projections) {
187                        conflictingTypes.add(projection.getType());
188                    }
189                    switch (typeParameterDescriptor.getVariance()) {
190                        case INVARIANT:
191                            // Leave conflicting types as is
192                            break;
193                        case IN_VARIANCE:
194                            // Filter out those who have supertypes in this set (common supertype)
195                            Filter.REMOVE_IF_SUPERTYPE_IN_THE_SET.proceed(conflictingTypes);
196                            break;
197                        case OUT_VARIANCE:
198                            // Filter out those who have subtypes in this set (common subtype)
199                            Filter.REMOVE_IF_SUBTYPE_IN_THE_SET.proceed(conflictingTypes);
200                            break;
201                    }
202    
203                    if (conflictingTypes.size() > 1) {
204                        DeclarationDescriptor containingDeclaration = typeParameterDescriptor.getContainingDeclaration();
205                        assert containingDeclaration instanceof ClassDescriptor : containingDeclaration;
206                        JetClassOrObject psiElement = (JetClassOrObject) BindingContextUtils
207                                .classDescriptorToDeclaration(trace.getBindingContext(), classDescriptor);
208                        JetDelegationSpecifierList delegationSpecifierList = psiElement.getDelegationSpecifierList();
209                        assert delegationSpecifierList != null;
210                        //                        trace.getErrorHandler().genericError(delegationSpecifierList.getNode(), "Type parameter " + typeParameterDescriptor.getName() + " of " + containingDeclaration.getName() + " has inconsistent values: " + conflictingTypes);
211                        trace.report(INCONSISTENT_TYPE_PARAMETER_VALUES
212                                             .on(delegationSpecifierList, typeParameterDescriptor, (ClassDescriptor) containingDeclaration,
213                                                 conflictingTypes));
214                    }
215                }
216            }
217        }
218    
219        private enum Filter {
220            REMOVE_IF_SUBTYPE_IN_THE_SET {
221                @Override
222                public boolean removeNeeded(JetType subject, JetType other) {
223                    return JetTypeChecker.DEFAULT.isSubtypeOf(other, subject);
224                }
225            },
226            REMOVE_IF_SUPERTYPE_IN_THE_SET {
227                @Override
228                public boolean removeNeeded(JetType subject, JetType other) {
229                    return JetTypeChecker.DEFAULT.isSubtypeOf(subject, other);
230                }
231            };
232    
233            private void proceed(Set<JetType> conflictingTypes) {
234                for (Iterator<JetType> iterator = conflictingTypes.iterator(); iterator.hasNext(); ) {
235                    JetType type = iterator.next();
236                    for (JetType otherType : conflictingTypes) {
237                        boolean subtypeOf = removeNeeded(type, otherType);
238                        if (type != otherType && subtypeOf) {
239                            iterator.remove();
240                            break;
241                        }
242                    }
243                }
244            }
245    
246            public abstract boolean removeNeeded(JetType subject, JetType other);
247        }
248    
249        private void checkObject(JetObjectDeclaration declaration) {
250            reportErrorIfHasIllegalModifier(declaration);
251        }
252    
253        private void checkClass(BodiesResolveContext c, JetClass aClass, ClassDescriptorWithResolutionScopes classDescriptor) {
254            checkOpenMembers(classDescriptor);
255            if (c.getTopDownAnalysisParameters().isLazyTopDownAnalysis()) {
256                checkTypeParameters(aClass);
257            }
258            if (aClass.isTrait()) {
259                checkTraitModifiers(aClass);
260                checkConstructorInTrait(aClass);
261            }
262            else if (aClass.isAnnotation()) {
263                checkAnnotationClassWithBody(aClass);
264                checkValOnAnnotationParameter(aClass);
265            }
266            else if (aClass.isEnum()) {
267                checkEnumModifiers(aClass);
268            }
269            else if (aClass instanceof JetEnumEntry) {
270                checkEnumEntry((JetEnumEntry) aClass, classDescriptor);
271            }
272        }
273    
274        private void checkTypeParameters(JetTypeParameterListOwner typeParameterListOwner) {
275            // TODO: Support annotation for type parameters
276            for (JetTypeParameter jetTypeParameter : typeParameterListOwner.getTypeParameters()) {
277                AnnotationResolver.reportUnsupportedAnnotationForTypeParameter(jetTypeParameter, trace);
278    
279                TypeParameterDescriptor typeParameter = trace.get(TYPE_PARAMETER, jetTypeParameter);
280                if (typeParameter != null) {
281                    DescriptorResolver.checkConflictingUpperBounds(trace, typeParameter, jetTypeParameter);
282                }
283            }
284        }
285    
286        private void checkConstructorInTrait(JetClass klass) {
287            JetParameterList primaryConstructorParameterList = klass.getPrimaryConstructorParameterList();
288            if (primaryConstructorParameterList != null) {
289                trace.report(CONSTRUCTOR_IN_TRAIT.on(primaryConstructorParameterList));
290            }
291        }
292    
293        private void checkTraitModifiers(JetClass aClass) {
294            reportErrorIfHasIllegalModifier(aClass);
295            JetModifierList modifierList = aClass.getModifierList();
296            if (modifierList == null) return;
297            if (modifierList.hasModifier(JetTokens.FINAL_KEYWORD)) {
298                trace.report(Errors.TRAIT_CAN_NOT_BE_FINAL.on(modifierList.getModifierNode(JetTokens.FINAL_KEYWORD).getPsi()));
299            }
300            if (modifierList.hasModifier(JetTokens.ABSTRACT_KEYWORD)) {
301                trace.report(Errors.ABSTRACT_MODIFIER_IN_TRAIT.on(aClass));
302            }
303            if (modifierList.hasModifier(JetTokens.OPEN_KEYWORD)) {
304                trace.report(Errors.OPEN_MODIFIER_IN_TRAIT.on(aClass));
305            }
306        }
307    
308        private void checkAnnotationClassWithBody(JetClassOrObject classOrObject) {
309            if (classOrObject.getBody() != null) {
310                trace.report(ANNOTATION_CLASS_WITH_BODY.on(classOrObject.getBody()));
311            }
312        }
313    
314        private void checkValOnAnnotationParameter(JetClass aClass) {
315            for (JetParameter parameter : aClass.getPrimaryConstructorParameters()) {
316                if (!parameter.hasValOrVarNode()) {
317                    trace.report(MISSING_VAL_ON_ANNOTATION_PARAMETER.on(parameter));
318                }
319            }
320        }
321    
322        private void checkOpenMembers(ClassDescriptorWithResolutionScopes classDescriptor) {
323            for (CallableMemberDescriptor memberDescriptor : classDescriptor.getDeclaredCallableMembers()) {
324                if (memberDescriptor.getKind() != CallableMemberDescriptor.Kind.DECLARATION) continue;
325                JetNamedDeclaration member = (JetNamedDeclaration) BindingContextUtils.descriptorToDeclaration(trace.getBindingContext(), memberDescriptor);
326                if (member != null && classDescriptor.getModality() == Modality.FINAL && member.hasModifier(JetTokens.OPEN_KEYWORD)) {
327                    trace.report(NON_FINAL_MEMBER_IN_FINAL_CLASS.on(member));
328                }
329            }
330        }
331    
332        private void checkProperty(JetProperty property, PropertyDescriptor propertyDescriptor) {
333            reportErrorIfHasIllegalModifier(property);
334            DeclarationDescriptor containingDeclaration = propertyDescriptor.getContainingDeclaration();
335            if (containingDeclaration instanceof ClassDescriptor) {
336                checkPropertyAbstractness(property, propertyDescriptor, (ClassDescriptor) containingDeclaration);
337            }
338            else {
339                modifiersChecker.checkIllegalModalityModifiers(property);
340            }
341            checkPropertyInitializer(property, propertyDescriptor);
342            checkAccessors(property, propertyDescriptor);
343            checkDeclaredTypeInPublicMember(property, propertyDescriptor);
344        }
345    
346        private void checkDeclaredTypeInPublicMember(JetNamedDeclaration member, CallableMemberDescriptor memberDescriptor) {
347            boolean hasDeferredType;
348            if (member instanceof JetProperty) {
349                hasDeferredType = ((JetProperty) member).getTypeRef() == null && DescriptorResolver.hasBody((JetProperty) member);
350            }
351            else {
352                assert member instanceof JetFunction;
353                JetFunction function = (JetFunction) member;
354                hasDeferredType = function.getReturnTypeRef() == null && function.hasBody() && !function.hasBlockBody();
355            }
356            if ((memberDescriptor.getVisibility().isPublicAPI()) && memberDescriptor.getOverriddenDescriptors().size() == 0 && hasDeferredType) {
357                trace.report(PUBLIC_MEMBER_SHOULD_SPECIFY_TYPE.on(member));
358            }
359        }
360    
361        private void checkPropertyAbstractness(
362                @NotNull JetProperty property,
363                @NotNull PropertyDescriptor propertyDescriptor,
364                @NotNull ClassDescriptor classDescriptor
365        ) {
366            JetPropertyAccessor getter = property.getGetter();
367            JetPropertyAccessor setter = property.getSetter();
368            JetModifierList modifierList = property.getModifierList();
369            ASTNode abstractNode = modifierList != null ? modifierList.getModifierNode(JetTokens.ABSTRACT_KEYWORD) : null;
370    
371            if (abstractNode != null) { //has abstract modifier
372                if (!(classDescriptor.getModality() == Modality.ABSTRACT) && classDescriptor.getKind() != ClassKind.ENUM_CLASS) {
373                    String name = property.getName();
374                    trace.report(ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS.on(property, name != null ? name : "", classDescriptor));
375                    return;
376                }
377                if (classDescriptor.getKind() == ClassKind.TRAIT) {
378                    trace.report(ABSTRACT_MODIFIER_IN_TRAIT.on(property));
379                }
380            }
381    
382            if (propertyDescriptor.getModality() == Modality.ABSTRACT) {
383                JetType returnType = propertyDescriptor.getReturnType();
384                if (returnType instanceof DeferredType) {
385                    returnType = ((DeferredType) returnType).getDelegate();
386                }
387    
388                JetExpression initializer = property.getInitializer();
389                if (initializer != null) {
390                    trace.report(ABSTRACT_PROPERTY_WITH_INITIALIZER.on(initializer));
391                }
392                JetPropertyDelegate delegate = property.getDelegate();
393                if (delegate != null) {
394                    trace.report(ABSTRACT_DELEGATED_PROPERTY.on(delegate));
395                }
396                if (getter != null && getter.hasBody()) {
397                    trace.report(ABSTRACT_PROPERTY_WITH_GETTER.on(getter));
398                }
399                if (setter != null && setter.hasBody()) {
400                    trace.report(ABSTRACT_PROPERTY_WITH_SETTER.on(setter));
401                }
402            }
403        }
404    
405        private void checkPropertyInitializer(@NotNull JetProperty property, @NotNull PropertyDescriptor propertyDescriptor) {
406            JetPropertyAccessor getter = property.getGetter();
407            JetPropertyAccessor setter = property.getSetter();
408            boolean hasAccessorImplementation = (getter != null && getter.hasBody()) ||
409                                                (setter != null && setter.hasBody());
410    
411            if (propertyDescriptor.getModality() == Modality.ABSTRACT) {
412                if (!property.hasDelegateExpressionOrInitializer() && property.getTypeRef() == null) {
413                    trace.report(PROPERTY_WITH_NO_TYPE_NO_INITIALIZER.on(property));
414                }
415                return;
416            }
417            DeclarationDescriptor containingDeclaration = propertyDescriptor.getContainingDeclaration();
418            boolean inTrait = containingDeclaration instanceof ClassDescriptor && ((ClassDescriptor)containingDeclaration).getKind() == ClassKind.TRAIT;
419            JetExpression initializer = property.getInitializer();
420            JetPropertyDelegate delegate = property.getDelegate();
421            boolean backingFieldRequired =
422                    Boolean.TRUE.equals(trace.getBindingContext().get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor));
423    
424            if (inTrait && backingFieldRequired && hasAccessorImplementation) {
425                trace.report(BACKING_FIELD_IN_TRAIT.on(property));
426            }
427    
428            if (initializer == null && delegate == null) {
429                boolean error = false;
430                if (backingFieldRequired && !inTrait &&
431                    Boolean.FALSE.equals(trace.getBindingContext().get(BindingContext.IS_INITIALIZED, propertyDescriptor))) {
432                    if (!(containingDeclaration instanceof ClassDescriptor) || hasAccessorImplementation) {
433                        error = true;
434                        trace.report(MUST_BE_INITIALIZED.on(property));
435                    }
436                    else {
437                        error = true;
438                        trace.report(MUST_BE_INITIALIZED_OR_BE_ABSTRACT.on(property));
439                    }
440                }
441                if (!error && property.getTypeRef() == null) {
442                    trace.report(PROPERTY_WITH_NO_TYPE_NO_INITIALIZER.on(property));
443                }
444                if (inTrait && property.hasModifier(JetTokens.FINAL_KEYWORD) && backingFieldRequired) {
445                    trace.report(FINAL_PROPERTY_IN_TRAIT.on(property));
446                }
447                return;
448            }
449    
450            if (inTrait) {
451                if (delegate != null) {
452                    trace.report(DELEGATED_PROPERTY_IN_TRAIT.on(delegate));
453                }
454                else {
455                    trace.report(PROPERTY_INITIALIZER_IN_TRAIT.on(initializer));
456                }
457            }
458            else if (delegate == null) {
459                if (!backingFieldRequired) {
460                    trace.report(PROPERTY_INITIALIZER_NO_BACKING_FIELD.on(initializer));
461                }
462                else if (property.getReceiverTypeRef() != null) {
463                    trace.report(EXTENSION_PROPERTY_WITH_BACKING_FIELD.on(initializer));
464                }
465            }
466        }
467    
468        protected void checkFunction(JetNamedFunction function, SimpleFunctionDescriptor functionDescriptor) {
469            reportErrorIfHasIllegalModifier(function);
470            DeclarationDescriptor containingDescriptor = functionDescriptor.getContainingDeclaration();
471            boolean hasAbstractModifier = function.hasModifier(JetTokens.ABSTRACT_KEYWORD);
472            checkDeclaredTypeInPublicMember(function, functionDescriptor);
473            if (containingDescriptor instanceof ClassDescriptor) {
474                ClassDescriptor classDescriptor = (ClassDescriptor) containingDescriptor;
475                boolean inTrait = classDescriptor.getKind() == ClassKind.TRAIT;
476                boolean inEnum = classDescriptor.getKind() == ClassKind.ENUM_CLASS;
477                boolean inAbstractClass = classDescriptor.getModality() == Modality.ABSTRACT;
478                if (hasAbstractModifier && !inAbstractClass && !inEnum) {
479                    trace.report(ABSTRACT_FUNCTION_IN_NON_ABSTRACT_CLASS.on(function, functionDescriptor.getName().asString(), classDescriptor));
480                }
481                if (hasAbstractModifier && inTrait) {
482                    trace.report(ABSTRACT_MODIFIER_IN_TRAIT.on(function));
483                }
484                boolean hasBody = function.hasBody();
485                if (hasBody && hasAbstractModifier) {
486                    trace.report(ABSTRACT_FUNCTION_WITH_BODY.on(function, functionDescriptor));
487                }
488                if (!hasBody && function.hasModifier(JetTokens.FINAL_KEYWORD) && inTrait) {
489                    trace.report(FINAL_FUNCTION_WITH_NO_BODY.on(function, functionDescriptor));
490                }
491                if (!hasBody && !hasAbstractModifier && !inTrait) {
492                    trace.report(NON_ABSTRACT_FUNCTION_WITH_NO_BODY.on(function, functionDescriptor));
493                }
494                return;
495            }
496            modifiersChecker.checkIllegalModalityModifiers(function);
497            if (!function.hasBody() && !hasAbstractModifier) {
498                trace.report(NON_MEMBER_FUNCTION_NO_BODY.on(function, functionDescriptor));
499            }
500        }
501    
502        private void checkAccessors(@NotNull JetProperty property, @NotNull PropertyDescriptor propertyDescriptor) {
503            for (JetPropertyAccessor accessor : property.getAccessors()) {
504                modifiersChecker.checkIllegalModalityModifiers(accessor);
505            }
506            JetPropertyAccessor getter = property.getGetter();
507            PropertyGetterDescriptor getterDescriptor = propertyDescriptor.getGetter();
508            JetModifierList getterModifierList = getter != null ? getter.getModifierList() : null;
509            if (getterModifierList != null && getterDescriptor != null) {
510                Map<JetModifierKeywordToken, ASTNode> nodes = ModifiersChecker.getNodesCorrespondingToModifiers(getterModifierList, Sets
511                        .newHashSet(JetTokens.PUBLIC_KEYWORD, JetTokens.PROTECTED_KEYWORD, JetTokens.PRIVATE_KEYWORD,
512                                    JetTokens.INTERNAL_KEYWORD));
513                if (getterDescriptor.getVisibility() != propertyDescriptor.getVisibility()) {
514                    for (ASTNode node : nodes.values()) {
515                        trace.report(Errors.GETTER_VISIBILITY_DIFFERS_FROM_PROPERTY_VISIBILITY.on(node.getPsi()));
516                    }
517                }
518                else {
519                    for (ASTNode node : nodes.values()) {
520                        trace.report(Errors.REDUNDANT_MODIFIER_IN_GETTER.on(node.getPsi()));
521                    }
522                }
523            }
524        }
525    
526        private void checkEnumModifiers(JetClass aClass) {
527            if (aClass.hasModifier(JetTokens.OPEN_KEYWORD)) {
528                trace.report(OPEN_MODIFIER_IN_ENUM.on(aClass));
529            }
530        }
531    
532        private void checkEnumEntry(@NotNull JetEnumEntry enumEntry, @NotNull ClassDescriptor classDescriptor) {
533            DeclarationDescriptor declaration = classDescriptor.getContainingDeclaration();
534            assert DescriptorUtils.isEnumClass(declaration) : "Enum entry should be declared in enum class: " + classDescriptor;
535            ClassDescriptor enumClass = (ClassDescriptor) declaration;
536    
537            List<JetDelegationSpecifier> delegationSpecifiers = enumEntry.getDelegationSpecifiers();
538            ConstructorDescriptor constructor = enumClass.getUnsubstitutedPrimaryConstructor();
539            assert constructor != null;
540            if (!constructor.getValueParameters().isEmpty() && delegationSpecifiers.isEmpty()) {
541                trace.report(ENUM_ENTRY_SHOULD_BE_INITIALIZED.on(enumEntry, enumClass));
542            }
543    
544            for (JetDelegationSpecifier delegationSpecifier : delegationSpecifiers) {
545                JetTypeReference typeReference = delegationSpecifier.getTypeReference();
546                if (typeReference != null) {
547                    JetType type = trace.getBindingContext().get(TYPE, typeReference);
548                    if (type != null) {
549                        JetType enumType = enumClass.getDefaultType();
550                        if (!type.getConstructor().equals(enumType.getConstructor())) {
551                            trace.report(ENUM_ENTRY_ILLEGAL_TYPE.on(typeReference, enumClass));
552                        }
553                    }
554                }
555            }
556        }
557    }