Modifiers checks were moved to DeclarationsChecker; added 'open' modifier checks

This commit is contained in:
svtk
2011-09-29 13:34:41 +04:00
parent 7b2c5132f7
commit 225913126b
24 changed files with 460 additions and 334 deletions
@@ -27,8 +27,8 @@ public abstract class DiagnosticFactoryWithPsiElement1<T extends PsiElement, A>
}
@NotNull
public DiagnosticWithPsiElement<T> on(@NotNull T element, @NotNull A argument) {
return on(element, element.getTextRange(), argument);
public DiagnosticWithPsiElement<T> on(@NotNull T elementToMark, @NotNull A argument) {
return on(elementToMark, elementToMark.getTextRange(), argument);
}
@NotNull
@@ -31,8 +31,8 @@ public abstract class DiagnosticFactoryWithPsiElement2<T extends PsiElement, A,
}
@NotNull
public DiagnosticWithPsiElement<T> on(@NotNull T element, @NotNull A a, @NotNull B b) {
return on(element, element.getNode(), a, b);
public DiagnosticWithPsiElement<T> on(@NotNull T elementToMark, @NotNull A a, @NotNull B b) {
return on(elementToMark, elementToMark.getNode(), a, b);
}
@NotNull
@@ -34,8 +34,8 @@ public abstract class DiagnosticFactoryWithPsiElement3<T extends PsiElement, A,
}
@NotNull
public DiagnosticWithPsiElement<T> on(@NotNull T element, @NotNull A a, @NotNull B b, @NotNull C c) {
return on(element, element, a, b, c);
public DiagnosticWithPsiElement<T> on(@NotNull T elementToMark, @NotNull A a, @NotNull B b, @NotNull C c) {
return on(elementToMark, elementToMark, a, b, c);
}
@NotNull
@@ -1,6 +1,7 @@
package org.jetbrains.jet.lang.diagnostics;
import org.jetbrains.jet.lang.psi.JetClass;
import org.jetbrains.jet.lang.psi.JetProperty;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lexer.JetKeywordToken;
@@ -11,4 +12,5 @@ public interface DiagnosticParameters {
DiagnosticParameter<JetKeywordToken> MODIFIER = new DiagnosticParameterImpl<JetKeywordToken>("MODIFIER");
DiagnosticParameter<JetClass> CLASS = new DiagnosticParameterImpl<JetClass>("CLASS");
DiagnosticParameter<JetType> TYPE = new DiagnosticParameterImpl<JetType>("TYPE");
DiagnosticParameter<JetProperty> PROPERTY = new DiagnosticParameterImpl<JetProperty>("TYPE");
}
@@ -59,6 +59,7 @@ public interface Errors {
return sb.toString();
}
};
ParameterizedDiagnosticFactory1<JetKeywordToken> ILLEGAL_MODIFIER = ParameterizedDiagnosticFactory1.create(ERROR, "Illegal modifier ''{0}''");
PsiElementOnlyDiagnosticFactory2<JetModifierList, JetKeywordToken, JetKeywordToken> REDUNDANT_MODIFIER = new PsiElementOnlyDiagnosticFactory2<JetModifierList, JetKeywordToken, JetKeywordToken>(Severity.WARNING, "Modifier {0} is redundant because {1} is present") {
@NotNull
@@ -67,6 +68,8 @@ public interface Errors {
return super.on(elementToBlame, nodeToMark, redundantModifier, presentModifier).add(DiagnosticParameters.MODIFIER, redundantModifier);
}
};
DiagnosticWithParameterFactory<JetModifierList, JetKeywordToken> REDUNDANT_MODIFIER_IN_TRAIT = DiagnosticWithParameterFactory.create(WARNING, "Modifier ''{0}'' is redundant in trait", DiagnosticParameters.MODIFIER);
SimplePsiElementOnlyDiagnosticFactory<JetClass> TRAIT_CAN_NOT_BE_FINAL = SimplePsiElementOnlyDiagnosticFactory.create(ERROR, "Trait can not be final");
SimpleDiagnosticFactory SAFE_CALLS_ARE_NOT_ALLOWED_ON_NAMESPACES = SimpleDiagnosticFactory.create(ERROR, "Safe calls are not allowed on namespaces");
SimpleDiagnosticFactory TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM = SimpleDiagnosticFactory.create(ERROR, "Type checking has run into a recursive problem"); // TODO: message
SimpleDiagnosticFactory RETURN_NOT_ALLOWED = SimpleDiagnosticFactory.create(ERROR, "'return' is not allowed here");
@@ -116,6 +119,9 @@ public interface Errors {
SimplePsiElementOnlyDiagnosticFactory<JetModifierListOwner> NON_MEMBER_ABSTRACT_ACCESSOR = SimplePsiElementOnlyDiagnosticFactory.create(ERROR, "This property is not a class or trait member and thus cannot have abstract accessors"); // TODO : Better message
PsiElementOnlyDiagnosticFactory1<JetFunctionOrPropertyAccessor, FunctionDescriptor> NON_MEMBER_FUNCTION_NO_BODY = PsiElementOnlyDiagnosticFactory1.create(ERROR, "Function {0} must have a body");
DiagnosticWithParameterFactory<JetNamedDeclaration, JetClass> NON_FINAL_MEMBER_IN_FINAL_CLASS = DiagnosticWithParameterFactory.create(ERROR, "Non final member in a final class", DiagnosticParameters.CLASS);
DiagnosticWithParameterFactory<JetPropertyAccessor, JetProperty> NON_FINAL_ACCESSOR_OF_FINAL_PROPERTY = DiagnosticWithParameterFactory.create(ERROR, "Non final accessor of a final property", DiagnosticParameters.PROPERTY);
SimpleDiagnosticFactory PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT = SimpleDiagnosticFactory.create(ERROR, "Projections are not allowed on type arguments of functions and properties"); // TODO : better positioning
SimpleDiagnosticFactory SUPERTYPE_NOT_INITIALIZED = SimpleDiagnosticFactory.create(ERROR, "This type has a constructor, and thus must be initialized here");
@@ -27,12 +27,12 @@ public class ParameterizedDiagnosticFactory1<A> extends DiagnosticFactoryWithPsi
}
@NotNull
public Diagnostic on(@NotNull PsiFile psiFile, @NotNull TextRange range, @NotNull A argument) {
return new GenericDiagnostic(this, severity, makeMessage(argument), psiFile, range);
public Diagnostic on(@NotNull PsiFile psiFile, @NotNull TextRange rangeToMark, @NotNull A argument) {
return new GenericDiagnostic(this, severity, makeMessage(argument), psiFile, rangeToMark);
}
@NotNull
public Diagnostic on(@NotNull ASTNode node, @NotNull A argument) {
return on(DiagnosticUtils.getContainingFile(node), node.getTextRange(), argument);
public Diagnostic on(@NotNull ASTNode nodeToMark, @NotNull A argument) {
return on(DiagnosticUtils.getContainingFile(nodeToMark), nodeToMark.getTextRange(), argument);
}
}
@@ -27,13 +27,13 @@ public class ParameterizedDiagnosticFactory2<A, B> extends DiagnosticFactoryWith
}
@NotNull
public Diagnostic on(@NotNull PsiFile psiFile, @NotNull TextRange range, @NotNull A a, @NotNull B b) {
return new GenericDiagnostic(this, severity, makeMessage(a, b), psiFile, range);
public Diagnostic on(@NotNull PsiFile psiFile, @NotNull TextRange rangeToMark, @NotNull A a, @NotNull B b) {
return new GenericDiagnostic(this, severity, makeMessage(a, b), psiFile, rangeToMark);
}
@NotNull
public Diagnostic on(@NotNull ASTNode node, @NotNull A a, @NotNull B b) {
return on(DiagnosticUtils.getContainingFile(node), node.getTextRange(), a, b);
public Diagnostic on(@NotNull ASTNode nodeToMark, @NotNull A a, @NotNull B b) {
return on(DiagnosticUtils.getContainingFile(nodeToMark), nodeToMark.getTextRange(), a, b);
}
}
@@ -27,12 +27,12 @@ public class ParameterizedDiagnosticFactory3<A, B, C> extends DiagnosticFactoryW
}
@NotNull
public Diagnostic on(@NotNull PsiFile psiFile, @NotNull TextRange range, @NotNull A a, @NotNull B b, @NotNull C c) {
return new GenericDiagnostic(this, severity, makeMessage(a, b, c), psiFile, range);
public Diagnostic on(@NotNull PsiFile psiFile, @NotNull TextRange rangeToMark, @NotNull A a, @NotNull B b, @NotNull C c) {
return new GenericDiagnostic(this, severity, makeMessage(a, b, c), psiFile, rangeToMark);
}
@NotNull
public Diagnostic on(@NotNull ASTNode node, @NotNull A a, @NotNull B b, @NotNull C c) {
return on(DiagnosticUtils.getContainingFile(node), node.getTextRange(), a, b, c);
public Diagnostic on(@NotNull ASTNode nodeToMark, @NotNull A a, @NotNull B b, @NotNull C c) {
return on(DiagnosticUtils.getContainingFile(nodeToMark), nodeToMark.getTextRange(), a, b, c);
}
}
@@ -3,10 +3,8 @@ package org.jetbrains.jet.lang.resolve;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.cfg.JetFlowInformationProvider;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.*;
@@ -18,14 +16,17 @@ import org.jetbrains.jet.lang.types.*;
import org.jetbrains.jet.lexer.JetTokens;
import org.jetbrains.jet.util.slicedmap.WritableSlice;
import java.util.*;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.jetbrains.jet.lang.diagnostics.Errors.*;
import static org.jetbrains.jet.lang.types.JetTypeInferrer.NO_EXPECTED_TYPE;
/**
* @author abreslav
*/
* @author abreslav
*/
public class BodyResolver {
private final TopDownAnalysisContext context;
@@ -43,7 +44,6 @@ public class BodyResolver {
JetSimpleNameExpression simpleNameExpression = (JetSimpleNameExpression) expression;
if (simpleNameExpression.getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER) {
if (!BodyResolver.this.context.getTrace().getBindingContext().get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) descriptor)) {
// BodyResolver.this.context.getTrace().getErrorHandler().genericError(expression.getNode(), "This property does not have a backing field");
BodyResolver.this.context.getTrace().report(NO_BACKING_FIELD.on(expression));
}
}
@@ -68,7 +68,6 @@ public class BodyResolver {
public void resolveBehaviorDeclarationBodies() {
// bindOverrides();
resolveDelegationSpecifierLists();
resolveClassAnnotations();
@@ -79,30 +78,9 @@ public class BodyResolver {
resolveSecondaryConstructorBodies();
resolveFunctionBodies();
checkIfPrimaryConstructorIsNecessary();
// checkOverrides();
}
private void checkIfPrimaryConstructorIsNecessary() {
for (Map.Entry<JetClass, MutableClassDescriptor> entry : context.getClasses().entrySet()) {
MutableClassDescriptor classDescriptor = entry.getValue();
JetClass jetClass = entry.getKey();
if (classDescriptor.getUnsubstitutedPrimaryConstructor() == null && !(classDescriptor.getKind() == ClassKind.TRAIT)) {
for (PropertyDescriptor propertyDescriptor : classDescriptor.getProperties()) {
if (context.getTrace().getBindingContext().get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor)) {
PsiElement nameIdentifier = jetClass.getNameIdentifier();
if (nameIdentifier != null) {
// context.getTrace().getErrorHandler().genericError(nameIdentifier.getNode(),
// "This class must have a primary constructor, because property " + propertyDescriptor.getName() + " has a backing field");
context.getTrace().report(PRIMARY_CONSTRUCTOR_MISSING_STATEFUL_PROPERTY.on(jetClass, nameIdentifier, propertyDescriptor));
}
break;
}
}
}
}
}
private void resolveDelegationSpecifierLists() {
// TODO : Make sure the same thing is not initialized twice
@@ -117,8 +95,8 @@ public class BodyResolver {
private void resolveDelegationSpecifierList(final JetClassOrObject jetClass, final MutableClassDescriptor descriptor) {
final ConstructorDescriptor primaryConstructor = descriptor.getUnsubstitutedPrimaryConstructor();
final JetScope scopeForConstructor = primaryConstructor == null
? null
: getInnerScopeForConstructor(primaryConstructor, descriptor.getScopeForMemberResolution(), true);
? null
: getInnerScopeForConstructor(primaryConstructor, descriptor.getScopeForMemberResolution(), true);
final JetTypeInferrer.Services typeInferrer = context.getSemanticServices().getTypeInferrerServices(traceForConstructors, JetFlowInformationProvider.NONE); // TODO : flow
final Map<JetTypeReference, JetType> supertypes = Maps.newLinkedHashMap();
@@ -131,7 +109,6 @@ public class BodyResolver {
@Override
public void visitDelegationByExpressionSpecifier(JetDelegatorByExpressionSpecifier specifier) {
if (descriptor.getKind() == ClassKind.TRAIT) {
// context.getTrace().getErrorHandler().genericError(specifier.getNode(), "Traits cannot use delegation");
context.getTrace().report(DELEGATION_IN_TRAIT.on(specifier));
}
JetType supertype = context.getTrace().getBindingContext().get(BindingContext.TYPE, specifier.getTypeReference());
@@ -162,7 +139,6 @@ public class BodyResolver {
JetValueArgumentList valueArgumentList = call.getValueArgumentList();
ASTNode node = valueArgumentList == null ? call.getNode() : valueArgumentList.getNode();
if (descriptor.getKind() == ClassKind.TRAIT) {
// context.getTrace().getErrorHandler().genericError(node, "Traits cannot initialize supertypes");
context.getTrace().report(SUPERTYPE_INITIALIZED_IN_TRAIT.on(node));
}
JetTypeReference typeReference = call.getTypeReference();
@@ -174,7 +150,6 @@ public class BodyResolver {
ClassDescriptor classDescriptor = TypeUtils.getClassDescriptor(supertype);
if (classDescriptor != null) {
if (classDescriptor.getKind() == ClassKind.TRAIT) {
// context.getTrace().getErrorHandler().genericError(node, "A trait may not have a constructor");
context.getTrace().report(CONSTRUCTOR_IN_TRAIT.on(node));
}
}
@@ -188,8 +163,6 @@ public class BodyResolver {
recordSupertype(typeReference, supertype);
assert valueArgumentList != null;
// context.getTrace().getErrorHandler().genericError(valueArgumentList.getNode(),
// "Class " + JetPsiUtil.safeName(jetClass.getName()) + " must have a constructor in order to be able to initialize supertypes");
context.getTrace().report(PRIMARY_CONSTRUCTOR_MISSING_SUPER_CONSTRUCTOR_CALL.on(valueArgumentList, jetClass));
}
}
@@ -205,7 +178,6 @@ public class BodyResolver {
if (classDescriptor != null) {
if (descriptor.getKind() != ClassKind.TRAIT) {
if (classDescriptor.hasConstructors() && !ErrorUtils.isError(classDescriptor.getTypeConstructor()) && classDescriptor.getKind() != ClassKind.TRAIT) {
// context.getTrace().getErrorHandler().genericError(specifier.getNode(), "This type has a constructor, and thus must be initialized here");
context.getTrace().report(SUPERTYPE_NOT_INITIALIZED.on(specifier));
}
}
@@ -249,7 +221,6 @@ public class BodyResolver {
if (classDescriptor != null) {
if (classDescriptor.getKind() != ClassKind.TRAIT) {
if (classAppeared) {
// context.getTrace().getErrorHandler().genericError(typeReference.getNode(), "Only one class may appear in a supertype list");
context.getTrace().report(MANY_CLASSES_IN_SUPERTYPE_LIST.on(typeReference));
}
else {
@@ -258,18 +229,15 @@ public class BodyResolver {
}
}
else {
// context.getTrace().getErrorHandler().genericError(typeReference.getNode(), "Only classes and traits may serve as supertypes");
context.getTrace().report(SUPERTYPE_NOT_A_CLASS_OR_TRAIT.on(typeReference));
}
TypeConstructor constructor = supertype.getConstructor();
if (!typeConstructors.add(constructor)) {
// context.getTrace().getErrorHandler().genericError(typeReference.getNode(), "A supertype appears twice");
context.getTrace().report(SUPERTYPE_APPEARS_TWICE.on(typeReference));
}
if (constructor.isSealed() && !allowedFinalSupertypes.contains(constructor)) {
// context.getTrace().getErrorHandler().genericError(typeReference.getNode(), "This type is final, so it cannot be inherited from");
context.getTrace().report(FINAL_SUPERTYPE.on(typeReference));
}
}
@@ -301,7 +269,6 @@ public class BodyResolver {
}
else {
for (JetClassInitializer anonymousInitializer : anonymousInitializers) {
// context.getTrace().getErrorHandler().genericError(anonymousInitializer.getNode(), "Anonymous initializers are only allowed in the presence of a primary constructor");
context.getTrace().report(ANONYMOUS_INITIALIZER_WITHOUT_CONSTRUCTOR.on(anonymousInitializer));
}
}
@@ -326,13 +293,11 @@ public class BodyResolver {
JetClass containingClass = PsiTreeUtil.getParentOfType(declaration, JetClass.class);
assert containingClass != null : "This must be guaranteed by the parser";
if (!containingClass.hasPrimaryConstructor()) {
// context.getTrace().getErrorHandler().genericError(declaration.getNameNode(), "A secondary constructor may appear only in a class that has a primary constructor");
context.getTrace().report(SECONDARY_CONSTRUCTOR_BUT_NO_PRIMARY.on(declaration.getNameNode()));
}
else {
List<JetDelegationSpecifier> initializers = declaration.getInitializers();
if (initializers.isEmpty()) {
// context.getTrace().getErrorHandler().genericError(declaration.getNameNode(), "Secondary constructors must have an initializer list");
context.getTrace().report(SECONDARY_CONSTRUCTOR_NO_INITIALIZER_LIST.on(declaration.getNameNode()));
}
else {
@@ -352,8 +317,8 @@ public class BodyResolver {
ClassDescriptor classDescriptor = descriptor.getContainingDeclaration();
typeInferrerForInitializers.getCallResolver().resolveCall(context.getTrace(),
functionInnerScope,
ReceiverDescriptor.NO_RECEIVER, call, NO_EXPECTED_TYPE);
functionInnerScope,
ReceiverDescriptor.NO_RECEIVER, call, NO_EXPECTED_TYPE);
// call.getThisReference(),
// classDescriptor,
// classDescriptor.getDefaultType(),
@@ -363,13 +328,11 @@ public class BodyResolver {
@Override
public void visitDelegationByExpressionSpecifier(JetDelegatorByExpressionSpecifier specifier) {
// context.getTrace().getErrorHandler().genericError(specifier.getNode(), "'by'-clause is only supported for primary constructors");
context.getTrace().report(BY_IN_SECONDARY_CONSTRUCTOR.on(specifier));
}
@Override
public void visitDelegationToSuperClassSpecifier(JetDelegatorToSuperClass specifier) {
// context.getTrace().getErrorHandler().genericError(specifier.getNode(), "Constructor parameters required");
context.getTrace().report(INITIALIZER_WITH_NO_ARGUMENTS.on(specifier));
}
@@ -380,7 +343,6 @@ public class BodyResolver {
});
for (int i = 1, initializersSize = initializers.size(); i < initializersSize; i++) {
JetDelegationSpecifier initializer = initializers.get(i);
// context.getTrace().getErrorHandler().genericError(initializer.getNode(), "Only one call to 'this(...)' is allowed");
context.getTrace().report(MANY_CALLS_TO_THIS.on(initializer));
}
}
@@ -440,7 +402,6 @@ public class BodyResolver {
}
resolvePropertyAccessors(property, propertyDescriptor, declaringScope);
checkProperty(property, propertyDescriptor, classDescriptor);
processed.add(property);
}
}
@@ -459,7 +420,6 @@ public class BodyResolver {
}
resolvePropertyAccessors(property, propertyDescriptor, declaringScope);
checkProperty(property, propertyDescriptor, null);
}
}
@@ -494,109 +454,6 @@ public class BodyResolver {
}
}
protected void checkProperty(JetProperty property, PropertyDescriptor propertyDescriptor, @Nullable ClassDescriptor classDescriptor) {
checkPropertyAbstractness(property, propertyDescriptor, classDescriptor);
checkPropertyInitializer(property, propertyDescriptor, classDescriptor);
}
private void checkPropertyAbstractness(JetProperty property, PropertyDescriptor propertyDescriptor, ClassDescriptor classDescriptor) {
JetPropertyAccessor getter = property.getGetter();
JetPropertyAccessor setter = property.getSetter();
JetModifierList modifierList = property.getModifierList();
ASTNode abstractNode = modifierList != null ? modifierList.getModifierNode(JetTokens.ABSTRACT_KEYWORD) : null;
if (abstractNode != null) { //has abstract modifier
if (classDescriptor == null) {
// context.getTrace().getErrorHandler().genericError(abstractNode, "A property may be abstract only when defined in a class or trait");
context.getTrace().report(ABSTRACT_PROPERTY_NOT_IN_CLASS.on(property, abstractNode));
return;
}
if (!(classDescriptor.getModality() == Modality.ABSTRACT) && classDescriptor.getKind() != ClassKind.ENUM_CLASS) {
// context.getTrace().getErrorHandler().genericError(abstractNode, "Abstract property " + property.getName() + " in non-abstract class " + classDescriptor.getName());
PsiElement classElement = context.getTrace().get(BindingContext.DESCRIPTOR_TO_DECLARATION, classDescriptor);
assert classElement instanceof JetClass;
context.getTrace().report(ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS.on(property, abstractNode, property.getName(), classDescriptor, (JetClass) classElement));
return;
}
if (classDescriptor.getKind() == ClassKind.TRAIT) {
// context.getTrace().getErrorHandler().genericWarning(abstractNode, "Abstract modifier is redundant in traits");
context.getTrace().report(REDUNDANT_ABSTRACT.on(property, abstractNode));
}
}
if (propertyDescriptor.getModality() == Modality.ABSTRACT) {
JetType returnType = propertyDescriptor.getReturnType();
if (returnType instanceof DeferredType) {
returnType = ((DeferredType) returnType).getActualType();
}
JetExpression initializer = property.getInitializer();
if (initializer != null) {
// context.getTrace().getErrorHandler().genericError(initializer.getNode(), "Property with initializer cannot be abstract");
context.getTrace().report(ABSTRACT_PROPERTY_WITH_INITIALIZER.on(property, initializer, returnType));
}
if (getter != null && getter.getBodyExpression() != null) {
// context.getTrace().getErrorHandler().genericError(getter.getNode(), "Property with getter implementation cannot be abstract");
context.getTrace().report(ABSTRACT_PROPERTY_WITH_GETTER.on(property, getter, returnType));
}
if (setter != null && setter.getBodyExpression() != null) {
// context.getTrace().getErrorHandler().genericError(setter.getNode(), "Property with setter implementation cannot be abstract");
context.getTrace().report(ABSTRACT_PROPERTY_WITH_SETTER.on(property, setter, returnType));
}
}
}
private void checkPropertyInitializer(JetProperty property, PropertyDescriptor propertyDescriptor, ClassDescriptor classDescriptor) {
JetPropertyAccessor getter = property.getGetter();
JetPropertyAccessor setter = property.getSetter();
boolean hasAccessorImplementation = (getter != null && getter.getBodyExpression() != null) ||
(setter != null && setter.getBodyExpression() != null);
if (propertyDescriptor.getModality() == Modality.ABSTRACT) return;
boolean inTrait = classDescriptor != null && classDescriptor.getKind() == ClassKind.TRAIT;
JetExpression initializer = property.getInitializer();
boolean backingFieldRequired = context.getTrace().getBindingContext().get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor);
PsiElement nameIdentifier = property.getNameIdentifier();
ASTNode nameNode = nameIdentifier == null ? property.getNode() : nameIdentifier.getNode();
if (inTrait && backingFieldRequired && hasAccessorImplementation) {
// context.getTrace().getErrorHandler().genericError(nameNode, "Property in a trait cannot have a backing field");
context.getTrace().report(BACKING_FIELD_IN_TRAIT.on(nameNode));
}
if (initializer == null) {
if (backingFieldRequired && !inTrait && !context.getTrace().getBindingContext().get(BindingContext.IS_INITIALIZED, propertyDescriptor)) {
if (classDescriptor == null || hasAccessorImplementation) {
// context.getTrace().getErrorHandler().genericError(nameNode, "Property must be initialized");
context.getTrace().report(MUST_BE_INITIALIZED.on(nameNode));
} else {
// context.getTrace().getErrorHandler().genericError(nameNode, "Property must be initialized or be abstract");
context.getTrace().report(MUST_BE_INITIALIZED_OR_BE_ABSTRACT.on(property, nameNode));
}
}
return;
}
if (inTrait) {
// context.getTrace().getErrorHandler().genericError(initializer.getNode(), "Property initializers are not allowed in traits");
JetType returnType = propertyDescriptor.getReturnType();
if (returnType instanceof DeferredType) {
returnType = ((DeferredType) returnType).getActualType();
}
context.getTrace().report(PROPERTY_INITIALIZER_IN_TRAIT.on(property, initializer, returnType));
}
else if (!backingFieldRequired) {
// context.getTrace().getErrorHandler().genericError(initializer.getNode(), "Initializer is not allowed here because this property has no backing field");
context.getTrace().report(PROPERTY_INITIALIZER_NO_BACKING_FIELD.on(initializer));
}
else if (classDescriptor != null && classDescriptor.getUnsubstitutedPrimaryConstructor() == null) {
// context.getTrace().getErrorHandler().genericError(initializer.getNode(), "Property initializers are not allowed when no primary constructor is present");
PsiElement classElement = context.getTrace().get(BindingContext.DESCRIPTOR_TO_DECLARATION, classDescriptor);
assert classElement instanceof JetClass;
context.getTrace().report(PROPERTY_INITIALIZER_NO_PRIMARY_CONSTRUCTOR.on(property, initializer, (JetClass) classElement));
}
}
private BindingTraceAdapter createFieldTrackingTrace(final PropertyDescriptor propertyDescriptor) {
return new BindingTraceAdapter(traceForMembers).addHandler(BindingContext.REFERENCE_TARGET, new BindingTraceAdapter.RecordHandler<JetReferenceExpression, DeclarationDescriptor>() {
@Override
@@ -647,13 +504,13 @@ public class BodyResolver {
private void resolveFunctionBodies() {
for (Map.Entry<JetNamedFunction, FunctionDescriptorImpl> entry : this.context.getFunctions().entrySet()) {
JetDeclaration declaration = entry.getKey();
JetNamedFunction declaration = entry.getKey();
FunctionDescriptor descriptor = entry.getValue();
JetScope declaringScope = this.context.getDeclaringScopes().get(declaration);
assert declaringScope != null;
resolveFunctionBody(traceForMembers, (JetNamedFunction) declaration, descriptor, declaringScope);
resolveFunctionBody(traceForMembers, declaration, descriptor, declaringScope);
assert descriptor.getReturnType() != null;
}
@@ -673,60 +530,6 @@ public class BodyResolver {
typeInferrer.checkFunctionReturnType(declaringScope, function, functionDescriptor);
}
checkFunction(function, functionDescriptor);
assert functionDescriptor.getReturnType() != null;
}
protected void checkFunction(JetDeclarationWithBody function, FunctionDescriptor functionDescriptor) {
DeclarationDescriptor containingDescriptor = functionDescriptor.getContainingDeclaration();
PsiElement nameIdentifier;
boolean isPropertyAccessor = false;
if (function instanceof JetNamedFunction) {
nameIdentifier = ((JetNamedFunction) function).getNameIdentifier();
}
else if (function instanceof JetPropertyAccessor) {
isPropertyAccessor = true;
nameIdentifier = ((JetPropertyAccessor) function).getNamePlaceholder();
}
else {
throw new UnsupportedOperationException();
}
JetFunctionOrPropertyAccessor functionOrPropertyAccessor = (JetFunctionOrPropertyAccessor) function;
JetModifierList modifierList = functionOrPropertyAccessor.getModifierList();
ASTNode abstractNode = modifierList != null ? modifierList.getModifierNode(JetTokens.ABSTRACT_KEYWORD) : null;
boolean hasAbstractModifier = abstractNode != null;
if (containingDescriptor instanceof ClassDescriptor) {
ClassDescriptor classDescriptor = (ClassDescriptor) containingDescriptor;
boolean inTrait = classDescriptor.getKind() == ClassKind.TRAIT;
boolean inEnum = classDescriptor.getKind() == ClassKind.ENUM_CLASS;
boolean inAbstractClass = classDescriptor.getModality() == Modality.ABSTRACT;
if (hasAbstractModifier && !inAbstractClass && !inTrait && !inEnum) {
PsiElement classElement = context.getTrace().get(BindingContext.DESCRIPTOR_TO_DECLARATION, classDescriptor);
assert classElement instanceof JetClass;
context.getTrace().report(ABSTRACT_FUNCTION_IN_NON_ABSTRACT_CLASS.on(functionOrPropertyAccessor, abstractNode, functionDescriptor.getName(), classDescriptor, (JetClass) classElement));
}
if (hasAbstractModifier && inTrait && !isPropertyAccessor) {
context.getTrace().report(REDUNDANT_ABSTRACT.on(functionOrPropertyAccessor, abstractNode));
}
if (function.getBodyExpression() != null && hasAbstractModifier) {
context.getTrace().report(ABSTRACT_FUNCTION_WITH_BODY.on(functionOrPropertyAccessor, abstractNode, functionDescriptor));
}
if (function.getBodyExpression() == null && !hasAbstractModifier && !inTrait && nameIdentifier != null && !isPropertyAccessor) {
context.getTrace().report(NON_ABSTRACT_FUNCTION_WITH_NO_BODY.on(functionOrPropertyAccessor, nameIdentifier, functionDescriptor));
}
return;
}
if (hasAbstractModifier) {
if (!isPropertyAccessor) {
context.getTrace().report(NON_MEMBER_ABSTRACT_FUNCTION.on(functionOrPropertyAccessor, abstractNode, functionDescriptor));
}
else {
context.getTrace().report(NON_MEMBER_ABSTRACT_ACCESSOR.on(functionOrPropertyAccessor, abstractNode));
}
}
if (function.getBodyExpression() == null && !hasAbstractModifier && nameIdentifier != null && !isPropertyAccessor) {
context.getTrace().report(NON_MEMBER_FUNCTION_NO_BODY.on(functionOrPropertyAccessor, nameIdentifier, functionDescriptor));
}
}
}
@@ -21,8 +21,6 @@ import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl;
import org.jetbrains.jet.lang.types.*;
import org.jetbrains.jet.lexer.JetKeywordToken;
import org.jetbrains.jet.lexer.JetToken;
import org.jetbrains.jet.lexer.JetTokens;
import java.util.*;
@@ -67,8 +65,8 @@ public class ClassDescriptorResolver {
}
descriptor.setTypeParameterDescriptors(typeParameters);
Modality defaultModality = descriptor.getKind() == ClassKind.TRAIT ? Modality.ABSTRACT : Modality.FINAL;
descriptor.setModality(resolveModalityFromModifiers(trace, classElement.getModifierList(), defaultModality));
descriptor.setVisibility(resolveVisibilityFromModifiers(trace, classElement.getModifierList()));
descriptor.setModality(resolveModalityFromModifiers(classElement.getModifierList(), defaultModality));
descriptor.setVisibility(resolveVisibilityFromModifiers(classElement.getModifierList()));
trace.record(BindingContext.CLASS, classElement, descriptor);
}
@@ -193,8 +191,8 @@ public class ClassDescriptorResolver {
}
boolean hasBody = function.getBodyExpression() != null;
Modality defaultModality = getDefaultModality(containingDescriptor, hasBody);
Modality modality = resolveModalityFromModifiers(trace, function.getModifierList(), defaultModality);
Visibility visibility = resolveVisibilityFromModifiers(trace, function.getModifierList());
Modality modality = resolveModalityFromModifiers(function.getModifierList(), defaultModality);
Visibility visibility = resolveVisibilityFromModifiers(function.getModifierList());
functionDescriptor.initialize(
receiverType,
typeParameterDescriptors,
@@ -438,7 +436,7 @@ public class ClassDescriptorResolver {
containingDeclaration,
annotationResolver.createAnnotationStubs(modifierList),
Modality.FINAL,
resolveVisibilityFromModifiers(trace, objectDeclaration.getModifierList()),
resolveVisibilityFromModifiers(objectDeclaration.getModifierList()),
false,
null,
JetPsiUtil.safeName(objectDeclaration.getName()),
@@ -499,8 +497,8 @@ public class ClassDescriptorResolver {
PropertyDescriptor propertyDescriptor = new PropertyDescriptor(
containingDeclaration,
annotationResolver.resolveAnnotations(scope, modifierList),
resolveModalityFromModifiers(trace, property.getModifierList(), defaultModality),
resolveVisibilityFromModifiers(trace, property.getModifierList()),
resolveModalityFromModifiers(property.getModifierList(), defaultModality),
resolveVisibilityFromModifiers(property.getModifierList()),
isVar,
receiverType,
JetPsiUtil.safeName(property.getName()),
@@ -552,17 +550,12 @@ public class ClassDescriptorResolver {
}
@NotNull
/*package*/ static Modality resolveModalityFromModifiers(@NotNull BindingTrace trace, @Nullable JetModifierList modifierList, @NotNull Modality defaultModality) {
/*package*/ static Modality resolveModalityFromModifiers(@Nullable JetModifierList modifierList, @NotNull Modality defaultModality) {
if (modifierList == null) return defaultModality;
checkCompatibility(trace, modifierList, Lists.newArrayList(JetTokens.ABSTRACT_KEYWORD, JetTokens.OPEN_KEYWORD, JetTokens.FINAL_KEYWORD),
Lists.<JetToken>newArrayList(JetTokens.ABSTRACT_KEYWORD, JetTokens.OPEN_KEYWORD));
boolean hasAbstractModifier = modifierList.hasModifier(JetTokens.ABSTRACT_KEYWORD);
boolean hasOverrideModifier = modifierList.hasModifier(JetTokens.OVERRIDE_KEYWORD);
if (modifierList.hasModifier(JetTokens.OPEN_KEYWORD)) {
if (hasAbstractModifier || hasOverrideModifier) {
trace.report(Errors.REDUNDANT_MODIFIER.on(modifierList, JetTokens.OPEN_KEYWORD, hasAbstractModifier ? JetTokens.ABSTRACT_KEYWORD : JetTokens.OVERRIDE_KEYWORD));
}
if (hasAbstractModifier || defaultModality == Modality.ABSTRACT) {
return Modality.ABSTRACT;
}
@@ -582,15 +575,13 @@ public class ClassDescriptorResolver {
}
@NotNull
/*package*/ static Visibility resolveVisibilityFromModifiers(@NotNull BindingTrace trace, @Nullable JetModifierList modifierList) {
return resolveVisibilityFromModifiers(trace, modifierList, Visibility.INTERNAL);
/*package*/ static Visibility resolveVisibilityFromModifiers(@Nullable JetModifierList modifierList) {
return resolveVisibilityFromModifiers(modifierList, Visibility.INTERNAL);
}
@NotNull
/*package*/ static Visibility resolveVisibilityFromModifiers(@NotNull BindingTrace trace, @Nullable JetModifierList modifierList, @NotNull Visibility defaultVisibility) {
/*package*/ static Visibility resolveVisibilityFromModifiers(@Nullable JetModifierList modifierList, @NotNull Visibility defaultVisibility) {
if (modifierList == null) return defaultVisibility;
checkCompatibility(trace, modifierList, Lists.newArrayList(JetTokens.PRIVATE_KEYWORD, JetTokens.PROTECTED_KEYWORD, JetTokens.PUBLIC_KEYWORD, JetTokens.INTERNAL_KEYWORD),
Lists.<JetToken>newArrayList(JetTokens.PROTECTED_KEYWORD, JetTokens.INTERNAL_KEYWORD));
if (modifierList.hasModifier(JetTokens.PRIVATE_KEYWORD)) return Visibility.PRIVATE;
if (modifierList.hasModifier(JetTokens.PUBLIC_KEYWORD)) return Visibility.PUBLIC;
if (modifierList.hasModifier(JetTokens.PROTECTED_KEYWORD)) {
@@ -601,29 +592,7 @@ public class ClassDescriptorResolver {
}
return defaultVisibility;
}
/*package*/ static boolean checkCompatibility(@NotNull BindingTrace trace, @Nullable JetModifierList modifierList, Collection<JetKeywordToken> availableModifiers, Collection<JetToken>... availableCombinations) {
if (modifierList == null) return true;
Collection<JetKeywordToken> presentModifiers = Sets.newLinkedHashSet();
for (JetKeywordToken modifier : availableModifiers) {
if (modifierList.hasModifier(modifier)) {
presentModifiers.add(modifier);
}
}
if (presentModifiers.size() == 1) {
return true;
}
for (Collection<JetToken> combination : availableCombinations) {
if (presentModifiers.containsAll(combination) && combination.containsAll(presentModifiers)) {
return true;
}
}
for (JetKeywordToken token : presentModifiers) {
trace.report(Errors.INCOMPATIBLE_MODIFIERS.on(modifierList.getModifierNode(token), presentModifiers));
}
return false;
}
@Nullable
private PropertySetterDescriptor resolvePropertySetterDescriptor(@NotNull JetScope scope, @NotNull JetProperty property, @NotNull PropertyDescriptor propertyDescriptor) {
JetPropertyAccessor setter = property.getSetter();
@@ -633,8 +602,8 @@ public class ClassDescriptorResolver {
JetParameter parameter = setter.getParameter();
setterDescriptor = new PropertySetterDescriptor(
resolveModalityFromModifiers(trace, setter.getModifierList(), propertyDescriptor.getModality()),
resolveVisibilityFromModifiers(trace, setter.getModifierList(), propertyDescriptor.getVisibility()),
resolveModalityFromModifiers(setter.getModifierList(), propertyDescriptor.getModality()),
resolveVisibilityFromModifiers(setter.getModifierList(), propertyDescriptor.getVisibility()),
propertyDescriptor, annotations, setter.getBodyExpression() != null, false);
if (parameter != null) {
if (parameter.isRef()) {
@@ -708,8 +677,8 @@ public class ClassDescriptorResolver {
}
getterDescriptor = new PropertyGetterDescriptor(
propertyDescriptor, annotations, resolveModalityFromModifiers(trace, getter.getModifierList(), propertyDescriptor.getModality()),
resolveVisibilityFromModifiers(trace, getter.getModifierList(), propertyDescriptor.getVisibility()),
propertyDescriptor, annotations, resolveModalityFromModifiers(getter.getModifierList(), propertyDescriptor.getModality()),
resolveVisibilityFromModifiers(getter.getModifierList(), propertyDescriptor.getVisibility()),
returnType, getter.getBodyExpression() != null, false);
trace.record(BindingContext.PROPERTY_ACCESSOR, getter, getterDescriptor);
}
@@ -748,7 +717,7 @@ public class ClassDescriptorResolver {
new WritableScopeImpl(scope, classDescriptor, new TraceBasedRedeclarationHandler(trace)).setDebugName("Scope with value parameters of a constructor"),
valueParameters),
Modality.FINAL,
resolveVisibilityFromModifiers(trace, modifierList));
resolveVisibilityFromModifiers(modifierList));
}
@Nullable
@@ -784,8 +753,8 @@ public class ClassDescriptorResolver {
PropertyDescriptor propertyDescriptor = new PropertyDescriptor(
classDescriptor,
annotationResolver.resolveAnnotations(scope, modifierList),
resolveModalityFromModifiers(trace, parameter.getModifierList(), Modality.FINAL),
resolveVisibilityFromModifiers(trace, parameter.getModifierList()),
resolveModalityFromModifiers(parameter.getModifierList(), Modality.FINAL),
resolveVisibilityFromModifiers(parameter.getModifierList()),
isMutable,
null,
name == null ? "<no name>" : name,
@@ -0,0 +1,352 @@
package org.jetbrains.jet.lang.resolve;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.diagnostics.Errors;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.types.DeferredType;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lexer.JetKeywordToken;
import org.jetbrains.jet.lexer.JetToken;
import org.jetbrains.jet.lexer.JetTokens;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import static org.jetbrains.jet.lang.diagnostics.Errors.*;
/**
* @author svtk
*/
public class DeclarationsChecker {
private TopDownAnalysisContext context;
public DeclarationsChecker(TopDownAnalysisContext context) {
this.context = context;
}
public void process() {
checkIfPrimaryConstructorIsNecessary();
Map<JetClass, MutableClassDescriptor> classes = context.getClasses();
for (Map.Entry<JetClass, MutableClassDescriptor> entry : classes.entrySet()) {
JetClass aClass = entry.getKey();
MutableClassDescriptor classDescriptor = entry.getValue();
checkClass(aClass, classDescriptor);
checkModifiers(aClass.getModifierList());
}
Map<JetObjectDeclaration, MutableClassDescriptor> objects = context.getObjects();
for (Map.Entry<JetObjectDeclaration, MutableClassDescriptor> entry : objects.entrySet()) {
JetObjectDeclaration objectDeclaration = entry.getKey();
MutableClassDescriptor objectDescriptor = entry.getValue();
checkObject(objectDeclaration, objectDescriptor);
}
Map<JetNamedFunction, FunctionDescriptorImpl> functions = context.getFunctions();
for (Map.Entry<JetNamedFunction, FunctionDescriptorImpl> entry : functions.entrySet()) {
JetNamedFunction function = entry.getKey();
FunctionDescriptorImpl functionDescriptor = entry.getValue();
checkFunction(function, functionDescriptor);
checkModifiers(function.getModifierList());
}
Map<JetProperty, PropertyDescriptor> properties = context.getProperties();
for (Map.Entry<JetProperty, PropertyDescriptor> entry : properties.entrySet()) {
JetProperty property = entry.getKey();
PropertyDescriptor propertyDescriptor = entry.getValue();
checkProperty(property, propertyDescriptor);
checkModifiers(property.getModifierList());
}
}
private void checkIfPrimaryConstructorIsNecessary() {
for (Map.Entry<JetClass, MutableClassDescriptor> entry : context.getClasses().entrySet()) {
MutableClassDescriptor classDescriptor = entry.getValue();
JetClass jetClass = entry.getKey();
if (classDescriptor.getUnsubstitutedPrimaryConstructor() == null && !(classDescriptor.getKind() == ClassKind.TRAIT)) {
for (PropertyDescriptor propertyDescriptor : classDescriptor.getProperties()) {
if (context.getTrace().getBindingContext().get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor)) {
PsiElement nameIdentifier = jetClass.getNameIdentifier();
if (nameIdentifier != null) {
context.getTrace().report(PRIMARY_CONSTRUCTOR_MISSING_STATEFUL_PROPERTY.on(jetClass, nameIdentifier, propertyDescriptor));
}
break;
}
}
}
}
}
private void checkClass(JetClass aClass, MutableClassDescriptor classDescriptor) {
checkOpenMembers(aClass, classDescriptor);
checkTraitModifiers(aClass);
}
private void checkTraitModifiers(JetClass aClass) {
if (!aClass.isTrait()) return;
JetModifierList modifierList = aClass.getModifierList();
if (modifierList == null) return;
if (modifierList.hasModifier(JetTokens.FINAL_KEYWORD)) {
context.getTrace().report(Errors.TRAIT_CAN_NOT_BE_FINAL.on(aClass, modifierList.getModifierNode(JetTokens.FINAL_KEYWORD)));
}
ArrayList<JetKeywordToken> redundantModifiers = Lists.newArrayList(JetTokens.OPEN_KEYWORD, JetTokens.ABSTRACT_KEYWORD);
for (JetKeywordToken modifier : redundantModifiers) {
if (modifierList.hasModifier(modifier)) {
context.getTrace().report(Errors.REDUNDANT_MODIFIER_IN_TRAIT.on(modifierList, modifierList.getModifierNode(modifier), modifier));
}
}
}
private void checkObject(JetObjectDeclaration objectDeclaration, MutableClassDescriptor classDescriptor) {
checkIllegalInThisContextModifiers(objectDeclaration.getModifierList(), JetTokens.ABSTRACT_KEYWORD, JetTokens.OPEN_KEYWORD, JetTokens.OVERRIDE_KEYWORD);
}
private void checkOpenMembers(JetClass aClass, MutableClassDescriptor classDescriptor) {
for (CallableMemberDescriptor memberDescriptor : classDescriptor.getCallableMembers()) {
JetNamedDeclaration member = (JetNamedDeclaration) context.getTrace().get(BindingContext.DESCRIPTOR_TO_DECLARATION, memberDescriptor);
if (member != null && classDescriptor.getModality() == Modality.FINAL && member.hasModifier(JetTokens.OPEN_KEYWORD)) {
ASTNode openModifierNode = member.getModifierList().getModifierNode(JetTokens.OPEN_KEYWORD);
context.getTrace().report(NON_FINAL_MEMBER_IN_FINAL_CLASS.on(member, openModifierNode, aClass));
}
}
}
protected void checkProperty(JetProperty property, PropertyDescriptor propertyDescriptor) {
DeclarationDescriptor containingDeclaration = propertyDescriptor.getContainingDeclaration();
ClassDescriptor classDescriptor = (containingDeclaration instanceof ClassDescriptor)
? (ClassDescriptor) containingDeclaration
: null;
checkPropertyAbstractness(property, propertyDescriptor, classDescriptor);
checkPropertyInitializer(property, propertyDescriptor, classDescriptor);
checkAccessors(property, propertyDescriptor);
}
private void checkPropertyAbstractness(JetProperty property, PropertyDescriptor propertyDescriptor, ClassDescriptor classDescriptor) {
JetPropertyAccessor getter = property.getGetter();
JetPropertyAccessor setter = property.getSetter();
JetModifierList modifierList = property.getModifierList();
ASTNode abstractNode = modifierList != null ? modifierList.getModifierNode(JetTokens.ABSTRACT_KEYWORD) : null;
if (abstractNode != null) { //has abstract modifier
if (classDescriptor == null) {
context.getTrace().report(ABSTRACT_PROPERTY_NOT_IN_CLASS.on(property, abstractNode));
return;
}
if (!(classDescriptor.getModality() == Modality.ABSTRACT) && classDescriptor.getKind() != ClassKind.ENUM_CLASS) {
PsiElement classElement = context.getTrace().get(BindingContext.DESCRIPTOR_TO_DECLARATION, classDescriptor);
assert classElement instanceof JetClass;
context.getTrace().report(ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS.on(property, abstractNode, property.getName(), classDescriptor, (JetClass) classElement));
return;
}
if (classDescriptor.getKind() == ClassKind.TRAIT) {
context.getTrace().report(REDUNDANT_ABSTRACT.on(property, abstractNode));
}
}
if (propertyDescriptor.getModality() == Modality.ABSTRACT) {
JetType returnType = propertyDescriptor.getReturnType();
if (returnType instanceof DeferredType) {
returnType = ((DeferredType) returnType).getActualType();
}
JetExpression initializer = property.getInitializer();
if (initializer != null) {
context.getTrace().report(ABSTRACT_PROPERTY_WITH_INITIALIZER.on(property, initializer, returnType));
}
if (getter != null && getter.getBodyExpression() != null) {
context.getTrace().report(ABSTRACT_PROPERTY_WITH_GETTER.on(property, getter, returnType));
}
if (setter != null && setter.getBodyExpression() != null) {
context.getTrace().report(ABSTRACT_PROPERTY_WITH_SETTER.on(property, setter, returnType));
}
}
}
private void checkPropertyInitializer(JetProperty property, PropertyDescriptor propertyDescriptor, ClassDescriptor classDescriptor) {
JetPropertyAccessor getter = property.getGetter();
JetPropertyAccessor setter = property.getSetter();
boolean hasAccessorImplementation = (getter != null && getter.getBodyExpression() != null) ||
(setter != null && setter.getBodyExpression() != null);
if (propertyDescriptor.getModality() == Modality.ABSTRACT) return;
boolean inTrait = classDescriptor != null && classDescriptor.getKind() == ClassKind.TRAIT;
JetExpression initializer = property.getInitializer();
boolean backingFieldRequired = context.getTrace().getBindingContext().get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor);
PsiElement nameIdentifier = property.getNameIdentifier();
ASTNode nameNode = nameIdentifier == null ? property.getNode() : nameIdentifier.getNode();
if (inTrait && backingFieldRequired && hasAccessorImplementation) {
context.getTrace().report(BACKING_FIELD_IN_TRAIT.on(nameNode));
}
if (initializer == null) {
if (backingFieldRequired && !inTrait && !context.getTrace().getBindingContext().get(BindingContext.IS_INITIALIZED, propertyDescriptor)) {
if (classDescriptor == null || hasAccessorImplementation) {
context.getTrace().report(MUST_BE_INITIALIZED.on(nameNode));
}
else {
context.getTrace().report(MUST_BE_INITIALIZED_OR_BE_ABSTRACT.on(property, nameNode));
}
}
return;
}
if (inTrait) {
JetType returnType = propertyDescriptor.getReturnType();
if (returnType instanceof DeferredType) {
returnType = ((DeferredType) returnType).getActualType();
}
context.getTrace().report(PROPERTY_INITIALIZER_IN_TRAIT.on(property, initializer, returnType));
}
else if (!backingFieldRequired) {
context.getTrace().report(PROPERTY_INITIALIZER_NO_BACKING_FIELD.on(initializer));
}
else if (classDescriptor != null && classDescriptor.getUnsubstitutedPrimaryConstructor() == null) {
PsiElement classElement = context.getTrace().get(BindingContext.DESCRIPTOR_TO_DECLARATION, classDescriptor);
assert classElement instanceof JetClass;
context.getTrace().report(PROPERTY_INITIALIZER_NO_PRIMARY_CONSTRUCTOR.on(property, initializer, (JetClass) classElement));
}
}
protected void checkFunction(JetDeclarationWithBody function, FunctionDescriptor functionDescriptor) {
DeclarationDescriptor containingDescriptor = functionDescriptor.getContainingDeclaration();
PsiElement nameIdentifier;
boolean isPropertyAccessor = false;
if (function instanceof JetNamedFunction) {
nameIdentifier = ((JetNamedFunction) function).getNameIdentifier();
}
else if (function instanceof JetPropertyAccessor) {
isPropertyAccessor = true;
nameIdentifier = ((JetPropertyAccessor) function).getNamePlaceholder();
}
else {
throw new UnsupportedOperationException();
}
JetFunctionOrPropertyAccessor functionOrPropertyAccessor = (JetFunctionOrPropertyAccessor) function;
JetModifierList modifierList = functionOrPropertyAccessor.getModifierList();
ASTNode abstractNode = modifierList != null ? modifierList.getModifierNode(JetTokens.ABSTRACT_KEYWORD) : null;
boolean hasAbstractModifier = abstractNode != null;
if (containingDescriptor instanceof ClassDescriptor) {
ClassDescriptor classDescriptor = (ClassDescriptor) containingDescriptor;
boolean inTrait = classDescriptor.getKind() == ClassKind.TRAIT;
boolean inEnum = classDescriptor.getKind() == ClassKind.ENUM_CLASS;
boolean inAbstractClass = classDescriptor.getModality() == Modality.ABSTRACT;
if (hasAbstractModifier && !inAbstractClass && !inTrait && !inEnum) {
PsiElement classElement = context.getTrace().get(BindingContext.DESCRIPTOR_TO_DECLARATION, classDescriptor);
assert classElement instanceof JetClass;
context.getTrace().report(ABSTRACT_FUNCTION_IN_NON_ABSTRACT_CLASS.on(functionOrPropertyAccessor, abstractNode, functionDescriptor.getName(), classDescriptor, (JetClass) classElement));
}
if (hasAbstractModifier && inTrait && !isPropertyAccessor) {
context.getTrace().report(REDUNDANT_ABSTRACT.on(functionOrPropertyAccessor, abstractNode));
}
if (function.getBodyExpression() != null && hasAbstractModifier) {
context.getTrace().report(ABSTRACT_FUNCTION_WITH_BODY.on(functionOrPropertyAccessor, abstractNode, functionDescriptor));
}
if (function.getBodyExpression() == null && !hasAbstractModifier && !inTrait && nameIdentifier != null && !isPropertyAccessor) {
context.getTrace().report(NON_ABSTRACT_FUNCTION_WITH_NO_BODY.on(functionOrPropertyAccessor, nameIdentifier, functionDescriptor));
}
return;
}
if (hasAbstractModifier) {
if (!isPropertyAccessor) {
context.getTrace().report(NON_MEMBER_ABSTRACT_FUNCTION.on(functionOrPropertyAccessor, abstractNode, functionDescriptor));
}
else {
context.getTrace().report(NON_MEMBER_ABSTRACT_ACCESSOR.on(functionOrPropertyAccessor, abstractNode));
}
}
if (function.getBodyExpression() == null && !hasAbstractModifier && nameIdentifier != null && !isPropertyAccessor) {
context.getTrace().report(NON_MEMBER_FUNCTION_NO_BODY.on(functionOrPropertyAccessor, nameIdentifier, functionDescriptor));
}
}
private void checkAccessors(JetProperty property, PropertyDescriptor propertyDescriptor) {
for (JetPropertyAccessor accessor : property.getAccessors()) {
PropertyAccessorDescriptor accessorDescriptor = accessor.isGetter()
? propertyDescriptor.getGetter()
: propertyDescriptor.getSetter();
checkFunction(accessor, accessorDescriptor);
if (propertyDescriptor.getModality() == Modality.FINAL && accessor.hasModifier(JetTokens.OPEN_KEYWORD)) {
ASTNode openModifierNode = accessor.getModifierList().getModifierNode(JetTokens.OPEN_KEYWORD);
context.getTrace().report(NON_FINAL_ACCESSOR_OF_FINAL_PROPERTY.on(accessor, openModifierNode, property));
}
}
}
private void checkModifiers(@Nullable JetModifierList modifierList) {
checkModalityModifiers(modifierList);
checkVisibilityModifiers(modifierList);
}
private void checkModalityModifiers(@Nullable JetModifierList modifierList) {
if (modifierList == null) return;
checkRedundantModifier(modifierList, Pair.create(JetTokens.OPEN_KEYWORD, JetTokens.ABSTRACT_KEYWORD), Pair.create(JetTokens.OPEN_KEYWORD, JetTokens.OVERRIDE_KEYWORD));
checkCompatibility(modifierList, Lists.newArrayList(JetTokens.ABSTRACT_KEYWORD, JetTokens.OPEN_KEYWORD, JetTokens.FINAL_KEYWORD),
Lists.<JetToken>newArrayList(JetTokens.ABSTRACT_KEYWORD, JetTokens.OPEN_KEYWORD));
}
private void checkVisibilityModifiers(@Nullable JetModifierList modifierList) {
if (modifierList == null) return;
checkCompatibility(modifierList, Lists.newArrayList(JetTokens.PRIVATE_KEYWORD, JetTokens.PROTECTED_KEYWORD, JetTokens.PUBLIC_KEYWORD, JetTokens.INTERNAL_KEYWORD),
Lists.<JetToken>newArrayList(JetTokens.PROTECTED_KEYWORD, JetTokens.INTERNAL_KEYWORD));
}
private void checkCompatibility(@Nullable JetModifierList modifierList, Collection<JetKeywordToken> availableModifiers, Collection<JetToken>... availableCombinations) {
if (modifierList == null) return;
Collection<JetKeywordToken> presentModifiers = Sets.newLinkedHashSet();
for (JetKeywordToken modifier : availableModifiers) {
if (modifierList.hasModifier(modifier)) {
presentModifiers.add(modifier);
}
}
if (presentModifiers.size() == 1) {
return;
}
for (Collection<JetToken> combination : availableCombinations) {
if (presentModifiers.containsAll(combination) && combination.containsAll(presentModifiers)) {
return;
}
}
for (JetKeywordToken token : presentModifiers) {
context.getTrace().report(Errors.INCOMPATIBLE_MODIFIERS.on(modifierList.getModifierNode(token), presentModifiers));
}
}
private void checkRedundantModifier(@NotNull JetModifierList modifierList, Pair<JetKeywordToken, JetKeywordToken>... redundantBundles) {
for (Pair<JetKeywordToken, JetKeywordToken> tokenPair : redundantBundles) {
JetKeywordToken redundantModifier = tokenPair.getFirst();
JetKeywordToken sufficientModifier = tokenPair.getSecond();
if (modifierList.hasModifier(redundantModifier) && modifierList.hasModifier(sufficientModifier)) {
context.getTrace().report(Errors.REDUNDANT_MODIFIER.on(modifierList, modifierList.getModifierNode(redundantModifier), redundantModifier, sufficientModifier));
}
}
}
private void checkIllegalInThisContextModifiers(@Nullable JetModifierList modifierList, JetKeywordToken... illegalModifiers) {
if (modifierList == null) return;
for (JetKeywordToken modifier : illegalModifiers) {
if (modifierList.hasModifier(modifier)) {
context.getTrace().report(Errors.ILLEGAL_MODIFIER.on(modifierList.getModifierNode(modifier), modifier));
}
}
}
}
@@ -68,6 +68,7 @@ public class TopDownAnalyzer {
new DelegationResolver(context).process();
new OverrideResolver(context).process();
new BodyResolver(context).resolveBehaviorDeclarationBodies();
new DeclarationsChecker(context).process();
}
public static void processStandardLibraryNamespace(
@@ -86,16 +87,7 @@ public class TopDownAnalyzer {
}
};
overrideResolver.process();
BodyResolver bodyResolver = new BodyResolver(context) {
@Override
protected void checkProperty(JetProperty property, PropertyDescriptor propertyDescriptor, @Nullable ClassDescriptor classDescriptor) {
}
@Override
protected void checkFunction(JetDeclarationWithBody function, FunctionDescriptor functionDescriptor) {
}
};
bodyResolver.resolveBehaviorDeclarationBodies();
new BodyResolver(context).resolveBehaviorDeclarationBodies();
}
}
@@ -98,7 +98,7 @@ public class TypeHierarchyResolver {
MutableClassDescriptor classObjectDescriptor = new MutableClassDescriptor(context.getTrace(), mutableClassDescriptor, outerScope, ClassKind.OBJECT);
classObjectDescriptor.setName("class-object-for-" + klass.getName());
classObjectDescriptor.setModality(Modality.FINAL);
classObjectDescriptor.setVisibility(ClassDescriptorResolver.resolveVisibilityFromModifiers(context.getTrace(), klass.getModifierList()));
classObjectDescriptor.setVisibility(ClassDescriptorResolver.resolveVisibilityFromModifiers(klass.getModifierList()));
classObjectDescriptor.createTypeConstructor();
createPrimaryConstructorForObject(null, classObjectDescriptor);
mutableClassDescriptor.setClassObjectDescriptor(classObjectDescriptor);
@@ -274,7 +274,7 @@ public class TypeHierarchyResolver {
JetObjectDeclaration objectDeclaration = entry.getKey();
MutableClassDescriptor descriptor = entry.getValue();
descriptor.setModality(Modality.FINAL);
descriptor.setVisibility(ClassDescriptorResolver.resolveVisibilityFromModifiers(context.getTrace(), objectDeclaration.getModifierList()));
descriptor.setVisibility(ClassDescriptorResolver.resolveVisibilityFromModifiers(objectDeclaration.getModifierList()));
descriptor.createTypeConstructor();
}
}
@@ -19,27 +19,16 @@ import org.jetbrains.jet.lexer.JetTokens;
*/
public class AddModifierFix extends ModifierFix {
private final JetToken[] modifiersThanCanBeReplaced;
private final JetToken[] conflictedModifiers;
private AddModifierFix(@NotNull JetModifierListOwner element, JetKeywordToken modifier, JetToken[] modifiersThanCanBeReplaced, JetToken[] conflictedModifiers) {
private AddModifierFix(@NotNull JetModifierListOwner element, JetKeywordToken modifier, JetToken[] modifiersThanCanBeReplaced) {
super(element, modifier);
this.modifiersThanCanBeReplaced = modifiersThanCanBeReplaced;
this.conflictedModifiers = conflictedModifiers;
}
private static boolean checkConflictModifiers(JetModifierListOwner element, JetToken[] conflictedModifiers) {
for (JetToken conflictedModifier : conflictedModifiers) {
if (element.hasModifier(conflictedModifier)) {
return true;
}
}
return false;
}
@NotNull
@Override
public String getText() {
if (modifier == JetTokens.ABSTRACT_KEYWORD) {
if (modifier == JetTokens.ABSTRACT_KEYWORD || modifier == JetTokens.OPEN_KEYWORD) {
return "Make " + getElementName() + " " + modifier.getValue();
}
return "Add '" + modifier.getValue() + "' modifier";
@@ -53,7 +42,7 @@ public class AddModifierFix extends ModifierFix {
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return element.isValid() && !checkConflictModifiers(element, conflictedModifiers);
return element.isValid();
}
@Override
@@ -97,17 +86,17 @@ public class AddModifierFix extends ModifierFix {
return true;
}
public static JetIntentionActionFactory<JetModifierListOwner> createFactory(final JetKeywordToken modifier, final JetToken[] modifiersThatCanBeReplaced, final JetToken[] conflictedModifiers) {
public static JetIntentionActionFactory<JetModifierListOwner> createFactory(final JetKeywordToken modifier, final JetToken[] modifiersThatCanBeReplaced) {
return new JetIntentionActionFactory<JetModifierListOwner>() {
@Override
public JetIntentionAction<JetModifierListOwner> createAction(DiagnosticWithPsiElement diagnostic) {
assert diagnostic.getPsiElement() instanceof JetModifierListOwner;
return new AddModifierFix((JetModifierListOwner) diagnostic.getPsiElement(), modifier, modifiersThatCanBeReplaced, conflictedModifiers);
return new AddModifierFix((JetModifierListOwner) diagnostic.getPsiElement(), modifier, modifiersThatCanBeReplaced);
}
};
}
public static JetIntentionActionFactory<JetModifierListOwner> createFactory(final JetKeywordToken modifier) {
return createFactory(modifier, new JetToken[0], new JetToken[0]);
return createFactory(modifier, new JetToken[0]);
}
}
@@ -30,7 +30,7 @@ public class QuickFixes {
static {
JetIntentionActionFactory<JetModifierListOwner> removeAbstractModifierFactory = RemoveModifierFix.createFactory(JetTokens.ABSTRACT_KEYWORD);
JetIntentionActionFactory<JetModifierListOwner> addAbstractModifierFactory = AddModifierFix.createFactory(JetTokens.ABSTRACT_KEYWORD, new JetToken[]{JetTokens.OPEN_KEYWORD}, new JetToken[] {JetTokens.FINAL_KEYWORD});
JetIntentionActionFactory<JetModifierListOwner> addAbstractModifierFactory = AddModifierFix.createFactory(JetTokens.ABSTRACT_KEYWORD, new JetToken[]{JetTokens.OPEN_KEYWORD, JetTokens.FINAL_KEYWORD});
add(Errors.ABSTRACT_PROPERTY_IN_PRIMARY_CONSTRUCTOR_PARAMETERS, removeAbstractModifierFactory);
add(Errors.ABSTRACT_PROPERTY_NOT_IN_CLASS, removeAbstractModifierFactory);
@@ -84,14 +84,24 @@ public class QuickFixes {
add(Errors.USELESS_ELVIS, RemoveRightPartOfBinaryExpressionFix.createRemoveElvisOperatorFactory());
add(Errors.UNNECESSARY_SAFE_CALL, ReplaceSafeCallToDotCall.createFactory());
add(Errors.REDUNDANT_MODIFIER, RemoveRedundantModifierFix.createFactory());
JetIntentionActionFactory<JetModifierList> removeRedundantModifierFactory = RemoveRedundantModifierFix.createFactory();
add(Errors.REDUNDANT_MODIFIER, removeRedundantModifierFactory);
add(Errors.REDUNDANT_MODIFIER_IN_TRAIT, removeRedundantModifierFactory);
add(Errors.TRAIT_CAN_NOT_BE_FINAL, RemoveModifierFix.createFactory(JetTokens.FINAL_KEYWORD));
add(Errors.PROPERTY_INITIALIZER_NO_PRIMARY_CONSTRUCTOR, RemovePartsFromPropertyFix.createRemoveInitializerFactory());
JetIntentionActionFactory<JetClass> addPrimaryConstructorFactory = AddPrimaryConstructorFix.createFactory();
add(Errors.PROPERTY_INITIALIZER_NO_PRIMARY_CONSTRUCTOR, QuickFixUtil.createFactoryRedirectingAdditionalInfoToAnotherFactory(addPrimaryConstructorFactory, DiagnosticParameters.CLASS));
add(Errors.PRIMARY_CONSTRUCTOR_MISSING_STATEFUL_PROPERTY, addPrimaryConstructorFactory);
JetIntentionActionFactory<JetModifierListOwner> addOpenModifierFactory = AddModifierFix.createFactory(JetTokens.OPEN_KEYWORD, new JetToken[]{JetTokens.FINAL_KEYWORD});
JetIntentionActionFactory<JetModifierListOwner> removeOpenModifierFactory = RemoveModifierFix.createFactory(JetTokens.OPEN_KEYWORD);
add(Errors.NON_FINAL_MEMBER_IN_FINAL_CLASS, QuickFixUtil.createFactoryRedirectingAdditionalInfoToAnotherFactory(addOpenModifierFactory, DiagnosticParameters.CLASS));
add(Errors.NON_FINAL_MEMBER_IN_FINAL_CLASS, removeOpenModifierFactory);
add(Errors.NON_FINAL_ACCESSOR_OF_FINAL_PROPERTY, QuickFixUtil.createFactoryRedirectingAdditionalInfoToAnotherFactory(addOpenModifierFactory, DiagnosticParameters.PROPERTY));
add(Errors.NON_FINAL_ACCESSOR_OF_FINAL_PROPERTY, removeOpenModifierFactory);
}
}
@@ -29,7 +29,7 @@ public class RemoveModifierFix extends ModifierFix {
@NotNull
@Override
public String getText() {
if (modifier == JetTokens.ABSTRACT_KEYWORD) {
if (modifier == JetTokens.ABSTRACT_KEYWORD || modifier == JetTokens.OPEN_KEYWORD) {
return "Make " + getElementName() + " not " + modifier.getValue();
}
return "Remove '" + modifier.getValue() + "' modifier";
+8 -8
View File
@@ -1,13 +1,13 @@
open trait A {
trait A {
fun foo() {}
}
open trait B : A, <error>E</error> {}
open trait C : B {}
open trait D : <error>B</error> {}
open trait E : <error>F</error> {}
open trait F : <error>D</error>, C {}
open trait G : F {}
open trait H : F {}
trait B : A, <error>E</error> {}
trait C : B {}
trait D : <error>B</error> {}
trait E : <error>F</error> {}
trait F : <error>D</error>, C {}
trait G : F {}
trait H : F {}
val a : A? = null
val b : B? = null
@@ -1,13 +1,13 @@
open trait A {
trait A {
fun foo() {}
}
open trait B : A, <!CYCLIC_INHERITANCE_HIERARCHY!>E<!> {}
open trait C : B {}
open trait D : <!CYCLIC_INHERITANCE_HIERARCHY!>B<!> {}
open trait E : <!CYCLIC_INHERITANCE_HIERARCHY!>F<!> {}
open trait F : <!CYCLIC_INHERITANCE_HIERARCHY!>D<!>, C {}
open trait G : F {}
open trait H : F {}
trait B : A, <!CYCLIC_INHERITANCE_HIERARCHY!>E<!> {}
trait C : B {}
trait D : <!CYCLIC_INHERITANCE_HIERARCHY!>B<!> {}
trait E : <!CYCLIC_INHERITANCE_HIERARCHY!>F<!> {}
trait F : <!CYCLIC_INHERITANCE_HIERARCHY!>D<!>, C {}
trait G : F {}
trait H : F {}
val a : A? = null
val b : B? = null
@@ -0,0 +1,7 @@
namespace a
abstract class A() {
<!INCOMPATIBLE_MODIFIERS!>abstract<!> <!INCOMPATIBLE_MODIFIERS!>final<!> fun f()
abstract <!REDUNDANT_MODIFIER!>open<!> fun g()
<!INCOMPATIBLE_MODIFIERS!>final<!> <!INCOMPATIBLE_MODIFIERS!>open<!> fun h() {}
}
@@ -1,4 +0,0 @@
// "Make 'foo' abstract" "false"
class B() {
final fun <caret>foo()
}
@@ -4,5 +4,5 @@ abstract class B() {
}
abstract class A() : B() {
<caret>abstract override fun foo()
abstract <caret>override fun foo()
}
@@ -4,5 +4,5 @@ abstract class B() {
}
abstract class A() : B() {
<caret>abstract override fun foo()
abstract override<caret> fun foo()
}
@@ -4,5 +4,5 @@ abstract class B() {
}
abstract class A() : B() {
<caret>abstract open override fun foo()
abstract <caret>open override fun foo()
}
@@ -4,5 +4,5 @@ abstract class B() {
}
abstract class A() : B() {
<caret>abstract override open fun foo()
abstract override <caret>open fun foo()
}