Issue 43200. Report ASSIGNMENT_TO_FINAL when 'final late' has an initializer.

Bug: https://github.com/dart-lang/sdk/issues/43200
Change-Id: Ib79b4e8246f8f67661212c3d107de961d72132b4
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/161471
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov 2020-09-02 15:38:22 +00:00 committed by commit-bot@chromium.org
parent ab16d79af9
commit 01024dfa5a
6 changed files with 89 additions and 38 deletions

View file

@ -265,9 +265,9 @@ class AssignmentExpressionResolver {
CompileTimeErrorCode.ASSIGNMENT_TO_CONST,
left,
);
} else if (variable.isFinal && !variable.isLate) {
} else if (variable.setter == null) {
if (variable is FieldElement) {
if (variable.setter == null && variable.isSynthetic) {
if (variable.isSynthetic) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER,
left,

View file

@ -1647,39 +1647,53 @@ class ErrorVerifier extends RecursiveAstVisitor<void> {
highlightedNode = expression.propertyName;
}
// check if element is assignable
Element toVariable(Element element) {
return element is PropertyAccessorElement ? element.variable : element;
}
element = toVariable(element);
if (element is VariableElement) {
if (element.isConst) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.ASSIGNMENT_TO_CONST, expression);
} else if (element.isFinal && !element.isLate) {
if (element is FieldElementImpl) {
if (element.setter == null && element.isSynthetic) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER,
highlightedNode,
[element.name, element.enclosingElement.displayName]);
} else {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.ASSIGNMENT_TO_FINAL,
highlightedNode,
[element.name]);
}
return;
}
if (_isNonNullableByDefault && element is PromotableElement) {
CompileTimeErrorCode.ASSIGNMENT_TO_CONST,
expression,
);
} else if (element.isFinal) {
if (_isNonNullableByDefault) {
// Handled during resolution, with flow analysis.
} else {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_LOCAL,
highlightedNode,
[element.name]);
CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_LOCAL,
expression,
[element.name],
);
}
}
} else if (element is PropertyAccessorElement && element.isGetter) {
var variable = element.variable;
if (variable.isConst) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.ASSIGNMENT_TO_CONST,
expression,
);
} else if (variable.setter == null) {
if (variable is FieldElement) {
if (variable.isSynthetic) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER,
highlightedNode,
[variable.name, variable.enclosingElement.displayName],
);
} else {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.ASSIGNMENT_TO_FINAL,
highlightedNode,
[variable.name],
);
}
return;
}
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_LOCAL,
highlightedNode,
[variable.name],
);
}
} else if (element is FunctionElement) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.ASSIGNMENT_TO_FUNCTION, expression);

View file

@ -160,13 +160,15 @@ void f(int value) {
}
test_topLevelVariable_late() async {
await assertNoErrorsInCode('''
await assertErrorsInCode('''
late final int a;
late final int b = 0;
void f() {
a = 1;
b = 1;
}
''');
''', [
error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_LOCAL, 62, 1),
]);
}
}

View file

@ -46,8 +46,23 @@ f() {
@reflectiveTest
class AssignmentToFinalWithNullSafetyTest extends AssignmentToFinalTest
with WithNullSafetyMixin {
test_field_late() async {
await assertNoErrorsInCode('''
test_field_late_propertyAccess() async {
await assertErrorsInCode('''
class A {
late final int a;
late final int b = 0;
void m() {
this.a = 1;
this.b = 1;
}
}
''', [
error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 92, 1),
]);
}
test_field_late_simpleIdentifier() async {
await assertErrorsInCode('''
class A {
late final int a;
late final int b = 0;
@ -56,11 +71,29 @@ class A {
b = 1;
}
}
''');
''', [
error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 82, 1),
]);
}
test_field_static_late() async {
await assertNoErrorsInCode('''
test_field_static_final_late_prefixedIdentifier() async {
await assertErrorsInCode('''
class A {
static late final int a;
static late final int b = 0;
}
void f() {
A.a = 1;
A.b = 1;
}
''', [
error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 97, 1),
]);
}
test_field_static_final_late_simpleIdentifier() async {
await assertErrorsInCode('''
class A {
static late final int a;
static late final int b = 0;
@ -69,7 +102,9 @@ class A {
b = 1;
}
}
''');
''', [
error(CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, 96, 1),
]);
}
test_set_abstract_field_final_invalid() async {

View file

@ -109,7 +109,7 @@ void test1() {
c1a.m1 = 0;
// ^^
// [analyzer] COMPILE_TIME_ERROR.ASSIGNMENT_TO_FINAL_LOCAL
// [analyzer] COMPILE_TIME_ERROR.ASSIGNMENT_TO_FINAL_NO_SETTER
// [cfe] The setter 'm1' isn't defined for the class 'C1<int>'.
c1a.m2;
@ -195,7 +195,7 @@ void test1() {
c1c.m1 = 0;
// ^^
// [analyzer] COMPILE_TIME_ERROR.ASSIGNMENT_TO_FINAL_LOCAL
// [analyzer] COMPILE_TIME_ERROR.ASSIGNMENT_TO_FINAL_NO_SETTER
// [cfe] The setter 'm1' isn't defined for the class 'C1<Object>'.
c1c.m2;

View file

@ -109,7 +109,7 @@ void test1() {
c1a.m1 = 0;
// ^^
// [analyzer] COMPILE_TIME_ERROR.ASSIGNMENT_TO_FINAL_LOCAL
// [analyzer] COMPILE_TIME_ERROR.ASSIGNMENT_TO_FINAL_NO_SETTER
// [cfe] The setter 'm1' isn't defined for the class 'C1<int>'.
c1a.m2;