Abstract FlowAnalysis from concrete types of nodes and elements.
R=paulberry@google.com
Change-Id: I8a42eb260d2c99c4ca24cb8b9059c1d22f9430e9
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/106683
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
diff --git a/pkg/analyzer/lib/src/dart/resolver/flow_analysis.dart b/pkg/analyzer/lib/src/dart/resolver/flow_analysis.dart
index 1c8dc31..b711bcb 100644
--- a/pkg/analyzer/lib/src/dart/resolver/flow_analysis.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/flow_analysis.dart
@@ -2,62 +2,60 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-import 'package:analyzer/dart/ast/ast.dart';
-import 'package:analyzer/dart/element/element.dart';
+/// Sets of variables that are potentially assigned in a statement.
+class AssignedVariables<Statement, Element> {
+ final emptySet = Set<Element>();
-/// Sets of variables that are potentially assigned in a node.
-class AssignedVariables {
- static final emptySet = Set<VariableElement>();
+ /// Mapping from a [Statement] representing a loop to the set of variables
+ /// that are potentially assigned in that loop.
+ final Map<Statement, Set<Element>> _map = {};
- /// Mapping from a loop [AstNode] to the set of variables that are
- /// potentially assigned in this loop.
- final Map<AstNode, Set<VariableElement>> _map = {};
+ /// The stack of nested nodes.
+ final List<Set<Element>> _stack = [];
- /// The stack of nested loops.
- final List<Set<VariableElement>> _stack = [];
+ AssignedVariables();
- /// Return the set of variables that are potentially assigned in the [loop].
- Set<VariableElement> operator [](AstNode loop) {
- return _map[loop] ?? emptySet;
+ /// Return the set of variables that are potentially assigned in the
+ /// [statement].
+ Set<Element> operator [](Statement statement) {
+ return _map[statement] ?? emptySet;
}
void beginLoop() {
- var set = Set<VariableElement>.identity();
+ var set = Set<Element>.identity();
_stack.add(set);
}
- void endLoop(AstNode loop) {
- _map[loop] = _stack.removeLast();
+ void endLoop(Statement node) {
+ _map[node] = _stack.removeLast();
}
- void write(VariableElement variable) {
+ void write(Element variable) {
for (var i = 0; i < _stack.length; ++i) {
_stack[i].add(variable);
}
}
}
-class FlowAnalysis<T> {
- final _identity = _State<T>(
- false,
- _ElementSet.empty,
- _ElementSet.empty,
- _ElementSet.empty,
- const {},
- );
+class FlowAnalysis<Statement, Expression, Element, Type> {
+ final _ElementSet<Element> _emptySet;
+ final _State<Element, Type> _identity;
/// The output list of variables that were read before they were written.
/// TODO(scheglov) use _ElementSet?
- final List<LocalVariableElement> readBeforeWritten = [];
+ final List<Element> readBeforeWritten = [];
+
+ /// The [NodeOperations], used to manipulate expressions.
+ final NodeOperations<Expression> nodeOperations;
/// The [TypeOperations], used to access types, and check subtyping.
- final TypeOperations<T> typeOperations;
+ final TypeOperations<Element, Type> typeOperations;
- /// The enclosing [FunctionBody], used to check for potential mutations.
- final FunctionBody functionBody;
+ /// The enclosing function body, used to check for potential mutations.
+ final FunctionBodyAccess functionBody;
/// The stack of states of variables that are not definitely assigned.
- final List<_State> _stack = [];
+ final List<_State<Element, Type>> _stack = [];
/// The mapping from labeled [Statement]s to the index in the [_stack]
/// where the first related element is located. The number of elements
@@ -66,25 +64,55 @@
final Map<Statement, int> _statementToStackIndex = {};
/// The list of all variables.
- final List<VariableElement> _variables = [];
+ final List<Element> _variables = [];
- _State<T> _current;
+ _State<Element, Type> _current;
/// The last boolean condition, for [_conditionTrue] and [_conditionFalse].
Expression _condition;
/// The state when [_condition] evaluates to `true`.
- _State<T> _conditionTrue;
+ _State<Element, Type> _conditionTrue;
/// The state when [_condition] evaluates to `false`.
- _State<T> _conditionFalse;
+ _State<Element, Type> _conditionFalse;
- FlowAnalysis(this.typeOperations, this.functionBody) {
- _current = _State<T>(
+ factory FlowAnalysis(
+ NodeOperations<Expression> nodeOperations,
+ TypeOperations<Element, Type> typeOperations,
+ FunctionBodyAccess functionBody,
+ ) {
+ var emptySet = _ElementSet<Element>._(
+ List<Element>(0),
+ );
+ var identifyState = _State<Element, Type>(
+ false,
+ emptySet,
+ emptySet,
+ emptySet,
+ const {},
+ );
+ return FlowAnalysis._(
+ nodeOperations,
+ typeOperations,
+ functionBody,
+ emptySet,
+ identifyState,
+ );
+ }
+
+ FlowAnalysis._(
+ this.nodeOperations,
+ this.typeOperations,
+ this.functionBody,
+ this._emptySet,
+ this._identity,
+ ) {
+ _current = _State<Element, Type>(
true,
- _ElementSet.empty,
- _ElementSet.empty,
- _ElementSet.empty,
+ _emptySet,
+ _emptySet,
+ _emptySet,
const {},
);
}
@@ -93,17 +121,18 @@
bool get isReachable => _current.reachable;
/// Add a new [variable], which might be already [assigned].
- void add(VariableElement variable, {bool assigned: false}) {
+ void add(Element variable, {bool assigned: false}) {
_variables.add(variable);
_current = _current.add(variable, assigned: assigned);
}
- void conditional_elseBegin(ConditionalExpression node, bool isBool) {
+ void conditional_elseBegin(Expression conditionalExpression,
+ Expression thenExpression, bool isBool) {
var afterThen = _current;
var falseCondition = _stack.removeLast();
if (isBool) {
- _conditionalEnd(node.thenExpression);
+ _conditionalEnd(thenExpression);
// Tail of the stack: falseThen, trueThen
}
@@ -111,12 +140,13 @@
_current = falseCondition;
}
- void conditional_end(ConditionalExpression node, bool isBool) {
+ void conditional_end(Expression conditionalExpression,
+ Expression elseExpression, bool isBool) {
var afterThen = _stack.removeLast();
var afterElse = _current;
if (isBool) {
- _conditionalEnd(node.elseExpression);
+ _conditionalEnd(elseExpression);
// Tail of the stack: falseThen, trueThen, falseElse, trueElse
var trueElse = _stack.removeLast();
@@ -128,7 +158,7 @@
var trueResult = _join(trueThen, trueElse);
var falseResult = _join(falseThen, falseElse);
- _condition = node;
+ _condition = conditionalExpression;
_conditionTrue = trueResult;
_conditionFalse = falseResult;
}
@@ -136,41 +166,41 @@
_current = _join(afterThen, afterElse);
}
- void conditional_thenBegin(ConditionalExpression node) {
- _conditionalEnd(node.condition);
+ void conditional_thenBegin(
+ Expression conditionalExpression, Expression condition) {
+ _conditionalEnd(condition);
// Tail of the stack: falseCondition, trueCondition
var trueCondition = _stack.removeLast();
_current = trueCondition;
}
- /// The [node] checks that the [variable] is equal to `null`.
- void conditionEqNull(BinaryExpression node, VariableElement variable) {
+ /// The [binaryExpression] checks that the [variable] is equal to `null`.
+ void conditionEqNull(Expression binaryExpression, Element variable) {
if (functionBody.isPotentiallyMutatedInClosure(variable)) {
return;
}
- _condition = node;
- _conditionTrue = _current.markNullable(variable);
- _conditionFalse = _current.markNonNullable(variable);
+ _condition = binaryExpression;
+ _conditionTrue = _current.markNullable(_emptySet, variable);
+ _conditionFalse = _current.markNonNullable(_emptySet, variable);
}
- /// The [node] checks that the [variable] is not equal to `null`.
- void conditionNotEqNull(BinaryExpression node, VariableElement variable) {
+ /// The [binaryExpression] checks that the [variable] is not equal to `null`.
+ void conditionNotEqNull(Expression binaryExpression, Element variable) {
if (functionBody.isPotentiallyMutatedInClosure(variable)) {
return;
}
- _condition = node;
- _conditionTrue = _current.markNonNullable(variable);
- _conditionFalse = _current.markNullable(variable);
+ _condition = binaryExpression;
+ _conditionTrue = _current.markNonNullable(_emptySet, variable);
+ _conditionFalse = _current.markNullable(_emptySet, variable);
}
- void doStatement_bodyBegin(
- DoStatement node, Set<VariableElement> loopAssigned) {
+ void doStatement_bodyBegin(Statement doStatement, Set<Element> loopAssigned) {
_current = _current.removePromotedAll(loopAssigned);
- _statementToStackIndex[node] = _stack.length;
+ _statementToStackIndex[doStatement] = _stack.length;
_stack.add(_identity); // break
_stack.add(_identity); // continue
}
@@ -182,8 +212,8 @@
_current = _join(_current, continueState);
}
- void doStatement_end(DoStatement node) {
- _conditionalEnd(node.condition);
+ void doStatement_end(Statement doStatement, Expression condition) {
+ _conditionalEnd(condition);
// Tail of the stack: break, falseCondition, trueCondition
_stack.removeLast(); // trueCondition
@@ -193,13 +223,13 @@
_current = _join(falseCondition, breakState);
}
- void falseLiteral(BooleanLiteral expression) {
+ void falseLiteral(Expression expression) {
_condition = expression;
_conditionTrue = _identity;
_conditionFalse = _current;
}
- void forEachStatement_bodyBegin(Set<VariableElement> loopAssigned) {
+ void forEachStatement_bodyBegin(Set<Element> loopAssigned) {
_stack.add(_current);
_current = _current.removePromotedAll(loopAssigned);
}
@@ -222,7 +252,7 @@
_current = trueCondition;
}
- void forStatement_conditionBegin(Set<VariableElement> loopAssigned) {
+ void forStatement_conditionBegin(Set<Element> loopAssigned) {
_current = _current.removePromotedAll(loopAssigned);
}
@@ -245,10 +275,10 @@
void functionExpression_begin() {
_stack.add(_current);
- Set<VariableElement> notPromoted = null;
+ Set<Element> notPromoted = null;
for (var variable in _current.promoted.keys) {
if (functionBody.isPotentiallyMutatedInScope(variable)) {
- notPromoted ??= Set<VariableElement>.identity();
+ notPromoted ??= Set<Element>.identity();
notPromoted.add(variable);
}
}
@@ -262,7 +292,7 @@
_current = _stack.removeLast();
}
- void handleBreak(AstNode target) {
+ void handleBreak(Statement target) {
var breakIndex = _statementToStackIndex[target];
if (breakIndex != null) {
_stack[breakIndex] = _join(_stack[breakIndex], _current);
@@ -270,7 +300,7 @@
_current = _current.exit();
}
- void handleContinue(AstNode target) {
+ void handleContinue(Statement target) {
var breakIndex = _statementToStackIndex[target];
if (breakIndex != null) {
var continueIndex = breakIndex + 1;
@@ -302,8 +332,8 @@
}
void ifStatement_end(bool hasElse) {
- _State<T> afterThen;
- _State<T> afterElse;
+ _State<Element, Type> afterThen;
+ _State<Element, Type> afterElse;
if (hasElse) {
afterThen = _stack.removeLast();
afterElse = _current;
@@ -314,8 +344,8 @@
_current = _join(afterThen, afterElse);
}
- void ifStatement_thenBegin(IfStatement ifStatement) {
- _conditionalEnd(ifStatement.condition);
+ void ifStatement_thenBegin(Statement ifStatement, Expression condition) {
+ _conditionalEnd(condition);
// Tail of the stack: falseCondition, trueCondition
var trueCondition = _stack.removeLast();
@@ -323,33 +353,33 @@
}
void isExpression_end(
- IsExpression isExpression, VariableElement variable, T type) {
+ Expression isExpression, Element variable, bool isNot, Type type) {
if (functionBody.isPotentiallyMutatedInClosure(variable)) {
return;
}
_condition = isExpression;
- if (isExpression.notOperator == null) {
- _conditionTrue = _current.promote(typeOperations, variable, type);
- _conditionFalse = _current;
- } else {
+ if (isNot) {
_conditionTrue = _current;
_conditionFalse = _current.promote(typeOperations, variable, type);
+ } else {
+ _conditionTrue = _current.promote(typeOperations, variable, type);
+ _conditionFalse = _current;
}
}
/// Return `true` if the [variable] is known to be be nullable.
- bool isNonNullable(VariableElement variable) {
+ bool isNonNullable(Element variable) {
return !_current.notNonNullable.contains(variable);
}
/// Return `true` if the [variable] is known to be be non-nullable.
- bool isNullable(VariableElement variable) {
+ bool isNullable(Element variable) {
return !_current.notNullable.contains(variable);
}
- void logicalAnd_end(BinaryExpression andExpression) {
- _conditionalEnd(andExpression.rightOperand);
+ void logicalAnd_end(Expression andExpression, Expression rightOperand) {
+ _conditionalEnd(rightOperand);
// Tail of the stack: falseLeft, trueLeft, falseRight, trueRight
var trueRight = _stack.removeLast();
@@ -369,16 +399,16 @@
_current = afterResult;
}
- void logicalAnd_rightBegin(BinaryExpression andExpression) {
- _conditionalEnd(andExpression.leftOperand);
+ void logicalAnd_rightBegin(Expression andExpression, Expression leftOperand) {
+ _conditionalEnd(leftOperand);
// Tail of the stack: falseLeft, trueLeft
var trueLeft = _stack.last;
_current = trueLeft;
}
- void logicalNot_end(PrefixExpression notExpression) {
- _conditionalEnd(notExpression.operand);
+ void logicalNot_end(Expression notExpression, Expression operand) {
+ _conditionalEnd(operand);
var trueExpr = _stack.removeLast();
var falseExpr = _stack.removeLast();
@@ -387,8 +417,8 @@
_conditionFalse = trueExpr;
}
- void logicalOr_end(BinaryExpression orExpression) {
- _conditionalEnd(orExpression.rightOperand);
+ void logicalOr_end(Expression orExpression, Expression rightOperand) {
+ _conditionalEnd(rightOperand);
// Tail of the stack: falseLeft, trueLeft, falseRight, trueRight
var trueRight = _stack.removeLast();
@@ -408,8 +438,8 @@
_current = afterResult;
}
- void logicalOr_rightBegin(BinaryExpression orExpression) {
- _conditionalEnd(orExpression.leftOperand);
+ void logicalOr_rightBegin(Expression orExpression, Expression leftOperand) {
+ _conditionalEnd(leftOperand);
// Tail of the stack: falseLeft, trueLeft
var falseLeft = _stack[_stack.length - 2];
@@ -418,12 +448,12 @@
/// Retrieves the type that the [variable] is promoted to, if the [variable]
/// is currently promoted. Otherwise returns `null`.
- T promotedType(VariableElement variable) {
+ Type promotedType(Element variable) {
return _current.promoted[variable];
}
/// Register read of the given [variable] in the current state.
- void read(LocalVariableElement variable) {
+ void read(Element variable) {
if (_current.notAssigned.contains(variable)) {
// Add to the list of violating variables, if not there yet.
for (var i = 0; i < readBeforeWritten.length; ++i) {
@@ -440,11 +470,11 @@
/// assigned in other cases that might target this with `continue`, so
/// these variables might have different types and are "un-promoted" from
/// the "afterExpression" state.
- void switchStatement_beginCase(Set<VariableElement> notPromoted) {
+ void switchStatement_beginCase(Set<Element> notPromoted) {
_current = _stack.last.removePromotedAll(notPromoted);
}
- void switchStatement_end(SwitchStatement node, bool hasDefault) {
+ void switchStatement_end(Statement switchStatement, bool hasDefault) {
// Tail of the stack: break, continue, afterExpression
var afterExpression = _current = _stack.removeLast();
_stack.removeLast(); // continue
@@ -457,14 +487,14 @@
}
}
- void switchStatement_expressionEnd(SwitchStatement node) {
- _statementToStackIndex[node] = _stack.length;
+ void switchStatement_expressionEnd(Statement switchStatement) {
+ _statementToStackIndex[switchStatement] = _stack.length;
_stack.add(_identity); // break
_stack.add(_identity); // continue
_stack.add(_current); // afterExpression
}
- void trueLiteral(BooleanLiteral expression) {
+ void trueLiteral(Expression expression) {
_condition = expression;
_conditionTrue = _current;
_conditionFalse = _identity;
@@ -475,7 +505,7 @@
// Tail of the stack: beforeBody
}
- void tryCatchStatement_bodyEnd(Set<VariableElement> assignedInBody) {
+ void tryCatchStatement_bodyEnd(Set<Element> assignedInBody) {
var beforeBody = _stack.removeLast();
var beforeCatch = beforeBody.removePromotedAll(assignedInBody);
_stack.add(beforeCatch);
@@ -503,12 +533,17 @@
_stack.add(_current); // beforeTry
}
- void tryFinallyStatement_end(Set<VariableElement> assignedInFinally) {
+ void tryFinallyStatement_end(Set<Element> assignedInFinally) {
var afterBody = _stack.removeLast();
- _current = _current.restrict(typeOperations, afterBody, assignedInFinally);
+ _current = _current.restrict(
+ typeOperations,
+ _emptySet,
+ afterBody,
+ assignedInFinally,
+ );
}
- void tryFinallyStatement_finallyBegin(Set<VariableElement> assignedInBody) {
+ void tryFinallyStatement_finallyBegin(Set<Element> assignedInBody) {
var beforeTry = _stack.removeLast();
var afterBody = _current;
_stack.add(afterBody);
@@ -519,20 +554,21 @@
assert(_stack.isEmpty);
}
- void whileStatement_bodyBegin(WhileStatement node) {
- _conditionalEnd(node.condition);
+ void whileStatement_bodyBegin(
+ Statement whileStatement, Expression condition) {
+ _conditionalEnd(condition);
// Tail of the stack: falseCondition, trueCondition
var trueCondition = _stack.removeLast();
- _statementToStackIndex[node] = _stack.length;
+ _statementToStackIndex[whileStatement] = _stack.length;
_stack.add(_identity); // break
_stack.add(_identity); // continue
_current = trueCondition;
}
- void whileStatement_conditionBegin(Set<VariableElement> loopAssigned) {
+ void whileStatement_conditionBegin(Set<Element> loopAssigned) {
_current = _current.removePromotedAll(loopAssigned);
}
@@ -546,17 +582,16 @@
/// Register write of the given [variable] in the current state.
void write(
- VariableElement variable, {
+ Element variable, {
bool isNull = false,
bool isNonNull = false,
}) {
- _current = _current.write(variable, isNull: isNull, isNonNull: isNonNull);
+ _current = _current.write(typeOperations, _emptySet, variable,
+ isNull: isNull, isNonNull: isNonNull);
}
void _conditionalEnd(Expression condition) {
- while (condition is ParenthesizedExpression) {
- condition = (condition as ParenthesizedExpression).expression;
- }
+ condition = nodeOperations.unwrapParenthesized(condition);
if (identical(condition, _condition)) {
_stack.add(_conditionFalse);
_stack.add(_conditionTrue);
@@ -566,7 +601,10 @@
}
}
- _State<T> _join(_State<T> first, _State<T> second) {
+ _State<Element, Type> _join(
+ _State<Element, Type> first,
+ _State<Element, Type> second,
+ ) {
if (identical(first, _identity)) return second;
if (identical(second, _identity)) return first;
@@ -590,14 +628,14 @@
);
}
- Map<VariableElement, T> _joinPromoted(
- Map<VariableElement, T> first,
- Map<VariableElement, T> second,
+ Map<Element, Type> _joinPromoted(
+ Map<Element, Type> first,
+ Map<Element, Type> second,
) {
if (identical(first, second)) return first;
if (first.isEmpty || second.isEmpty) return const {};
- var result = <VariableElement, T>{};
+ var result = <Element, Type>{};
var alwaysFirst = true;
var alwaysSecond = true;
for (var element in first.keys) {
@@ -627,32 +665,44 @@
}
}
+/// Accessor for function body information.
+abstract class FunctionBodyAccess<Element> {
+ bool isPotentiallyMutatedInClosure(Element variable);
+
+ bool isPotentiallyMutatedInScope(Element variable);
+}
+
+/// Operations on nodes, abstracted from concrete node interfaces.
+abstract class NodeOperations<Expression> {
+ /// If the [node] is a parenthesized expression, recursively unwrap it.
+ Expression unwrapParenthesized(Expression node);
+}
+
/// Operations on types, abstracted from concrete type interfaces.
-abstract class TypeOperations<T> {
- /// Return the static type of with the given [element].
- T elementType(VariableElement element);
+abstract class TypeOperations<Element, Type> {
+ /// Return the static type of the given [element].
+ Type elementType(Element element);
+
+ /// Return `true` if the [element] is a local variable, not a parameter.
+ bool isLocalVariable(Element element);
/// Return `true` if the [leftType] is a subtype of the [rightType].
- bool isSubtypeOf(T leftType, T rightType);
+ bool isSubtypeOf(Type leftType, Type rightType);
}
/// List based immutable set of elements.
-class _ElementSet {
- static final empty = _ElementSet._(
- List<VariableElement>(0),
- );
-
- final List<VariableElement> elements;
+class _ElementSet<Element> {
+ final List<Element> elements;
_ElementSet._(this.elements);
- _ElementSet add(VariableElement addedElement) {
+ _ElementSet<Element> add(Element addedElement) {
if (contains(addedElement)) {
return this;
}
var length = elements.length;
- var newElements = List<VariableElement>(length + 1);
+ var newElements = List<Element>(length + 1);
for (var i = 0; i < length; ++i) {
newElements[i] = elements[i];
}
@@ -660,7 +710,7 @@
return _ElementSet._(newElements);
}
- _ElementSet addAll(Iterable<VariableElement> elements) {
+ _ElementSet<Element> addAll(Iterable<Element> elements) {
var result = this;
for (var element in elements) {
result = result.add(element);
@@ -668,7 +718,7 @@
return result;
}
- bool contains(VariableElement element) {
+ bool contains(Element element) {
var length = elements.length;
for (var i = 0; i < length; ++i) {
if (identical(elements[i], element)) {
@@ -678,7 +728,10 @@
return false;
}
- _ElementSet intersect(_ElementSet other) {
+ _ElementSet<Element> intersect({
+ _ElementSet<Element> empty,
+ _ElementSet<Element> other,
+ }) {
if (identical(other, empty)) return empty;
// TODO(scheglov) optimize
@@ -689,7 +742,10 @@
return _ElementSet._(newElements);
}
- _ElementSet remove(VariableElement removedElement) {
+ _ElementSet<Element> remove(
+ _ElementSet<Element> empty,
+ Element removedElement,
+ ) {
if (!contains(removedElement)) {
return this;
}
@@ -699,7 +755,7 @@
return empty;
}
- var newElements = List<VariableElement>(length - 1);
+ var newElements = List<Element>(length - 1);
var newIndex = 0;
for (var i = 0; i < length; ++i) {
var element = elements[i];
@@ -711,7 +767,7 @@
return _ElementSet._(newElements);
}
- _ElementSet union(_ElementSet other) {
+ _ElementSet<Element> union(_ElementSet<Element> other) {
if (other.elements.isEmpty) {
return this;
}
@@ -726,12 +782,12 @@
}
}
-class _State<T> {
+class _State<Element, Type> {
final bool reachable;
- final _ElementSet notAssigned;
- final _ElementSet notNullable;
- final _ElementSet notNonNullable;
- final Map<VariableElement, T> promoted;
+ final _ElementSet<Element> notAssigned;
+ final _ElementSet<Element> notNullable;
+ final _ElementSet<Element> notNonNullable;
+ final Map<Element, Type> promoted;
_State(
this.reachable,
@@ -742,7 +798,7 @@
);
/// Add a new [variable] to track definite assignment.
- _State<T> add(VariableElement variable, {bool assigned: false}) {
+ _State<Element, Type> add(Element variable, {bool assigned: false}) {
var newNotAssigned = assigned ? notAssigned : notAssigned.add(variable);
var newNotNullable = notNullable.add(variable);
var newNotNonNullable = notNonNullable.add(variable);
@@ -753,7 +809,7 @@
return this;
}
- return _State<T>(
+ return _State<Element, Type>(
reachable,
newNotAssigned,
newNotNullable,
@@ -762,20 +818,27 @@
);
}
- _State<T> exit() {
- return _State<T>(false, notAssigned, notNullable, notNonNullable, promoted);
+ _State<Element, Type> exit() {
+ return _State<Element, Type>(
+ false,
+ notAssigned,
+ notNullable,
+ notNonNullable,
+ promoted,
+ );
}
- _State<T> markNonNullable(VariableElement variable) {
+ _State<Element, Type> markNonNullable(
+ _ElementSet<Element> emptySet, Element variable) {
var newNotNullable = notNullable.add(variable);
- var newNotNonNullable = notNonNullable.remove(variable);
+ var newNotNonNullable = notNonNullable.remove(emptySet, variable);
if (identical(newNotNullable, notNullable) &&
identical(newNotNonNullable, notNonNullable)) {
return this;
}
- return _State<T>(
+ return _State<Element, Type>(
reachable,
notAssigned,
newNotNullable,
@@ -784,8 +847,9 @@
);
}
- _State<T> markNullable(VariableElement variable) {
- var newNotNullable = notNullable.remove(variable);
+ _State<Element, Type> markNullable(
+ _ElementSet<Element> emptySet, Element variable) {
+ var newNotNullable = notNullable.remove(emptySet, variable);
var newNotNonNullable = notNonNullable.add(variable);
if (identical(newNotNullable, notNullable) &&
@@ -793,7 +857,7 @@
return this;
}
- return _State<T>(
+ return _State<Element, Type>(
reachable,
notAssigned,
newNotNullable,
@@ -802,19 +866,19 @@
);
}
- _State<T> promote(
- TypeOperations<T> typeOperations,
- VariableElement variable,
- T type,
+ _State<Element, Type> promote(
+ TypeOperations<Element, Type> typeOperations,
+ Element variable,
+ Type type,
) {
var previousType = promoted[variable];
previousType ??= typeOperations.elementType(variable);
if (typeOperations.isSubtypeOf(type, previousType) &&
type != previousType) {
- var newPromoted = <VariableElement, T>{}..addAll(promoted);
+ var newPromoted = <Element, Type>{}..addAll(promoted);
newPromoted[variable] = type;
- return _State<T>(
+ return _State<Element, Type>(
reachable,
notAssigned,
notNullable,
@@ -826,7 +890,7 @@
return this;
}
- _State<T> removePromotedAll(Set<VariableElement> variables) {
+ _State<Element, Type> removePromotedAll(Set<Element> variables) {
var newNotNullable = notNullable.addAll(variables);
var newNotNonNullable = notNonNullable.addAll(variables);
var newPromoted = _removePromotedAll(promoted, variables);
@@ -835,7 +899,7 @@
identical(newNotNonNullable, notNonNullable) &&
identical(newPromoted, promoted)) return this;
- return _State<T>(
+ return _State<Element, Type>(
reachable,
notAssigned,
newNotNullable,
@@ -844,22 +908,26 @@
);
}
- _State<T> restrict(
- TypeOperations<T> typeOperations,
- _State<T> other,
- Set<VariableElement> unsafe,
+ _State<Element, Type> restrict(
+ TypeOperations<Element, Type> typeOperations,
+ _ElementSet<Element> emptySet,
+ _State<Element, Type> other,
+ Set<Element> unsafe,
) {
var newReachable = reachable && other.reachable;
- var newNotAssigned = notAssigned.intersect(other.notAssigned);
+ var newNotAssigned = notAssigned.intersect(
+ empty: emptySet,
+ other: other.notAssigned,
+ );
- var newNotNullable = _ElementSet.empty;
+ var newNotNullable = emptySet;
for (var variable in notNullable.elements) {
if (unsafe.contains(variable) || other.notNullable.contains(variable)) {
newNotNullable = newNotNullable.add(variable);
}
}
- var newNotNonNullable = _ElementSet.empty;
+ var newNotNonNullable = emptySet;
for (var variable in notNonNullable.elements) {
if (unsafe.contains(variable) ||
other.notNonNullable.contains(variable)) {
@@ -867,7 +935,7 @@
}
}
- var newPromoted = <VariableElement, T>{};
+ var newPromoted = <Element, Type>{};
for (var variable in promoted.keys) {
var thisType = promoted[variable];
if (!unsafe.contains(variable)) {
@@ -892,10 +960,10 @@
);
}
- _State<T> setReachable(bool reachable) {
+ _State<Element, Type> setReachable(bool reachable) {
if (this.reachable == reachable) return this;
- return _State<T>(
+ return _State<Element, Type>(
reachable,
notAssigned,
notNullable,
@@ -904,20 +972,23 @@
);
}
- _State<T> write(
- VariableElement variable, {
+ _State<Element, Type> write(
+ TypeOperations<Element, Type> typeOperations,
+ _ElementSet<Element> emptySet,
+ Element variable, {
bool isNull = false,
bool isNonNull = false,
}) {
- var newNotAssigned = variable is LocalVariableElement
- ? notAssigned.remove(variable)
+ var newNotAssigned = typeOperations.isLocalVariable(variable)
+ ? notAssigned.remove(emptySet, variable)
: notAssigned;
- var newNotNullable =
- isNull ? notNullable.remove(variable) : notNullable.add(variable);
+ var newNotNullable = isNull
+ ? notNullable.remove(emptySet, variable)
+ : notNullable.add(variable);
var newNotNonNullable = isNonNull
- ? notNonNullable.remove(variable)
+ ? notNonNullable.remove(emptySet, variable)
: notNonNullable.add(variable);
var newPromoted = _removePromoted(promoted, variable);
@@ -929,7 +1000,7 @@
return this;
}
- return _State<T>(
+ return _State<Element, Type>(
reachable,
newNotAssigned,
newNotNullable,
@@ -938,13 +1009,10 @@
);
}
- Map<VariableElement, T> _removePromoted(
- Map<VariableElement, T> map,
- VariableElement variable,
- ) {
+ Map<Element, Type> _removePromoted(Map<Element, Type> map, Element variable) {
if (map.isEmpty) return const {};
- var result = <VariableElement, T>{};
+ var result = <Element, Type>{};
for (var key in map.keys) {
if (!identical(key, variable)) {
result[key] = map[key];
@@ -955,14 +1023,14 @@
return result;
}
- Map<VariableElement, T> _removePromotedAll(
- Map<VariableElement, T> map,
- Set<VariableElement> variables,
+ Map<Element, Type> _removePromotedAll(
+ Map<Element, Type> map,
+ Set<Element> variables,
) {
if (map.isEmpty) return const {};
if (variables.isEmpty) return map;
- var result = <VariableElement, T>{};
+ var result = <Element, Type>{};
var noChanges = true;
for (var key in map.keys) {
if (variables.contains(key)) {
@@ -977,14 +1045,14 @@
return result;
}
- static _State<T> _identicalOrNew<T>(
- _State<T> first,
- _State<T> second,
+ static _State<Element, Type> _identicalOrNew<Element, Type>(
+ _State<Element, Type> first,
+ _State<Element, Type> second,
bool newReachable,
- _ElementSet newNotAssigned,
- _ElementSet newNotNullable,
- _ElementSet newNotNonNullable,
- Map<VariableElement, T> newPromoted,
+ _ElementSet<Element> newNotAssigned,
+ _ElementSet<Element> newNotNullable,
+ _ElementSet<Element> newNotNonNullable,
+ Map<Element, Type> newPromoted,
) {
if (first.reachable == newReachable &&
identical(first.notAssigned, newNotAssigned) &&
@@ -1001,7 +1069,7 @@
return second;
}
- return _State<T>(
+ return _State<Element, Type>(
newReachable,
newNotAssigned,
newNotNullable,
diff --git a/pkg/analyzer/test/src/dart/resolution/flow_analysis_test.dart b/pkg/analyzer/test/src/dart/resolution/flow_analysis_test.dart
index 5dd9420..6a39734 100644
--- a/pkg/analyzer/test/src/dart/resolution/flow_analysis_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/flow_analysis_test.dart
@@ -1308,13 +1308,13 @@
var unit = result.unit;
- var loopAssignedVariables = AssignedVariables();
- unit.accept(_AssignedVariablesVisitor(loopAssignedVariables));
+ var assignedVariables = AssignedVariables<Statement, VariableElement>();
+ unit.accept(_AssignedVariablesVisitor(assignedVariables));
var typeSystem = unit.declaredElement.context.typeSystem;
unit.accept(_AstVisitor(
typeSystem,
- loopAssignedVariables,
+ assignedVariables,
{},
readBeforeWritten,
[],
@@ -1623,13 +1623,13 @@
var unit = result.unit;
- var loopAssignedVariables = AssignedVariables();
- unit.accept(_AssignedVariablesVisitor(loopAssignedVariables));
+ var assignedVariables = AssignedVariables<Statement, VariableElement>();
+ unit.accept(_AssignedVariablesVisitor(assignedVariables));
var typeSystem = unit.declaredElement.context.typeSystem;
unit.accept(_AstVisitor(
typeSystem,
- loopAssignedVariables,
+ assignedVariables,
{},
[],
nullableNodes,
@@ -2070,13 +2070,13 @@
var unit = result.unit;
- var loopAssignedVariables = AssignedVariables();
- unit.accept(_AssignedVariablesVisitor(loopAssignedVariables));
+ var assignedVariables = AssignedVariables<Statement, VariableElement>();
+ unit.accept(_AssignedVariablesVisitor(assignedVariables));
var typeSystem = unit.declaredElement.context.typeSystem;
unit.accept(_AstVisitor(
typeSystem,
- loopAssignedVariables,
+ assignedVariables,
{},
[],
[],
@@ -2903,13 +2903,13 @@
var unit = result.unit;
- var loopAssignedVariables = AssignedVariables();
- unit.accept(_AssignedVariablesVisitor(loopAssignedVariables));
+ var assignedVariables = AssignedVariables<Statement, VariableElement>();
+ unit.accept(_AssignedVariablesVisitor(assignedVariables));
var typeSystem = unit.declaredElement.context.typeSystem;
unit.accept(_AstVisitor(
typeSystem,
- loopAssignedVariables,
+ assignedVariables,
promotedTypes,
[],
[],
@@ -3018,7 +3018,8 @@
class _AstVisitor extends GeneralizingAstVisitor<void> {
static final trueLiteral = astFactory.booleanLiteral(null, true);
- final TypeOperations<DartType> typeOperations;
+ final NodeOperations<Expression> nodeOperations;
+ final TypeOperations<VariableElement, DartType> typeOperations;
final AssignedVariables assignedVariables;
final Map<AstNode, DartType> promotedTypes;
final List<LocalVariableElement> readBeforeWritten;
@@ -3027,7 +3028,7 @@
final List<AstNode> unreachableNodes;
final List<FunctionBody> functionBodiesThatDontComplete;
- FlowAnalysis<DartType> flow;
+ FlowAnalysis<Statement, Expression, VariableElement, DartType> flow;
_AstVisitor(
TypeSystem typeSystem,
@@ -3038,7 +3039,8 @@
this.nonNullableNodes,
this.unreachableNodes,
this.functionBodiesThatDontComplete)
- : typeOperations = _TypeSystemTypeOperations(typeSystem);
+ : nodeOperations = _NodeOperations(),
+ typeOperations = _TypeSystemTypeOperations(typeSystem);
@override
void visitAssignmentExpression(AssignmentExpression node) {
@@ -3080,19 +3082,19 @@
if (operator == TokenType.AMPERSAND_AMPERSAND) {
left.accept(this);
- flow.logicalAnd_rightBegin(node);
+ flow.logicalAnd_rightBegin(node, node.leftOperand);
_checkUnreachableNode(node.rightOperand);
right.accept(this);
- flow.logicalAnd_end(node);
+ flow.logicalAnd_end(node, node.rightOperand);
} else if (operator == TokenType.BAR_BAR) {
left.accept(this);
- flow.logicalOr_rightBegin(node);
+ flow.logicalOr_rightBegin(node, node.leftOperand);
_checkUnreachableNode(node.rightOperand);
right.accept(this);
- flow.logicalOr_end(node);
+ flow.logicalOr_end(node, node.rightOperand);
} else if (operator == TokenType.BANG_EQ) {
left.accept(this);
right.accept(this);
@@ -3133,7 +3135,11 @@
var isFlowOwner = flow == null;
if (isFlowOwner) {
- flow = FlowAnalysis<DartType>(typeOperations, node);
+ flow = FlowAnalysis<Statement, Expression, VariableElement, DartType>(
+ nodeOperations,
+ typeOperations,
+ _FunctionBodyAccess(node),
+ );
var function = node.parent;
if (function is FunctionExpression) {
@@ -3149,7 +3155,10 @@
super.visitBlockFunctionBody(node);
if (isFlowOwner) {
- readBeforeWritten.addAll(flow.readBeforeWritten);
+ for (var variable in flow.readBeforeWritten) {
+ assert(variable is LocalVariableElement);
+ readBeforeWritten.add(variable);
+ }
if (!flow.isReachable) {
functionBodiesThatDontComplete.add(node);
@@ -3186,16 +3195,16 @@
condition.accept(this);
- flow.conditional_thenBegin(node);
+ flow.conditional_thenBegin(node, node.condition);
_checkUnreachableNode(node.thenExpression);
thenExpression.accept(this);
var isBool = thenExpression.staticType.isDartCoreBool;
- flow.conditional_elseBegin(node, isBool);
+ flow.conditional_elseBegin(node, node.thenExpression, isBool);
_checkUnreachableNode(node.elseExpression);
elseExpression.accept(this);
- flow.conditional_end(node, isBool);
+ flow.conditional_end(node, node.elseExpression, isBool);
}
@override
@@ -3218,7 +3227,7 @@
flow.doStatement_conditionBegin();
condition.accept(this);
- flow.doStatement_end(node);
+ flow.doStatement_end(node, node.condition);
}
@override
@@ -3285,7 +3294,7 @@
condition.accept(this);
- flow.ifStatement_thenBegin(node);
+ flow.ifStatement_thenBegin(node, node.condition);
thenStatement.accept(this);
if (elseStatement != null) {
@@ -3305,7 +3314,12 @@
if (expression is SimpleIdentifier) {
var element = expression.staticElement;
if (element is VariableElement) {
- flow.isExpression_end(node, element, typeAnnotation.type);
+ flow.isExpression_end(
+ node,
+ element,
+ node.notOperator != null,
+ typeAnnotation.type,
+ );
}
}
}
@@ -3317,7 +3331,7 @@
var operator = node.operator.type;
if (operator == TokenType.BANG) {
operand.accept(this);
- flow.logicalNot_end(node);
+ flow.logicalNot_end(node, node.operand);
} else {
operand.accept(this);
}
@@ -3385,7 +3399,7 @@
var member = members[i];
flow.switchStatement_beginCase(
- member.labels.isNotEmpty ? assignedInCases : AssignedVariables.emptySet,
+ member.labels.isNotEmpty ? assignedInCases : assignedVariables.emptySet,
);
member.accept(this);
@@ -3460,7 +3474,7 @@
flow.whileStatement_conditionBegin(assignedVariables[node]);
condition.accept(this);
- flow.whileStatement_bodyBegin(node);
+ flow.whileStatement_bodyBegin(node, node.condition);
body.accept(this);
flow.whileStatement_end();
@@ -3532,7 +3546,34 @@
}
}
-class _TypeSystemTypeOperations implements TypeOperations<DartType> {
+class _FunctionBodyAccess implements FunctionBodyAccess<VariableElement> {
+ final FunctionBody node;
+
+ _FunctionBodyAccess(this.node);
+
+ @override
+ bool isPotentiallyMutatedInClosure(VariableElement variable) {
+ return node.isPotentiallyMutatedInClosure(variable);
+ }
+
+ @override
+ bool isPotentiallyMutatedInScope(VariableElement variable) {
+ return node.isPotentiallyMutatedInScope(variable);
+ }
+}
+
+class _NodeOperations implements NodeOperations<Expression> {
+ @override
+ Expression unwrapParenthesized(Expression node) {
+ while (node is ParenthesizedExpression) {
+ node = (node as ParenthesizedExpression).expression;
+ }
+ return node;
+ }
+}
+
+class _TypeSystemTypeOperations
+ implements TypeOperations<VariableElement, DartType> {
final TypeSystem typeSystem;
_TypeSystemTypeOperations(this.typeSystem);
@@ -3546,4 +3587,9 @@
bool isSubtypeOf(DartType leftType, DartType rightType) {
return typeSystem.isSubtypeOf(leftType, rightType);
}
+
+ @override
+ bool isLocalVariable(VariableElement element) {
+ return element is LocalVariableElement;
+ }
}
diff --git a/pkg/nnbd_migration/lib/src/decorated_type_operations.dart b/pkg/nnbd_migration/lib/src/decorated_type_operations.dart
index b1e3fa8..0b7a04c 100644
--- a/pkg/nnbd_migration/lib/src/decorated_type_operations.dart
+++ b/pkg/nnbd_migration/lib/src/decorated_type_operations.dart
@@ -9,7 +9,8 @@
import 'package:nnbd_migration/src/node_builder.dart';
/// [TypeOperations] that works with [DecoratedType]s.
-class DecoratedTypeOperations implements TypeOperations<DecoratedType> {
+class DecoratedTypeOperations
+ implements TypeOperations<VariableElement, DecoratedType> {
final TypeSystem _typeSystem;
final VariableRepository _variableRepository;
@@ -21,6 +22,11 @@
}
@override
+ bool isLocalVariable(VariableElement element) {
+ return element is LocalVariableElement;
+ }
+
+ @override
bool isSubtypeOf(DecoratedType leftType, DecoratedType rightType) {
return _typeSystem.isSubtypeOf(leftType.type, rightType.type);
}