Skip to content

Commit d9b874c

Browse files
scheglovcommit-bot@chromium.org
authored andcommitted
Resolve AssignmentExpression with SimpleIdentifier directly.
Change-Id: I81cc3d6b1cf31d840727300c0a22e8acfa5bf17c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/161261 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
1 parent 2b93765 commit d9b874c

12 files changed

+682
-131
lines changed

pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart

Lines changed: 208 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import 'package:analyzer/dart/element/type_provider.dart';
1010
import 'package:analyzer/error/listener.dart';
1111
import 'package:analyzer/src/dart/ast/ast.dart';
1212
import 'package:analyzer/src/dart/ast/token.dart';
13+
import 'package:analyzer/src/dart/element/element.dart';
1314
import 'package:analyzer/src/dart/element/type.dart';
1415
import 'package:analyzer/src/dart/element/type_system.dart';
1516
import '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

Comments
 (0)