From ec55dddfcd91d864018665501e52927c0a03beb8 Mon Sep 17 00:00:00 2001 From: svtk Date: Tue, 6 Dec 2011 22:41:19 +0400 Subject: [PATCH] backing fields resolve changed KT-462 Consider allowing initializing properties via property names when it is safe KT-598 Allow to use backing fields after this expression --- .../jet/lang/cfg/JetControlFlowProcessor.java | 11 ++ .../lang/cfg/JetFlowInformationProvider.java | 179 ++++++++++++------ .../descriptors/MutableClassDescriptor.java | 3 +- .../jet/lang/diagnostics/Errors.java | 9 +- .../jetbrains/jet/lang/psi/JetPsiUtil.java | 6 +- .../jet/lang/resolve/BodyResolver.java | 55 ++---- .../jet/lang/resolve/DeclarationsChecker.java | 3 +- .../jet/lang/resolve/calls/CallResolver.java | 10 +- .../lang/resolve/calls/TaskPrioritizers.java | 38 +++- .../lang/resolve/scopes/WritableScope.java | 2 - .../resolve/scopes/WritableScopeImpl.java | 11 -- .../resolve/scopes/WriteThroughScope.java | 7 - .../BasicExpressionTypingVisitor.java | 17 +- .../ExpressionTypingVisitorForStatements.java | 11 +- .../tests/AnonymousInitializers.jet | 2 +- .../testData/diagnostics/tests/Properties.jet | 14 +- .../UninitializedOrReassignedVariables.jet | 10 +- .../tests/backingField/ReadInFunction.jet | 2 +- ...NonexistentAbstractPropertyInAnonymous.jet | 4 +- ...dNonexistentAbstractPropertyInFunction.jet | 4 +- .../ReadNonexistentCustomGetInAnonymous.jet | 4 +- ...nexistentCustomGetInAnotherInitializer.jet | 4 +- .../ReadNonexistentDeclaredInHigher.jet | 13 +- .../WriteNonexistentDeclaredInHigher.jet | 4 +- .../kt462BackingFieldsResolve.jet | 172 +++++++++++++++++ .../tests/control-flow-analysis/kt510.jet | 4 +- .../jet/plugin/quickfix/AddModifierFix.java | 2 +- .../quickfix/ChangeToBackingFieldFix.java | 7 +- .../quickfix/ChangeVariableMutabilityFix.java | 7 +- .../jet/plugin/quickfix/QuickFixes.java | 6 +- .../plugin/quickfix/RemoveModifierFix.java | 2 +- idea/testData/checker/Properties.jet | 4 +- .../changeToBackingField/afterBFRequired.kt | 3 +- .../changeToBackingField/afterKt510.kt | 3 +- .../changeToBackingField/beforeBFRequired.kt | 3 +- .../changeToBackingField/beforeKt510.kt | 1 + 36 files changed, 422 insertions(+), 215 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/backingField/kt462BackingFieldsResolve.jet diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java index 7b1945f9351..e0a035d9b0b 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowProcessor.java @@ -821,6 +821,9 @@ public class JetControlFlowProcessor { } private void visitClassOrObject(JetClassOrObject classOrObject) { + for (JetDelegationSpecifier specifier : classOrObject.getDelegationSpecifiers()) { + value(specifier, inCondition); + } List declarations = classOrObject.getDeclarations(); List properties = Lists.newArrayList(); for (JetDeclaration declaration : declarations) { @@ -839,6 +842,14 @@ public class JetControlFlowProcessor { visitClassOrObject(klass); } + @Override + public void visitDelegationToSuperCallSpecifier(JetDelegatorToSuperCall call) { + List valueArguments = call.getValueArguments(); + for (ValueArgument valueArgument : valueArguments) { + value(valueArgument.getArgumentExpression(), inCondition); + } + } + @Override public void visitJetElement(JetElement element) { builder.unsupported(element); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java index 4c260399c57..4f7166c0425 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java @@ -6,11 +6,11 @@ import com.google.common.collect.Sets; import com.intellij.openapi.util.Pair; import com.intellij.psi.PsiElement; import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.cfg.pseudocode.*; import org.jetbrains.jet.lang.descriptors.*; -import org.jetbrains.jet.lang.descriptors.VariableDescriptor; import org.jetbrains.jet.lang.diagnostics.Errors; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; @@ -223,22 +223,24 @@ public class JetFlowInformationProvider { @Override public void execute(Instruction instruction, @Nullable Map enterData, @Nullable Map exitData) { assert enterData != null && exitData != null; + VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, true); + if (variableDescriptor == null) return; if (instruction instanceof ReadValueInstruction) { - VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, false); - if (variableDescriptor != null && declaredVariables.contains(variableDescriptor)) { - checkIsInitialized(variableDescriptor, ((ReadValueInstruction) instruction).getElement(), exitData.get(variableDescriptor), varWithUninitializedErrorGenerated); + JetElement element = ((ReadValueInstruction) instruction).getElement(); + boolean error = checkBackingField(variableDescriptor, element); + if (!error && declaredVariables.contains(variableDescriptor)) { + checkIsInitialized(variableDescriptor, element, exitData.get(variableDescriptor), varWithUninitializedErrorGenerated); } } else if (instruction instanceof WriteValueInstruction) { - VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, true); JetElement element = ((WriteValueInstruction) instruction).getlValue(); - if (variableDescriptor != null && element instanceof JetExpression) { - if (!processLocalDeclaration) { // error has been generated before while processing outer function of this local declaration - checkValReassignment(variableDescriptor, (JetExpression) element, enterData.get(variableDescriptor), varWithValReassignErrorGenerated); - } - if (processClassOrObject) { - checkInitializationUsingBackingField(variableDescriptor, (JetExpression) element, enterData.get(variableDescriptor), exitData.get(variableDescriptor)); - } + boolean error = checkBackingField(variableDescriptor, element); + if (!(element instanceof JetExpression)) return; + if (!error && !processLocalDeclaration) { // error has been generated before while processing outer function of this local declaration + error = checkValReassignment(variableDescriptor, (JetExpression) element, enterData.get(variableDescriptor), varWithValReassignErrorGenerated); + } + if (!error && processClassOrObject) { + checkInitializationUsingBackingField(variableDescriptor, (JetExpression) element, enterData.get(variableDescriptor), exitData.get(variableDescriptor)); } } } @@ -263,7 +265,7 @@ public class JetFlowInformationProvider { } } - private void checkValReassignment(@NotNull VariableDescriptor variableDescriptor, @NotNull JetExpression expression, @NotNull VariableInitializers enterInitializers, @NotNull Collection varWithValReassignErrorGenerated) { + private boolean checkValReassignment(@NotNull VariableDescriptor variableDescriptor, @NotNull JetExpression expression, @NotNull VariableInitializers enterInitializers, @NotNull Collection varWithValReassignErrorGenerated) { boolean isInitializedNotHere = enterInitializers.isInitialized(); Set possibleLocalInitializers = enterInitializers.getPossibleLocalInitializers(); if (possibleLocalInitializers.size() == 1) { @@ -298,12 +300,19 @@ public class JetFlowInformationProvider { } if (!hasReassignMethodReturningUnit) { trace.report(Errors.VAL_REASSIGNMENT.on(expression, variableDescriptor)); + return true; } } + return false; } private void checkInitializationUsingBackingField(@NotNull VariableDescriptor variableDescriptor, @NotNull JetExpression expression, @NotNull VariableInitializers enterInitializers, @NotNull VariableInitializers exitInitializers) { if (variableDescriptor instanceof PropertyDescriptor && !enterInitializers.isInitialized() && exitInitializers.isInitialized()) { + if (!variableDescriptor.isVar()) return; + if (!trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor)) return; + PsiElement property = trace.get(BindingContext.DESCRIPTOR_TO_DECLARATION, variableDescriptor); + assert property instanceof JetProperty; + if (((PropertyDescriptor) variableDescriptor).getModality() == Modality.FINAL && ((JetProperty) property).getSetter() == null) return; JetExpression variable = expression; if (expression instanceof JetDotQualifiedExpression) { if (((JetDotQualifiedExpression) expression).getReceiverExpression() instanceof JetThisExpression) { @@ -313,12 +322,63 @@ public class JetFlowInformationProvider { if (variable instanceof JetSimpleNameExpression) { JetSimpleNameExpression simpleNameExpression = (JetSimpleNameExpression) variable; if (simpleNameExpression.getReferencedNameElementType() != JetTokens.FIELD_IDENTIFIER) { - trace.report(Errors.INITIALIZATION_USING_BACKING_FIELD.on(simpleNameExpression, expression, variableDescriptor)); + if (((PropertyDescriptor) variableDescriptor).getModality() != Modality.FINAL) { + trace.report(Errors.INITIALIZATION_USING_BACKING_FIELD_OPEN_SETTER.on(simpleNameExpression, expression, variableDescriptor)); + } + else { + trace.report(Errors.INITIALIZATION_USING_BACKING_FIELD_CUSTOM_SETTER.on(simpleNameExpression, expression, variableDescriptor)); + } } } } } + private boolean checkBackingField(@NotNull VariableDescriptor variableDescriptor, @NotNull JetElement element) { + boolean[] error = new boolean[1]; + if (isBackingFieldReference((JetElement) element.getParent(), error, false)) return false; // this expression has been already checked + if (!isBackingFieldReference(element, error, true)) return false; + if (error[0]) return true; + if (!(variableDescriptor instanceof PropertyDescriptor)) { + trace.report(Errors.NOT_PROPERTY_BACKING_FIELD.on(element)); + return true; + } + PsiElement property = trace.get(BindingContext.DESCRIPTOR_TO_DECLARATION, variableDescriptor); + if (!trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor) && + !PsiTreeUtil.isAncestor(property, element, false)) { // not to generate error in accessors of abstract properties, there is one: declared accessor of abstract property + if (((PropertyDescriptor) variableDescriptor).getModality() == Modality.ABSTRACT) { + trace.report(NO_BACKING_FIELD_ABSTRACT_PROPERTY.on(element)); + } + else { + trace.report(NO_BACKING_FIELD_CUSTOM_ACCESSORS.on(element)); + } + return true; + } + + JetClassOrObject propertyClassOrObject = PsiTreeUtil.getParentOfType(property, JetClassOrObject.class); + if (propertyClassOrObject == null || !PsiTreeUtil.isAncestor(propertyClassOrObject, element, false)) { + trace.report(Errors.INACCESSIBLE_BACKING_FIELD.on(element)); + return true; + } + return false; + } + + private boolean isBackingFieldReference(@Nullable JetElement element, boolean[] error, boolean reportError) { + error[0] = false; + if (element instanceof JetSimpleNameExpression && ((JetSimpleNameExpression) element).getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER) { + return true; + } + if (element instanceof JetDotQualifiedExpression && isBackingFieldReference(((JetDotQualifiedExpression) element).getSelectorExpression(), error, false)) { + if (((JetDotQualifiedExpression) element).getReceiverExpression() instanceof JetThisExpression) { + return true; + } + error[0] = true; + if (reportError) { + trace.report(Errors.INACCESSIBLE_BACKING_FIELD.on(element)); + } + } + return false; + } + private void recordInitializedVariables(Collection declaredVariables, Map resultInfo) { for (Map.Entry entry : resultInfo.entrySet()) { VariableDescriptor variable = entry.getKey(); @@ -456,58 +516,55 @@ public class JetFlowInformationProvider { public void execute(Instruction instruction, @Nullable Map enterData, @Nullable Map exitData) { assert enterData != null && exitData != null; VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, false); - if (variableDescriptor != null && declaredVariables.contains(variableDescriptor)) { - PsiElement variableDeclarationElement = trace.get(BindingContext.DESCRIPTOR_TO_DECLARATION, variableDescriptor); - assert variableDeclarationElement instanceof JetDeclaration; - boolean isLocal = JetPsiUtil.isLocal((JetDeclaration) variableDeclarationElement); - if (isLocal) { - if (instruction instanceof WriteValueInstruction) { - JetElement element = ((WriteValueInstruction) instruction).getElement(); - if (enterData.get(variableDescriptor) == VariableStatus.WRITTEN || enterData.get(variableDescriptor) == VariableStatus.UNUSED) { - if (element instanceof JetBinaryExpression && ((JetBinaryExpression) element).getOperationToken() == JetTokens.EQ) { - JetExpression right = ((JetBinaryExpression) element).getRight(); - if (right != null) { - trace.report(Errors.UNUSED_VALUE.on(right, right, variableDescriptor)); - } - } - else if (element instanceof JetPostfixExpression) { - IElementType operationToken = ((JetPostfixExpression) element).getOperationSign().getReferencedNameElementType(); - if (operationToken == JetTokens.PLUSPLUS || operationToken == JetTokens.MINUSMINUS) { - trace.report(Errors.UNUSED_CHANGED_VALUE.on(element, element)); - } - } + if (variableDescriptor == null || !declaredVariables.contains(variableDescriptor)) return; + PsiElement variableDeclarationElement = trace.get(BindingContext.DESCRIPTOR_TO_DECLARATION, variableDescriptor); + assert variableDeclarationElement instanceof JetDeclaration; + if (!JetPsiUtil.isLocal((JetDeclaration) variableDeclarationElement)) return; + if (instruction instanceof WriteValueInstruction) { + JetElement element = ((WriteValueInstruction) instruction).getElement(); + if (enterData.get(variableDescriptor) == VariableStatus.WRITTEN || enterData.get(variableDescriptor) == VariableStatus.UNUSED) { + if (element instanceof JetBinaryExpression && ((JetBinaryExpression) element).getOperationToken() == JetTokens.EQ) { + JetExpression right = ((JetBinaryExpression) element).getRight(); + if (right != null) { + trace.report(Errors.UNUSED_VALUE.on(right, right, variableDescriptor)); } - } - else if (instruction instanceof VariableDeclarationInstruction) { - JetDeclaration element = ((VariableDeclarationInstruction) instruction).getVariableDeclarationElement(); - if (element instanceof JetNamedDeclaration) { - PsiElement nameIdentifier = ((JetNamedDeclaration) element).getNameIdentifier(); - PsiElement elementToMark = nameIdentifier != null ? nameIdentifier : element; - if (enterData.get(variableDescriptor) == VariableStatus.UNUSED) { - if (element instanceof JetProperty) { - trace.report(Errors.UNUSED_VARIABLE.on((JetProperty) element, elementToMark, variableDescriptor)); - } - else if (element instanceof JetParameter) { - PsiElement psiElement = element.getParent().getParent(); - if (psiElement instanceof JetFunction) { - boolean isMain = (psiElement instanceof JetNamedFunction) && JetMainDetector.isMain((JetNamedFunction) psiElement); - DeclarationDescriptor descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, psiElement); - assert descriptor instanceof FunctionDescriptor; - FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor; - if (!isMain && !functionDescriptor.getModality().isOverridable() && functionDescriptor.getOverriddenDescriptors().isEmpty()) { - trace.report(Errors.UNUSED_PARAMETER.on((JetParameter) element, elementToMark, variableDescriptor)); - } - } - } - } - else if (enterData.get(variableDescriptor) == VariableStatus.WRITTEN && element instanceof JetProperty) { - trace.report(Errors.ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE.on((JetNamedDeclaration) element, elementToMark, variableDescriptor)); - } + } + else if (element instanceof JetPostfixExpression) { + IElementType operationToken = ((JetPostfixExpression) element).getOperationSign().getReferencedNameElementType(); + if (operationToken == JetTokens.PLUSPLUS || operationToken == JetTokens.MINUSMINUS) { + trace.report(Errors.UNUSED_CHANGED_VALUE.on(element, element)); } - } } } + else if (instruction instanceof VariableDeclarationInstruction) { + JetDeclaration element = ((VariableDeclarationInstruction) instruction).getVariableDeclarationElement(); + if (element instanceof JetNamedDeclaration) { + PsiElement nameIdentifier = ((JetNamedDeclaration) element).getNameIdentifier(); + PsiElement elementToMark = nameIdentifier != null ? nameIdentifier : element; + if (enterData.get(variableDescriptor) == VariableStatus.UNUSED) { + if (element instanceof JetProperty) { + trace.report(Errors.UNUSED_VARIABLE.on((JetProperty) element, elementToMark, variableDescriptor)); + } + else if (element instanceof JetParameter) { + PsiElement psiElement = element.getParent().getParent(); + if (psiElement instanceof JetFunction) { + boolean isMain = (psiElement instanceof JetNamedFunction) && JetMainDetector.isMain((JetNamedFunction) psiElement); + DeclarationDescriptor descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, psiElement); + assert descriptor instanceof FunctionDescriptor; + FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor; + if (!isMain && !functionDescriptor.getModality().isOverridable() && functionDescriptor.getOverriddenDescriptors().isEmpty()) { + trace.report(Errors.UNUSED_PARAMETER.on((JetParameter) element, elementToMark, variableDescriptor)); + } + } + } + } + else if (enterData.get(variableDescriptor) == VariableStatus.WRITTEN && element instanceof JetProperty) { + trace.report(Errors.ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE.on((JetNamedDeclaration) element, elementToMark, variableDescriptor)); + } + } + + } } }); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/MutableClassDescriptor.java b/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/MutableClassDescriptor.java index 5590bcda559..3d80086d425 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/MutableClassDescriptor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/MutableClassDescriptor.java @@ -52,7 +52,7 @@ public class MutableClassDescriptor extends MutableDeclarationDescriptor impleme this.scopeForMemberLookup = new WritableScopeImpl(JetScope.EMPTY, this, redeclarationHandler).setDebugName("MemberLookup").changeLockLevel(WritableScope.LockLevel.BOTH); this.scopeForSupertypeResolution = new WritableScopeImpl(outerScope, this, redeclarationHandler).setDebugName("SupertypeResolution").changeLockLevel(WritableScope.LockLevel.BOTH); this.scopeForMemberResolution = new WritableScopeImpl(scopeForSupertypeResolution, this, redeclarationHandler).setDebugName("MemberResolution").changeLockLevel(WritableScope.LockLevel.BOTH); - this.scopeForInitializers = new WritableScopeImpl(scopeForMemberResolution, this, redeclarationHandler).setDebugName("PropertyInitializers").changeLockLevel(WritableScope.LockLevel.BOTH); + this.scopeForInitializers = new WritableScopeImpl(scopeForMemberResolution, this, redeclarationHandler).setDebugName("Initializers").changeLockLevel(WritableScope.LockLevel.BOTH); this.kind = kind; } @@ -137,7 +137,6 @@ public class MutableClassDescriptor extends MutableDeclarationDescriptor impleme callableMembers.add(propertyDescriptor); scopeForMemberLookup.addVariableDescriptor(propertyDescriptor); scopeForMemberResolution.addVariableDescriptor(propertyDescriptor); - scopeForInitializers.addPropertyDescriptorByFieldName("$" + propertyDescriptor.getName(), propertyDescriptor); } @Override diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java index 786acc783b2..1a91ce479d5 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java @@ -84,7 +84,11 @@ public interface Errors { SimpleDiagnosticFactory CANNOT_INFER_PARAMETER_TYPE = SimpleDiagnosticFactory.create(ERROR, "Cannot infer a type for this parameter. To specify it explicitly use the {(p : Type) => ...} notation"); - SimpleDiagnosticFactory NO_BACKING_FIELD = SimpleDiagnosticFactory.create(ERROR, "This property does not have a backing field"); + SimpleDiagnosticFactory NO_BACKING_FIELD_ABSTRACT_PROPERTY = SimpleDiagnosticFactory.create(ERROR, "This property doesn't have a backing field, because it's abstract"); + SimpleDiagnosticFactory NO_BACKING_FIELD_CUSTOM_ACCESSORS = SimpleDiagnosticFactory.create(ERROR, "This property doesn't have a backing field, because it has custom accessors without reference to the backing field"); + SimpleDiagnosticFactory INACCESSIBLE_BACKING_FIELD = SimpleDiagnosticFactory.create(ERROR, "The backing field is not accessible here"); + SimpleDiagnosticFactory NOT_PROPERTY_BACKING_FIELD = SimpleDiagnosticFactory.create(ERROR, "The referenced variable is not a property and doesn't have backing field"); + SimpleDiagnosticFactory MIXING_NAMED_AND_POSITIONED_ARGUMENTS = SimpleDiagnosticFactory.create(ERROR, "Mixing named and positioned arguments in not allowed"); SimpleDiagnosticFactory ARGUMENT_PASSED_TWICE = SimpleDiagnosticFactory.create(ERROR, "An argument is already passed for this parameter"); UnresolvedReferenceDiagnosticFactory NAMED_PARAMETER_NOT_FOUND = new UnresolvedReferenceDiagnosticFactory("Cannot find a parameter with this name");//SimpleDiagnosticFactory.create(ERROR, "Cannot find a parameter with this name"); @@ -166,7 +170,8 @@ public interface Errors { PsiElementOnlyDiagnosticFactory1 VAL_REASSIGNMENT = PsiElementOnlyDiagnosticFactory1.create(ERROR, "Val can not be reassigned", NAME); SimplePsiElementOnlyDiagnosticFactory VARIABLE_EXPECTED = new SimplePsiElementOnlyDiagnosticFactory(ERROR, "Variable expected"); - PsiElementOnlyDiagnosticFactory1 INITIALIZATION_USING_BACKING_FIELD = PsiElementOnlyDiagnosticFactory1.create(ERROR, "Initialization using backing field required", NAME); + PsiElementOnlyDiagnosticFactory1 INITIALIZATION_USING_BACKING_FIELD_CUSTOM_SETTER = PsiElementOnlyDiagnosticFactory1.create(ERROR, "This property has a custom setter, so initialization using backing field required", NAME); + PsiElementOnlyDiagnosticFactory1 INITIALIZATION_USING_BACKING_FIELD_OPEN_SETTER = PsiElementOnlyDiagnosticFactory1.create(ERROR, "Setter of this property can be overridden, so initialization using backing field required", NAME); PsiElementOnlyDiagnosticFactory1 FUNCTION_PARAMETERS_OF_INLINE_FUNCTION = PsiElementOnlyDiagnosticFactory1.create(ERROR, "Function parameters of inline function can only be invoked", NAME); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiUtil.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiUtil.java index bdd55b0b25e..8afc13f7b0f 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiUtil.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiUtil.java @@ -104,9 +104,9 @@ public class JetPsiUtil { } } - public static boolean isLocal(@NotNull JetDeclaration declaration) { - JetClassOrObject classOrObject = PsiTreeUtil.getParentOfType(declaration, JetClassOrObject.class); - JetDeclarationWithBody function = PsiTreeUtil.getParentOfType(declaration, JetDeclarationWithBody.class); + public static boolean isLocal(@NotNull JetElement element) { + JetClassOrObject classOrObject = PsiTreeUtil.getParentOfType(element, JetClassOrObject.class); + JetDeclarationWithBody function = PsiTreeUtil.getParentOfType(element, JetDeclarationWithBody.class); if (function != null && PsiTreeUtil.isAncestor(classOrObject, function, false)) { return true; } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java index db12d3887a8..81e0f647999 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java @@ -34,41 +34,11 @@ import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE; public class BodyResolver { private final TopDownAnalysisContext context; - private final ObservableBindingTrace traceForConstructors; - private final ObservableBindingTrace traceForMembers; + private final ObservableBindingTrace trace; public BodyResolver(TopDownAnalysisContext context) { this.context = context; - - // This allows access to backing fields - this.traceForConstructors = new ObservableBindingTrace(context.getTrace()).addHandler(BindingContext.REFERENCE_TARGET, new ObservableBindingTrace.RecordHandler() { - @Override - public void handleRecord(WritableSlice slice, JetReferenceExpression expression, DeclarationDescriptor descriptor) { - if (expression instanceof JetSimpleNameExpression) { - 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().report(NO_BACKING_FIELD.on(expression)); - } - } - } - } - }); - - // This tracks access to properties in order to register primary constructor parameters that yield real fields (JET-9) - // NOTE!!! This code is not used, because we do not allow constructor parameter access in member bodies - this.traceForMembers = new ObservableBindingTrace(context.getTrace()).addHandler(BindingContext.REFERENCE_TARGET, new ObservableBindingTrace.RecordHandler() { - @Override - public void handleRecord(WritableSlice slice, JetReferenceExpression expression, DeclarationDescriptor descriptor) { - if (descriptor instanceof PropertyDescriptor) { - PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor; - if (BodyResolver.this.context.getPrimaryConstructorParameterProperties().contains(propertyDescriptor)) { - traceForMembers.record(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor); - } - } - } - }); - + this.trace = context.getTrace(); } public void resolveBehaviorDeclarationBodies() { @@ -100,8 +70,8 @@ public class BodyResolver { final ConstructorDescriptor primaryConstructor = descriptor.getUnsubstitutedPrimaryConstructor(); final JetScope scopeForConstructor = primaryConstructor == null ? null - : FunctionDescriptorUtil.getFunctionInnerScope(descriptor.getScopeForSupertypeResolution(), primaryConstructor, traceForConstructors); - final ExpressionTypingServices typeInferrer = context.getSemanticServices().getTypeInferrerServices(traceForConstructors); // TODO : flow + : FunctionDescriptorUtil.getFunctionInnerScope(descriptor.getScopeForSupertypeResolution(), primaryConstructor, trace); + final ExpressionTypingServices typeInferrer = context.getSemanticServices().getTypeInferrerServices(trace); // TODO : flow final Map supertypes = Maps.newLinkedHashMap(); JetVisitorVoid visitor = new JetVisitorVoid() { @@ -268,7 +238,7 @@ public class BodyResolver { ConstructorDescriptor primaryConstructor = classDescriptor.getUnsubstitutedPrimaryConstructor(); assert primaryConstructor != null; final JetScope scopeForInitializers = classDescriptor.getScopeForInitializers(); - ExpressionTypingServices typeInferrer = context.getSemanticServices().getTypeInferrerServices(traceForConstructors); + ExpressionTypingServices typeInferrer = context.getSemanticServices().getTypeInferrerServices(trace); for (JetClassInitializer anonymousInitializer : anonymousInitializers) { typeInferrer.getType(scopeForInitializers, anonymousInitializer.getBody(), NO_EXPECTED_TYPE); } @@ -294,9 +264,9 @@ public class BodyResolver { private void resolveSecondaryConstructorBody(JetSecondaryConstructor declaration, final ConstructorDescriptor descriptor) { if (!context.completeAnalysisNeeded(declaration)) return; MutableClassDescriptor classDescriptor = (MutableClassDescriptor) descriptor.getContainingDeclaration(); - final JetScope scopeForSupertypeInitializers = FunctionDescriptorUtil.getFunctionInnerScope(classDescriptor.getScopeForSupertypeResolution(), descriptor, traceForConstructors); + final JetScope scopeForSupertypeInitializers = FunctionDescriptorUtil.getFunctionInnerScope(classDescriptor.getScopeForSupertypeResolution(), descriptor, trace); //contains only constructor parameters - final JetScope scopeForConstructorBody = FunctionDescriptorUtil.getFunctionInnerScope(classDescriptor.getScopeForInitializers(), descriptor, traceForConstructors); + final JetScope scopeForConstructorBody = FunctionDescriptorUtil.getFunctionInnerScope(classDescriptor.getScopeForInitializers(), descriptor, trace); //contains members & backing fields final CallResolver callResolver = new CallResolver(context.getSemanticServices(), DataFlowInfo.EMPTY); // TODO: dataFlowInfo @@ -360,7 +330,7 @@ public class BodyResolver { } JetExpression bodyExpression = declaration.getBodyExpression(); if (bodyExpression != null) { - ExpressionTypingServices typeInferrer = context.getSemanticServices().getTypeInferrerServices(traceForConstructors); + ExpressionTypingServices typeInferrer = context.getSemanticServices().getTypeInferrerServices(trace); typeInferrer.checkFunctionReturnType(scopeForConstructorBody, declaration, JetStandardClasses.getUnitType()); } @@ -417,7 +387,6 @@ public class BodyResolver { JetScope propertyDeclarationInnerScope = context.getDescriptorResolver().getPropertyDeclarationInnerScope( declaringScope, propertyDescriptor, propertyDescriptor.getTypeParameters(), propertyDescriptor.getReceiverParameter()); WritableScope accessorScope = new WritableScopeImpl(propertyDeclarationInnerScope, declaringScope.getContainingDeclaration(), new TraceBasedRedeclarationHandler(context.getTrace())).setDebugName("Accessor scope"); - accessorScope.addPropertyDescriptorByFieldName("$" + propertyDescriptor.getName(), propertyDescriptor); accessorScope.changeLockLevel(WritableScope.LockLevel.READING); return accessorScope; @@ -442,7 +411,7 @@ public class BodyResolver { } private ObservableBindingTrace createFieldTrackingTrace(final PropertyDescriptor propertyDescriptor) { - return new ObservableBindingTrace(traceForMembers).addHandler(BindingContext.REFERENCE_TARGET, new ObservableBindingTrace.RecordHandler() { + return new ObservableBindingTrace(trace).addHandler(BindingContext.REFERENCE_TARGET, new ObservableBindingTrace.RecordHandler() { @Override public void handleRecord(WritableSlice slice, JetReferenceExpression expression, DeclarationDescriptor descriptor) { if (expression instanceof JetSimpleNameExpression) { @@ -450,7 +419,7 @@ public class BodyResolver { if (simpleNameExpression.getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER) { // This check may be considered redundant as long as $x is only accessible from accessors to $x if (descriptor == propertyDescriptor) { // TODO : original? - traceForMembers.record(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor); // TODO: this context.getTrace()? + trace.record(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor); // TODO: this context.getTrace()? } } } @@ -460,7 +429,7 @@ public class BodyResolver { private void resolvePropertyInitializer(JetProperty property, PropertyDescriptor propertyDescriptor, JetExpression initializer, JetScope scope) { //JetFlowInformationProvider flowInformationProvider = context.getDescriptorResolver().computeFlowData(property, initializer); // TODO : flow JET-15 - ExpressionTypingServices typeInferrer = context.getSemanticServices().getTypeInferrerServices(traceForConstructors); + ExpressionTypingServices typeInferrer = context.getSemanticServices().getTypeInferrerServices(trace); JetType expectedTypeForInitializer = property.getPropertyTypeRef() != null ? propertyDescriptor.getOutType() : NO_EXPECTED_TYPE; JetType type = typeInferrer.getType(context.getDescriptorResolver().getPropertyDeclarationInnerScope(scope, propertyDescriptor, propertyDescriptor.getTypeParameters(), propertyDescriptor.getReceiverParameter()), initializer, expectedTypeForInitializer); // @@ -494,7 +463,7 @@ public class BodyResolver { JetScope declaringScope = this.context.getDeclaringScopes().get(declaration); assert declaringScope != null; - resolveFunctionBody(traceForMembers, declaration, descriptor, declaringScope); + resolveFunctionBody(trace, declaration, descriptor, declaringScope); assert descriptor.getReturnType() != null; } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DeclarationsChecker.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DeclarationsChecker.java index 34e55786dbc..9254fcdc0de 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DeclarationsChecker.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DeclarationsChecker.java @@ -181,7 +181,8 @@ public class DeclarationsChecker { 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)); + String name = property.getName(); + context.getTrace().report(ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS.on(property, abstractNode, name != null ? name : "", classDescriptor, (JetClass) classElement)); return; } if (classDescriptor.getKind() == ClassKind.TRAIT) { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java index a896a2bf4e5..09fef717803 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java @@ -64,7 +64,15 @@ public class CallResolver { if (referencedName == null) { return null; } - List> prioritizedTasks = TaskPrioritizers.PROPERTY_TASK_PRIORITIZER.computePrioritizedTasks(scope, call, referencedName, trace.getBindingContext(), dataFlowInfo); + TaskPrioritizer task_prioritizer; + if (nameExpression.getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER) { + referencedName = referencedName.substring(1); + task_prioritizer = TaskPrioritizers.PROPERTY_TASK_PRIORITIZER; + } + else { + task_prioritizer = TaskPrioritizers.VARIABLE_TASK_PRIORITIZER; + } + List> prioritizedTasks = task_prioritizer.computePrioritizedTasks(scope, call, referencedName, trace.getBindingContext(), dataFlowInfo); return resolveCallToDescriptor(trace, scope, call, expectedType, prioritizedTasks, nameExpression); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/TaskPrioritizers.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/TaskPrioritizers.java index 292cfd760d1..c5a4f75f8b1 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/TaskPrioritizers.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/TaskPrioritizers.java @@ -1,5 +1,6 @@ package org.jetbrains.jet.lang.resolve.calls; +import com.google.common.collect.Lists; import com.google.common.collect.Sets; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.descriptors.*; @@ -8,10 +9,7 @@ import org.jetbrains.jet.lang.types.ErrorUtils; import org.jetbrains.jet.lang.types.JetStandardClasses; import org.jetbrains.jet.lang.types.JetType; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.Set; +import java.util.*; /** * @author abreslav @@ -83,7 +81,7 @@ public class TaskPrioritizers { } }; - /*package*/ static TaskPrioritizer PROPERTY_TASK_PRIORITIZER = new TaskPrioritizer() { + /*package*/ static TaskPrioritizer VARIABLE_TASK_PRIORITIZER = new TaskPrioritizer() { @NotNull @Override @@ -115,4 +113,34 @@ public class TaskPrioritizers { return Collections.emptyList(); } }; + + /*package*/ static TaskPrioritizer PROPERTY_TASK_PRIORITIZER = new TaskPrioritizer() { + private Collection filterProperties(Collection variableDescriptors) { + ArrayList properties = Lists.newArrayList(); + for (VariableDescriptor descriptor : variableDescriptors) { + if (descriptor instanceof PropertyDescriptor) { + properties.add(descriptor); + } + } + return properties; + } + + @NotNull + @Override + protected Collection getNonExtensionsByName(JetScope scope, String name) { + return filterProperties(VARIABLE_TASK_PRIORITIZER.getNonExtensionsByName(scope, name)); + } + + @NotNull + @Override + protected Collection getMembersByName(@NotNull JetType receiver, String name) { + return filterProperties(VARIABLE_TASK_PRIORITIZER.getMembersByName(receiver, name)); + } + + @NotNull + @Override + protected Collection getExtensionsByName(JetScope scope, String name) { + return filterProperties(VARIABLE_TASK_PRIORITIZER.getExtensionsByName(scope, name)); + } + }; } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/WritableScope.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/WritableScope.java index e9002d3cc64..e0d2df9cc51 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/WritableScope.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/WritableScope.java @@ -36,8 +36,6 @@ public interface WritableScope extends JetScope { @Nullable NamespaceDescriptor getDeclaredNamespace(@NotNull String name); - void addPropertyDescriptorByFieldName(@NotNull String fieldName, @NotNull PropertyDescriptor propertyDescriptor); - void importScope(@NotNull JetScope imported); void setImplicitReceiver(@NotNull ReceiverDescriptor implicitReceiver); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/WritableScopeImpl.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/WritableScopeImpl.java index bacaa26ea9e..77bb17c3982 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/WritableScopeImpl.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/WritableScopeImpl.java @@ -338,17 +338,6 @@ public class WritableScopeImpl extends WritableScopeWithImports { return propertyDescriptorsByFieldNames; } - @Override - public void addPropertyDescriptorByFieldName(@NotNull String fieldName, @NotNull PropertyDescriptor propertyDescriptor) { - checkMayWrite(); - - if (!fieldName.startsWith("$")) { - throw new IllegalStateException(); - } - - getPropertyDescriptorsByFieldNames().put(fieldName, propertyDescriptor); - } - @Override public PropertyDescriptor getPropertyByFieldReference(@NotNull String fieldName) { checkMayRead(); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/WriteThroughScope.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/WriteThroughScope.java index c00a3e04547..5f123dc5a44 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/WriteThroughScope.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/WriteThroughScope.java @@ -176,13 +176,6 @@ public class WriteThroughScope extends WritableScopeWithImports { return writableWorker.getDeclaredNamespace(name); } - @Override - public void addPropertyDescriptorByFieldName(@NotNull String fieldName, @NotNull PropertyDescriptor propertyDescriptor) { - checkMayWrite(); - - writableWorker.addPropertyDescriptorByFieldName(fieldName, propertyDescriptor); - } - @Override public void importScope(@NotNull JetScope imported) { checkMayWrite(); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java index 09ef1b6e574..c59643b06d6 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java @@ -47,22 +47,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { public JetType visitSimpleNameExpression(JetSimpleNameExpression expression, ExpressionTypingContext context) { // TODO : other members // TODO : type substitutions??? - String referencedName = expression.getReferencedName(); - if (expression.getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER - && referencedName != null) { - PropertyDescriptor property = context.scope.getPropertyByFieldReference(referencedName); - if (property == null) { - context.trace.report(UNRESOLVED_REFERENCE.on(expression)); - } - else { - context.trace.record(REFERENCE_TARGET, expression, property); - return DataFlowUtils.checkType(property.getOutType(), expression, context); - } - } - else { - return DataFlowUtils.checkType(getSelectorReturnType(NO_RECEIVER, null, expression, context), expression, context); // TODO : Extensions to this - } - return null; + return DataFlowUtils.checkType(getSelectorReturnType(NO_RECEIVER, null, expression, context), expression, context); // TODO : Extensions to this } private JetType lookupNamespaceOrClassObject(JetSimpleNameExpression expression, String referencedName, ExpressionTypingContext context) { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorForStatements.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorForStatements.java index 59642c4c73e..c483bfff97c 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorForStatements.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorForStatements.java @@ -213,16 +213,7 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito basic.checkLValue(context.trace, expression.getLeft()); } if (left instanceof JetSimpleNameExpression) { - JetSimpleNameExpression simpleName = (JetSimpleNameExpression) left; - String referencedName = simpleName.getReferencedName(); - if (simpleName.getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER - && referencedName != null) { - PropertyDescriptor property = context.scope.getPropertyByFieldReference(referencedName); - if (property != null) { - context.trace.record(BindingContext.VARIABLE_ASSIGNMENT, simpleName, property); - } - } - ExpressionTypingUtils.checkWrappingInRef(simpleName, context); + ExpressionTypingUtils.checkWrappingInRef(left, context); } return checkExpectedType(expression, contextWithExpectedType); } diff --git a/compiler/testData/diagnostics/tests/AnonymousInitializers.jet b/compiler/testData/diagnostics/tests/AnonymousInitializers.jet index 74efce233c3..515ed807058 100644 --- a/compiler/testData/diagnostics/tests/AnonymousInitializers.jet +++ b/compiler/testData/diagnostics/tests/AnonymousInitializers.jet @@ -24,7 +24,7 @@ class WithC() { { val z = b val zz = x - val zzz = $a + val zzz = $a } this(a : Int) : this() { diff --git a/compiler/testData/diagnostics/tests/Properties.jet b/compiler/testData/diagnostics/tests/Properties.jet index 1b3ca0b8dee..ecb73643452 100644 --- a/compiler/testData/diagnostics/tests/Properties.jet +++ b/compiler/testData/diagnostics/tests/Properties.jet @@ -1,4 +1,4 @@ - var x : Int = 1 + x +var x : Int = 1 + x get() : Int = 1 set(ref value : Long) { $x = value.int @@ -14,16 +14,16 @@ class Test() { var a : Int = 111 - var b : Int get() = $a; set(x) {a = x; $a = x} + var b : Int get() = $a; set(x) {a = x; $a = x} this(i : Int) : this() { - $b = $a - $a = $b - a = $b + $b = $a + $a = $b + a = $b } fun f() { - $b = $a - a = $b + $b = $a + a = $b } public val i = 1 } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/UninitializedOrReassignedVariables.jet b/compiler/testData/diagnostics/tests/UninitializedOrReassignedVariables.jet index a3dcf7c81cf..c9c53ef1fe7 100644 --- a/compiler/testData/diagnostics/tests/UninitializedOrReassignedVariables.jet +++ b/compiler/testData/diagnostics/tests/UninitializedOrReassignedVariables.jet @@ -137,14 +137,14 @@ class AnonymousInitializers(var a: String, val b: String) { { $i = 13 - $j = 30 - j = 34 + $j = 30 + j = 34 } val k: String { if (1 < 3) { - k = "a" + k = "a" } else { $k = "b" @@ -288,7 +288,7 @@ class TestObjectExpression() { val y : Int { if (true) - x = 12 + x = 12 else $x = 1 } @@ -349,4 +349,4 @@ fun test(m : M) { fun test1(m : M) { m.x++ m.y-- -} +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/backingField/ReadInFunction.jet b/compiler/testData/diagnostics/tests/backingField/ReadInFunction.jet index dac44656db9..9ed84b87bcc 100644 --- a/compiler/testData/diagnostics/tests/backingField/ReadInFunction.jet +++ b/compiler/testData/diagnostics/tests/backingField/ReadInFunction.jet @@ -1,4 +1,4 @@ class ReadByAnotherPropertyInitializer() { val a = 1 - fun ff() = $a + fun ff() = $a } diff --git a/compiler/testData/diagnostics/tests/backingField/ReadNonexistentAbstractPropertyInAnonymous.jet b/compiler/testData/diagnostics/tests/backingField/ReadNonexistentAbstractPropertyInAnonymous.jet index 31ecbc026b3..5c7863b359f 100644 --- a/compiler/testData/diagnostics/tests/backingField/ReadNonexistentAbstractPropertyInAnonymous.jet +++ b/compiler/testData/diagnostics/tests/backingField/ReadNonexistentAbstractPropertyInAnonymous.jet @@ -2,6 +2,6 @@ abstract class ReadNonexistent() { abstract val aa: Int { - val x = $aa + val x = $aa } -} +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/backingField/ReadNonexistentAbstractPropertyInFunction.jet b/compiler/testData/diagnostics/tests/backingField/ReadNonexistentAbstractPropertyInFunction.jet index bb29feddfae..dc3ddc9cc28 100644 --- a/compiler/testData/diagnostics/tests/backingField/ReadNonexistentAbstractPropertyInFunction.jet +++ b/compiler/testData/diagnostics/tests/backingField/ReadNonexistentAbstractPropertyInFunction.jet @@ -1,5 +1,5 @@ abstract class ReadNonexistent { abstract val aa: Int - fun ff() = $aa -} + fun ff() = $aa +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/backingField/ReadNonexistentCustomGetInAnonymous.jet b/compiler/testData/diagnostics/tests/backingField/ReadNonexistentCustomGetInAnonymous.jet index be275ba22fc..d90f9631d6f 100644 --- a/compiler/testData/diagnostics/tests/backingField/ReadNonexistentCustomGetInAnonymous.jet +++ b/compiler/testData/diagnostics/tests/backingField/ReadNonexistentCustomGetInAnonymous.jet @@ -3,6 +3,6 @@ class ReadNonexistent() { get() = 1 { - val x = $a + val x = $a } -} +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/backingField/ReadNonexistentCustomGetInAnotherInitializer.jet b/compiler/testData/diagnostics/tests/backingField/ReadNonexistentCustomGetInAnotherInitializer.jet index 80b852f68b0..5754f538a8e 100644 --- a/compiler/testData/diagnostics/tests/backingField/ReadNonexistentCustomGetInAnotherInitializer.jet +++ b/compiler/testData/diagnostics/tests/backingField/ReadNonexistentCustomGetInAnotherInitializer.jet @@ -2,5 +2,5 @@ class CustomValNoBackingField() { val a: Int get() = 1 - val b = $a -} + val b = $a +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/backingField/ReadNonexistentDeclaredInHigher.jet b/compiler/testData/diagnostics/tests/backingField/ReadNonexistentDeclaredInHigher.jet index 1ec73f2984e..5754f538a8e 100644 --- a/compiler/testData/diagnostics/tests/backingField/ReadNonexistentDeclaredInHigher.jet +++ b/compiler/testData/diagnostics/tests/backingField/ReadNonexistentDeclaredInHigher.jet @@ -1,7 +1,6 @@ -val y = 1 - -class A() { - { - val x = $y - } -} +class CustomValNoBackingField() { + val a: Int + get() = 1 + + val b = $a +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/backingField/WriteNonexistentDeclaredInHigher.jet b/compiler/testData/diagnostics/tests/backingField/WriteNonexistentDeclaredInHigher.jet index 039a381fd55..26cb04ea093 100644 --- a/compiler/testData/diagnostics/tests/backingField/WriteNonexistentDeclaredInHigher.jet +++ b/compiler/testData/diagnostics/tests/backingField/WriteNonexistentDeclaredInHigher.jet @@ -2,6 +2,6 @@ val y = 1 class A() { { - $y = 1 + $y = 1 } -} +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/backingField/kt462BackingFieldsResolve.jet b/compiler/testData/diagnostics/tests/backingField/kt462BackingFieldsResolve.jet new file mode 100644 index 00000000000..af0ec07ea8d --- /dev/null +++ b/compiler/testData/diagnostics/tests/backingField/kt462BackingFieldsResolve.jet @@ -0,0 +1,172 @@ +//KT-462 Consider allowing initializing properties via property names when it is safe +//KT-598 Allow to use backing fields after this expression + +namespace kt462 + +abstract class TestInitializationWithoutBackingField() { + val valWithBackingField : Int + { + valWithBackingField = 2 + } + + val valWithoutBackingField : Int + get() = 42 + { + valWithoutBackingField = 45 + } + + var finalDefaultVar : Int + { + finalDefaultVar = 3 + } + + open var openVar : Int + { + openVar = 4 + } + + var varWithCustomSetter : Int + set(v: Int) { + $varWithCustomSetter = v + } + { + varWithCustomSetter = 3 + } + + var varWithoutBackingField : Int + get() = 3 + set(v: Int) {} + { + varWithoutBackingField = 4 + } + + abstract var abstractVar : Int + { + abstractVar = 34 + } +} + +abstract class TestInitializationThroughBackingField() { + val valWithBackingField : Int + { + $valWithBackingField = 2 + } + + val valWithoutBackingField : Int + get() = 42 + { + $valWithoutBackingField = 45 + } + + var finalDefaultVar : Int + { + $finalDefaultVar = 3 + } + + open var openVar : Int + { + $openVar = 4 + } + + var varWithCustomSetter : Int + set(v: Int) { + $varWithCustomSetter = v + } + { + $varWithCustomSetter = 3 + } + + var varWithoutBackingField : Int + get() = 3 + set(v: Int) {} + { + $varWithoutBackingField = 4 + } + + abstract var abstractVar : Int + { + $abstractVar = 34 + } +} + +class TestBackingFieldsVisibility() { + var a : Int = 712 + { + $a = 37 + this.$a = 357 + } + + fun foo() { + $a = 334 + this.$a = 347 + } + + fun foo(a: TestBackingFieldsVisibility) { + val b : Int = 3 + $b = 342 + a.$a = 3 + } + + val x = $namespaceLevelVar + + class Inner() { + val z = this@TestBackingFieldsVisibility.$x + } + + abstract val w = 11 + get() = $w //test there is no second error here +} + +val namespaceLevelVar = 11 + +class T() { + val z : Int get() = 42 + + { + this.$z = 34 + } + + fun foo() { + this.$z = 343 + } + + val a = object { + val x = $z + { + $z = 23 + } + } + + var x: Int = 2 + get() { + val o = object { + { + $x = 34 + } + fun foo() { + $x = 23 + } + } + return 1 + } + + var r: Int = $x + set(v: Int) { + if (true) { + $r = 33 + } + else { + $r = 35 + } + } + + fun bar() { + $x = 34 + val o = object { + val y = $x + { + $x = 422 + } + } + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/control-flow-analysis/kt510.jet b/compiler/testData/diagnostics/tests/control-flow-analysis/kt510.jet index 050c84f2cf4..41feb040f2a 100644 --- a/compiler/testData/diagnostics/tests/control-flow-analysis/kt510.jet +++ b/compiler/testData/diagnostics/tests/control-flow-analysis/kt510.jet @@ -5,7 +5,7 @@ namespace kt510 public open class Identifier1() { var field : Boolean { - field = false; // error + field = false; // error } } @@ -13,6 +13,6 @@ public open class Identifier1() { public open class Identifier2() { var field : Boolean { - this.field = false; + this.field = false; } } \ No newline at end of file diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/AddModifierFix.java b/idea/src/org/jetbrains/jet/plugin/quickfix/AddModifierFix.java index 1872257488d..45918386a78 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/AddModifierFix.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/AddModifierFix.java @@ -31,7 +31,7 @@ public class AddModifierFix extends JetIntentionAction { } @NotNull - /*package*/ static String getElementName(JetModifierListOwner modifierListOwner) { + /*package*/ static String getElementName(@NotNull JetModifierListOwner modifierListOwner) { String name = null; if (modifierListOwner instanceof PsiNameIdentifierOwner) { PsiElement nameIdentifier = ((PsiNameIdentifierOwner) modifierListOwner).getNameIdentifier(); diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/ChangeToBackingFieldFix.java b/idea/src/org/jetbrains/jet/plugin/quickfix/ChangeToBackingFieldFix.java index b6d43fa5863..dae43ef8e60 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/ChangeToBackingFieldFix.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/ChangeToBackingFieldFix.java @@ -36,12 +36,7 @@ public class ChangeToBackingFieldFix extends JetIntentionAction createFactory() { diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/ChangeVariableMutabilityFix.java b/idea/src/org/jetbrains/jet/plugin/quickfix/ChangeVariableMutabilityFix.java index 23a01d37acf..9b7cdba6ee4 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/ChangeVariableMutabilityFix.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/ChangeVariableMutabilityFix.java @@ -78,8 +78,9 @@ public class ChangeVariableMutabilityFix implements IntentionAction { @Override public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { - JetProperty element = getCorrespondingProperty(editor, (JetFile)file); - JetProperty newElement = (JetProperty) element.copy(); + JetProperty property = getCorrespondingProperty(editor, (JetFile)file); + assert property != null && !property.isVar(); + JetProperty newElement = (JetProperty) property.copy(); if (newElement.isVar()) { PsiElement varElement = newElement.getNode().findChildByType(JetTokens.VAR_KEYWORD).getPsi(); @@ -94,7 +95,7 @@ public class ChangeVariableMutabilityFix implements IntentionAction { PsiElement varElement = varProperty.getNode().findChildByType(JetTokens.VAR_KEYWORD).getPsi(); CodeEditUtil.replaceChild(newElement.getNode(), valElement.getNode(), varElement.getNode()); } - element.replace(newElement); + property.replace(newElement); } @Override diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixes.java b/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixes.java index 3a98d0803ac..d34688b4736 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixes.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixes.java @@ -111,8 +111,10 @@ public class QuickFixes { add(Errors.ILLEGAL_MODIFIER, removeModifierFactory); add(Errors.PUBLIC_MEMBER_SHOULD_SPECIFY_TYPE, AddReturnTypeFix.createFactory()); - - add(Errors.INITIALIZATION_USING_BACKING_FIELD, ChangeToBackingFieldFix.createFactory()); + + JetIntentionActionFactory changeToBackingFieldFactory = ChangeToBackingFieldFix.createFactory(); + add(Errors.INITIALIZATION_USING_BACKING_FIELD_CUSTOM_SETTER, changeToBackingFieldFactory); + add(Errors.INITIALIZATION_USING_BACKING_FIELD_OPEN_SETTER, changeToBackingFieldFactory); ImplementMethodsHandler implementMethodsHandler = new ImplementMethodsHandler(); actionMap.put(Errors.ABSTRACT_MEMBER_NOT_IMPLEMENTED, implementMethodsHandler); diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/RemoveModifierFix.java b/idea/src/org/jetbrains/jet/plugin/quickfix/RemoveModifierFix.java index efe1337520b..fc0dfc0b647 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/RemoveModifierFix.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/RemoveModifierFix.java @@ -35,7 +35,7 @@ public class RemoveModifierFix { if (isRedundant) { return JetBundle.message("remove.redundant.modifier", modifier.getValue()); } - if (element != null && modifier == JetTokens.ABSTRACT_KEYWORD || modifier == JetTokens.OPEN_KEYWORD) { + if (element != null && (modifier == JetTokens.ABSTRACT_KEYWORD || modifier == JetTokens.OPEN_KEYWORD)) { return JetBundle.message("make.element.not.modifier", AddModifierFix.getElementName(element), modifier.getValue()); } return JetBundle.message("remove.modifier", modifier.getValue()); diff --git a/idea/testData/checker/Properties.jet b/idea/testData/checker/Properties.jet index 8f9ae255576..7ca6725365e 100644 --- a/idea/testData/checker/Properties.jet +++ b/idea/testData/checker/Properties.jet @@ -14,7 +14,7 @@ class Test() { var a : Int = 111 - var b : Int get() = $a; set(x) {a = x; $a = x} + var b : Int get() = $a; set(x) {a = x; $a = x} this(i : Int) : this() { $b = $a @@ -22,7 +22,7 @@ class Test() { a = $b } fun f() { - $b = $a + $b = $a a = $b } } \ No newline at end of file diff --git a/idea/testData/quickfix/variables/changeToBackingField/afterBFRequired.kt b/idea/testData/quickfix/variables/changeToBackingField/afterBFRequired.kt index b136e4ab85d..87c441a372c 100644 --- a/idea/testData/quickfix/variables/changeToBackingField/afterBFRequired.kt +++ b/idea/testData/quickfix/variables/changeToBackingField/afterBFRequired.kt @@ -1,6 +1,7 @@ // "Change reference to backing field" "true" class A() { - val a : Int + var a : Int + set(v) {} { $a = 1 } diff --git a/idea/testData/quickfix/variables/changeToBackingField/afterKt510.kt b/idea/testData/quickfix/variables/changeToBackingField/afterKt510.kt index c48a4e73754..5ce1e112210 100644 --- a/idea/testData/quickfix/variables/changeToBackingField/afterKt510.kt +++ b/idea/testData/quickfix/variables/changeToBackingField/afterKt510.kt @@ -1,7 +1,8 @@ // "Change reference to backing field" "true" public open class Identifier() { var field : Boolean + set(v) {} { - $field = false; + this.$field = false; } } \ No newline at end of file diff --git a/idea/testData/quickfix/variables/changeToBackingField/beforeBFRequired.kt b/idea/testData/quickfix/variables/changeToBackingField/beforeBFRequired.kt index 3c9b678fe9b..48fdd186232 100644 --- a/idea/testData/quickfix/variables/changeToBackingField/beforeBFRequired.kt +++ b/idea/testData/quickfix/variables/changeToBackingField/beforeBFRequired.kt @@ -1,6 +1,7 @@ // "Change reference to backing field" "true" class A() { - val a : Int + var a : Int + set(v) {} { a = 1 } diff --git a/idea/testData/quickfix/variables/changeToBackingField/beforeKt510.kt b/idea/testData/quickfix/variables/changeToBackingField/beforeKt510.kt index 46ccdd440a6..ad1ef0139cd 100644 --- a/idea/testData/quickfix/variables/changeToBackingField/beforeKt510.kt +++ b/idea/testData/quickfix/variables/changeToBackingField/beforeKt510.kt @@ -1,6 +1,7 @@ // "Change reference to backing field" "true" public open class Identifier() { var field : Boolean + set(v) {} { this.field = false; }