@@ -10,6 +10,7 @@ import 'package:analyzer/dart/element/type_provider.dart';
1010import 'package:analyzer/error/listener.dart' ;
1111import 'package:analyzer/src/dart/ast/ast.dart' ;
1212import 'package:analyzer/src/dart/ast/token.dart' ;
13+ import 'package:analyzer/src/dart/element/element.dart' ;
1314import 'package:analyzer/src/dart/element/type.dart' ;
1415import 'package:analyzer/src/dart/element/type_system.dart' ;
1516import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart' ;
@@ -62,16 +63,9 @@ class AssignmentExpressionResolver {
6263 var left = node.leftHandSide;
6364 var right = node.rightHandSide;
6465
65- // Case `id = e`.
66- // Consider an assignment of the form `id = e`, where `id` is an identifier.
6766 if (left is SimpleIdentifier ) {
68- var leftLookup = _resolver.nameScope.lookup2 (left.name);
69- // When the lexical lookup yields a local variable `v`.
70- var leftGetter = leftLookup.getter;
71- if (leftGetter is VariableElement ) {
72- _resolve_SimpleIdentifier_LocalVariable (node, left, leftGetter, right);
73- return ;
74- }
67+ _resolve_SimpleIdentifier (node, left);
68+ return ;
7569 }
7670
7771 left? .accept (_resolver);
@@ -87,7 +81,8 @@ class AssignmentExpressionResolver {
8781 _resolver.setWriteElement (left, null );
8882 }
8983
90- _resolve1 (node);
84+ _resolve1 (node, getReadType (left));
85+
9186 _setRhsContext (node, left.staticType, operator , right);
9287
9388 _flowAnalysis? .assignmentExpression (node);
@@ -105,18 +100,6 @@ class AssignmentExpressionResolver {
105100 _flowAnalysis? .assignmentExpression_afterRight (node);
106101 }
107102
108- /// Set the static type of [node] to be the least upper bound of the static
109- /// types of subexpressions [expr1] and [expr2] .
110- ///
111- /// TODO(scheglov) this is duplicate
112- void _analyzeLeastUpperBound (
113- Expression node, Expression expr1, Expression expr2) {
114- DartType staticType1 = getReadType (expr1);
115- DartType staticType2 = getReadType (expr2);
116-
117- _analyzeLeastUpperBoundTypes (node, staticType1, staticType2);
118- }
119-
120103 /// Set the static type of [node] to be the least upper bound of the static
121104 /// types [staticType1] and [staticType2] .
122105 ///
@@ -138,6 +121,55 @@ class AssignmentExpressionResolver {
138121 _inferenceHelper.recordStaticType (node, staticType);
139122 }
140123
124+ void _checkForInvalidAssignment (
125+ DartType writeType,
126+ Expression right,
127+ DartType rightType,
128+ ) {
129+ // TODO(scheglov) should not happen
130+ if (writeType == null ) {
131+ return ;
132+ }
133+
134+ if (! writeType.isVoid && _checkForUseOfVoidResult (right)) {
135+ return ;
136+ }
137+
138+ if (_typeSystem.isAssignableTo2 (rightType, writeType)) {
139+ return ;
140+ }
141+
142+ _errorReporter.reportErrorForNode (
143+ CompileTimeErrorCode .INVALID_ASSIGNMENT ,
144+ right,
145+ [rightType, writeType],
146+ );
147+ }
148+
149+ /// Check for situations where the result of a method or function is used,
150+ /// when it returns 'void'. Or, in rare cases, when other types of expressions
151+ /// are void, such as identifiers.
152+ ///
153+ /// See [StaticWarningCode.USE_OF_VOID_RESULT] .
154+ /// TODO(scheglov) this is duplicate
155+ bool _checkForUseOfVoidResult (Expression expression) {
156+ if (expression == null ||
157+ ! identical (expression.staticType, VoidTypeImpl .instance)) {
158+ return false ;
159+ }
160+
161+ if (expression is MethodInvocation ) {
162+ SimpleIdentifier methodName = expression.methodName;
163+ _errorReporter.reportErrorForNode (
164+ CompileTimeErrorCode .USE_OF_VOID_RESULT , methodName, []);
165+ } else {
166+ _errorReporter.reportErrorForNode (
167+ CompileTimeErrorCode .USE_OF_VOID_RESULT , expression, []);
168+ }
169+
170+ return true ;
171+ }
172+
141173 /// Return the non-nullable variant of the [type] if null safety is enabled,
142174 /// otherwise return the type itself.
143175 ///
@@ -175,14 +207,105 @@ class AssignmentExpressionResolver {
175207 }
176208 }
177209
178- void _resolve1 (AssignmentExpressionImpl node) {
210+ void _reportNotSetter (
211+ Expression left,
212+ Element requested,
213+ Element recovery,
214+ ) {
215+ if (requested != null ) {
216+ if (requested is VariableElement ) {
217+ if (requested.isConst) {
218+ _errorReporter.reportErrorForNode (
219+ CompileTimeErrorCode .ASSIGNMENT_TO_CONST ,
220+ left,
221+ );
222+ } else if (requested.isFinal) {
223+ if (_isNonNullableByDefault) {
224+ // Handled during resolution, with flow analysis.
225+ } else {
226+ _errorReporter.reportErrorForNode (
227+ CompileTimeErrorCode .ASSIGNMENT_TO_FINAL_LOCAL ,
228+ left,
229+ [requested.name],
230+ );
231+ }
232+ }
233+ }
234+ return ;
235+ }
236+
237+ if (recovery is ClassElement ||
238+ recovery is DynamicElementImpl ||
239+ recovery is FunctionTypeAliasElement ||
240+ recovery is TypeParameterElement ) {
241+ _errorReporter.reportErrorForNode (
242+ CompileTimeErrorCode .ASSIGNMENT_TO_TYPE ,
243+ left,
244+ );
245+ } else if (recovery is FunctionElement ) {
246+ _errorReporter.reportErrorForNode (
247+ CompileTimeErrorCode .ASSIGNMENT_TO_FUNCTION ,
248+ left,
249+ );
250+ } else if (recovery is MethodElement ) {
251+ _errorReporter.reportErrorForNode (
252+ CompileTimeErrorCode .ASSIGNMENT_TO_METHOD ,
253+ left,
254+ );
255+ } else if (recovery is PrefixElement ) {
256+ _errorReporter.reportErrorForNode (
257+ CompileTimeErrorCode .PREFIX_IDENTIFIER_NOT_FOLLOWED_BY_DOT ,
258+ left,
259+ [recovery.name],
260+ );
261+ } else if (recovery is PropertyAccessorElement && recovery.isGetter) {
262+ var variable = recovery.variable;
263+ if (variable.isConst) {
264+ _errorReporter.reportErrorForNode (
265+ CompileTimeErrorCode .ASSIGNMENT_TO_CONST ,
266+ left,
267+ );
268+ } else if (variable.isFinal && ! variable.isLate) {
269+ if (variable is FieldElement ) {
270+ if (variable.setter == null && variable.isSynthetic) {
271+ _errorReporter.reportErrorForNode (
272+ CompileTimeErrorCode .ASSIGNMENT_TO_FINAL_NO_SETTER ,
273+ left,
274+ [variable.name, variable.enclosingElement.displayName],
275+ );
276+ } else {
277+ _errorReporter.reportErrorForNode (
278+ CompileTimeErrorCode .ASSIGNMENT_TO_FINAL ,
279+ left,
280+ [variable.name],
281+ );
282+ }
283+ return ;
284+ }
285+ _errorReporter.reportErrorForNode (
286+ CompileTimeErrorCode .ASSIGNMENT_TO_FINAL_LOCAL ,
287+ left,
288+ [variable.name],
289+ );
290+ }
291+ } else if (recovery is MultiplyDefinedElementImpl ) {
292+ // Will be reported in ErrorVerifier.
293+ } else {
294+ if (left is SimpleIdentifier && ! left.isSynthetic) {
295+ _errorReporter.reportErrorForNode (
296+ CompileTimeErrorCode .UNDEFINED_IDENTIFIER ,
297+ left,
298+ [left.name],
299+ );
300+ }
301+ }
302+ }
303+
304+ void _resolve1 (AssignmentExpressionImpl node, DartType leftType) {
179305 Token operator = node.operator ;
180306 TokenType operatorType = operator .type;
181307 Expression leftHandSide = node.leftHandSide;
182308
183- var leftType = getReadType (leftHandSide);
184- leftType = _resolveTypeParameter (leftType);
185-
186309 if (identical (leftType, NeverTypeImpl .instance)) {
187310 return ;
188311 }
@@ -241,13 +364,18 @@ class AssignmentExpressionResolver {
241364 // promoting the LHS/ to non-null (as we know its value will not be used
242365 // if null)
243366 _analyzeLeastUpperBoundTypes (
244- node,
245- _typeSystem.promoteToNonNull (getReadType (node.leftHandSide)),
246- getReadType (node.rightHandSide));
367+ node,
368+ _typeSystem.promoteToNonNull (node.readType),
369+ getReadType (node.rightHandSide),
370+ );
247371 } else {
248372 // The static type of a compound assignment using ??= before NNBD is the
249373 // least upper bound of the static types of the LHS and RHS.
250- _analyzeLeastUpperBound (node, node.leftHandSide, node.rightHandSide);
374+ _analyzeLeastUpperBoundTypes (
375+ node,
376+ node.readType,
377+ node.rightHandSide.staticType,
378+ );
251379 }
252380 } else if (operator == TokenType .AMPERSAND_AMPERSAND_EQ ||
253381 operator == TokenType .BAR_BAR_EQ ) {
@@ -285,30 +413,56 @@ class AssignmentExpressionResolver {
285413 _resolver.nullShortingTermination (node);
286414 }
287415
288- void _resolve_SimpleIdentifier_LocalVariable (
416+ void _resolve_SimpleIdentifier (
289417 AssignmentExpressionImpl node,
290418 SimpleIdentifier left,
291- VariableElement leftElement,
292- Expression right,
293419 ) {
420+ var right = node.rightHandSide;
294421 var operator = node.operator .type;
295422
296- left.staticElement = leftElement;
297423 if (operator != TokenType .EQ ) {
298- _resolver.setReadElement (left, leftElement);
424+ var readLookup = _resolver.lexicalLookup (node: left, setter: false );
425+ var readElement = readLookup.requested;
426+ _resolver.setReadElement (left, readElement);
299427 }
300- _resolver.setWriteElement (left, leftElement);
301428
302- var leftType = _resolver.localVariableTypeProvider.getType (left);
303- // TODO(scheglov) Set the type only when `operator != TokenType.EQ`.
304- _recordStaticType (left, leftType);
429+ var writeLookup = _resolver.lexicalLookup (node: left, setter: true );
430+ var writeElement = writeLookup.requested ?? writeLookup.recovery;
431+ _resolver.setWriteElement (left, writeElement);
432+ _reportNotSetter (left, writeLookup.requested, writeLookup.recovery);
433+
434+ // TODO(scheglov) This is mostly necessary for backward compatibility.
435+ // Although we also use `staticElement` for `getType(left)` below.
436+ {
437+ if (operator != TokenType .EQ ) {
438+ var readElement = node.readElement;
439+ if (readElement is PropertyAccessorElement ) {
440+ left.auxiliaryElements = AuxiliaryElements (readElement);
441+ }
442+ }
443+ left.staticElement = node.writeElement;
444+ if (node.readElement is VariableElement ) {
445+ var leftType = _resolver.localVariableTypeProvider.getType (left);
446+ _recordStaticType (left, leftType);
447+ } else {
448+ _recordStaticType (left, node.writeType);
449+ }
450+ }
305451
306452 if (operator != TokenType .EQ ) {
453+ // TODO(scheglov) Change this method to work with elements.
307454 _resolver.checkReadOfNotAssignedLocalVariable (left);
308455 }
309456
310- _resolve1 (node);
311- _setRhsContext (node, leftType, operator , right);
457+ _resolve1 (node, node.readType);
458+
459+ {
460+ var leftType = node.writeType;
461+ if (node.writeElement is VariableElement ) {
462+ leftType = _resolver.localVariableTypeProvider.getType (left);
463+ }
464+ _setRhsContext (node, leftType, operator , right);
465+ }
312466
313467 var flow = _flowAnalysis? .flow;
314468 if (flow != null && operator == TokenType .QUESTION_QUESTION_EQ ) {
@@ -320,21 +474,26 @@ class AssignmentExpressionResolver {
320474
321475 _resolve2 (node);
322476
477+ // TODO(scheglov) inline into resolve2().
478+ DartType assignedType;
479+ if (operator == TokenType .EQ ||
480+ operator == TokenType .QUESTION_QUESTION_EQ ) {
481+ assignedType = right.staticType;
482+ } else {
483+ assignedType = node.staticType;
484+ }
485+ _checkForInvalidAssignment (node.writeType, right, assignedType);
486+
323487 if (flow != null ) {
324- flow.write (leftElement, node.staticType);
488+ if (writeElement is VariableElement ) {
489+ flow.write (writeElement, node.staticType);
490+ }
325491 if (node.operator .type == TokenType .QUESTION_QUESTION_EQ ) {
326492 flow.ifNullExpression_end ();
327493 }
328494 }
329495 }
330496
331- /// If the given [type] is a type parameter, resolve it to the type that
332- /// should be used when looking up members. Otherwise, return the original
333- /// type.
334- // TODO(scheglov) this is duplicate
335- DartType _resolveTypeParameter (DartType type) =>
336- type? .resolveToBound (_typeProvider.objectType);
337-
338497 void _setRhsContext (AssignmentExpressionImpl node, DartType leftType,
339498 TokenType operator , Expression right) {
340499 switch (operator ) {
0 commit comments