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>
This commit is contained in:
Konstantin Shcheglov 2019-06-19 22:51:11 +00:00 committed by commit-bot@chromium.org
parent 6762d95c88
commit c5567c0031
3 changed files with 343 additions and 223 deletions

View file

@ -2,62 +2,60 @@
// for details. All rights reserved. Use of this source code is governed by a // 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. // BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/dart/ast/ast.dart'; /// Sets of variables that are potentially assigned in a statement.
import 'package:analyzer/dart/element/element.dart'; class AssignedVariables<Statement, Element> {
final emptySet = Set<Element>();
/// Sets of variables that are potentially assigned in a node. /// Mapping from a [Statement] representing a loop to the set of variables
class AssignedVariables { /// that are potentially assigned in that loop.
static final emptySet = Set<VariableElement>(); final Map<Statement, Set<Element>> _map = {};
/// Mapping from a loop [AstNode] to the set of variables that are /// The stack of nested nodes.
/// potentially assigned in this loop. final List<Set<Element>> _stack = [];
final Map<AstNode, Set<VariableElement>> _map = {};
/// The stack of nested loops. AssignedVariables();
final List<Set<VariableElement>> _stack = [];
/// Return the set of variables that are potentially assigned in the [loop]. /// Return the set of variables that are potentially assigned in the
Set<VariableElement> operator [](AstNode loop) { /// [statement].
return _map[loop] ?? emptySet; Set<Element> operator [](Statement statement) {
return _map[statement] ?? emptySet;
} }
void beginLoop() { void beginLoop() {
var set = Set<VariableElement>.identity(); var set = Set<Element>.identity();
_stack.add(set); _stack.add(set);
} }
void endLoop(AstNode loop) { void endLoop(Statement node) {
_map[loop] = _stack.removeLast(); _map[node] = _stack.removeLast();
} }
void write(VariableElement variable) { void write(Element variable) {
for (var i = 0; i < _stack.length; ++i) { for (var i = 0; i < _stack.length; ++i) {
_stack[i].add(variable); _stack[i].add(variable);
} }
} }
} }
class FlowAnalysis<T> { class FlowAnalysis<Statement, Expression, Element, Type> {
final _identity = _State<T>( final _ElementSet<Element> _emptySet;
false, final _State<Element, Type> _identity;
_ElementSet.empty,
_ElementSet.empty,
_ElementSet.empty,
const {},
);
/// The output list of variables that were read before they were written. /// The output list of variables that were read before they were written.
/// TODO(scheglov) use _ElementSet? /// 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. /// 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. /// The enclosing function body, used to check for potential mutations.
final FunctionBody functionBody; final FunctionBodyAccess functionBody;
/// The stack of states of variables that are not definitely assigned. /// 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] /// The mapping from labeled [Statement]s to the index in the [_stack]
/// where the first related element is located. The number of elements /// where the first related element is located. The number of elements
@ -66,25 +64,55 @@ class FlowAnalysis<T> {
final Map<Statement, int> _statementToStackIndex = {}; final Map<Statement, int> _statementToStackIndex = {};
/// The list of all variables. /// 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]. /// The last boolean condition, for [_conditionTrue] and [_conditionFalse].
Expression _condition; Expression _condition;
/// The state when [_condition] evaluates to `true`. /// The state when [_condition] evaluates to `true`.
_State<T> _conditionTrue; _State<Element, Type> _conditionTrue;
/// The state when [_condition] evaluates to `false`. /// The state when [_condition] evaluates to `false`.
_State<T> _conditionFalse; _State<Element, Type> _conditionFalse;
FlowAnalysis(this.typeOperations, this.functionBody) { factory FlowAnalysis(
_current = _State<T>( 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, true,
_ElementSet.empty, _emptySet,
_ElementSet.empty, _emptySet,
_ElementSet.empty, _emptySet,
const {}, const {},
); );
} }
@ -93,17 +121,18 @@ class FlowAnalysis<T> {
bool get isReachable => _current.reachable; bool get isReachable => _current.reachable;
/// Add a new [variable], which might be already [assigned]. /// 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); _variables.add(variable);
_current = _current.add(variable, assigned: assigned); _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 afterThen = _current;
var falseCondition = _stack.removeLast(); var falseCondition = _stack.removeLast();
if (isBool) { if (isBool) {
_conditionalEnd(node.thenExpression); _conditionalEnd(thenExpression);
// Tail of the stack: falseThen, trueThen // Tail of the stack: falseThen, trueThen
} }
@ -111,12 +140,13 @@ class FlowAnalysis<T> {
_current = falseCondition; _current = falseCondition;
} }
void conditional_end(ConditionalExpression node, bool isBool) { void conditional_end(Expression conditionalExpression,
Expression elseExpression, bool isBool) {
var afterThen = _stack.removeLast(); var afterThen = _stack.removeLast();
var afterElse = _current; var afterElse = _current;
if (isBool) { if (isBool) {
_conditionalEnd(node.elseExpression); _conditionalEnd(elseExpression);
// Tail of the stack: falseThen, trueThen, falseElse, trueElse // Tail of the stack: falseThen, trueThen, falseElse, trueElse
var trueElse = _stack.removeLast(); var trueElse = _stack.removeLast();
@ -128,7 +158,7 @@ class FlowAnalysis<T> {
var trueResult = _join(trueThen, trueElse); var trueResult = _join(trueThen, trueElse);
var falseResult = _join(falseThen, falseElse); var falseResult = _join(falseThen, falseElse);
_condition = node; _condition = conditionalExpression;
_conditionTrue = trueResult; _conditionTrue = trueResult;
_conditionFalse = falseResult; _conditionFalse = falseResult;
} }
@ -136,41 +166,41 @@ class FlowAnalysis<T> {
_current = _join(afterThen, afterElse); _current = _join(afterThen, afterElse);
} }
void conditional_thenBegin(ConditionalExpression node) { void conditional_thenBegin(
_conditionalEnd(node.condition); Expression conditionalExpression, Expression condition) {
_conditionalEnd(condition);
// Tail of the stack: falseCondition, trueCondition // Tail of the stack: falseCondition, trueCondition
var trueCondition = _stack.removeLast(); var trueCondition = _stack.removeLast();
_current = trueCondition; _current = trueCondition;
} }
/// The [node] checks that the [variable] is equal to `null`. /// The [binaryExpression] checks that the [variable] is equal to `null`.
void conditionEqNull(BinaryExpression node, VariableElement variable) { void conditionEqNull(Expression binaryExpression, Element variable) {
if (functionBody.isPotentiallyMutatedInClosure(variable)) { if (functionBody.isPotentiallyMutatedInClosure(variable)) {
return; return;
} }
_condition = node; _condition = binaryExpression;
_conditionTrue = _current.markNullable(variable); _conditionTrue = _current.markNullable(_emptySet, variable);
_conditionFalse = _current.markNonNullable(variable); _conditionFalse = _current.markNonNullable(_emptySet, variable);
} }
/// The [node] checks that the [variable] is not equal to `null`. /// The [binaryExpression] checks that the [variable] is not equal to `null`.
void conditionNotEqNull(BinaryExpression node, VariableElement variable) { void conditionNotEqNull(Expression binaryExpression, Element variable) {
if (functionBody.isPotentiallyMutatedInClosure(variable)) { if (functionBody.isPotentiallyMutatedInClosure(variable)) {
return; return;
} }
_condition = node; _condition = binaryExpression;
_conditionTrue = _current.markNonNullable(variable); _conditionTrue = _current.markNonNullable(_emptySet, variable);
_conditionFalse = _current.markNullable(variable); _conditionFalse = _current.markNullable(_emptySet, variable);
} }
void doStatement_bodyBegin( void doStatement_bodyBegin(Statement doStatement, Set<Element> loopAssigned) {
DoStatement node, Set<VariableElement> loopAssigned) {
_current = _current.removePromotedAll(loopAssigned); _current = _current.removePromotedAll(loopAssigned);
_statementToStackIndex[node] = _stack.length; _statementToStackIndex[doStatement] = _stack.length;
_stack.add(_identity); // break _stack.add(_identity); // break
_stack.add(_identity); // continue _stack.add(_identity); // continue
} }
@ -182,8 +212,8 @@ class FlowAnalysis<T> {
_current = _join(_current, continueState); _current = _join(_current, continueState);
} }
void doStatement_end(DoStatement node) { void doStatement_end(Statement doStatement, Expression condition) {
_conditionalEnd(node.condition); _conditionalEnd(condition);
// Tail of the stack: break, falseCondition, trueCondition // Tail of the stack: break, falseCondition, trueCondition
_stack.removeLast(); // trueCondition _stack.removeLast(); // trueCondition
@ -193,13 +223,13 @@ class FlowAnalysis<T> {
_current = _join(falseCondition, breakState); _current = _join(falseCondition, breakState);
} }
void falseLiteral(BooleanLiteral expression) { void falseLiteral(Expression expression) {
_condition = expression; _condition = expression;
_conditionTrue = _identity; _conditionTrue = _identity;
_conditionFalse = _current; _conditionFalse = _current;
} }
void forEachStatement_bodyBegin(Set<VariableElement> loopAssigned) { void forEachStatement_bodyBegin(Set<Element> loopAssigned) {
_stack.add(_current); _stack.add(_current);
_current = _current.removePromotedAll(loopAssigned); _current = _current.removePromotedAll(loopAssigned);
} }
@ -222,7 +252,7 @@ class FlowAnalysis<T> {
_current = trueCondition; _current = trueCondition;
} }
void forStatement_conditionBegin(Set<VariableElement> loopAssigned) { void forStatement_conditionBegin(Set<Element> loopAssigned) {
_current = _current.removePromotedAll(loopAssigned); _current = _current.removePromotedAll(loopAssigned);
} }
@ -245,10 +275,10 @@ class FlowAnalysis<T> {
void functionExpression_begin() { void functionExpression_begin() {
_stack.add(_current); _stack.add(_current);
Set<VariableElement> notPromoted = null; Set<Element> notPromoted = null;
for (var variable in _current.promoted.keys) { for (var variable in _current.promoted.keys) {
if (functionBody.isPotentiallyMutatedInScope(variable)) { if (functionBody.isPotentiallyMutatedInScope(variable)) {
notPromoted ??= Set<VariableElement>.identity(); notPromoted ??= Set<Element>.identity();
notPromoted.add(variable); notPromoted.add(variable);
} }
} }
@ -262,7 +292,7 @@ class FlowAnalysis<T> {
_current = _stack.removeLast(); _current = _stack.removeLast();
} }
void handleBreak(AstNode target) { void handleBreak(Statement target) {
var breakIndex = _statementToStackIndex[target]; var breakIndex = _statementToStackIndex[target];
if (breakIndex != null) { if (breakIndex != null) {
_stack[breakIndex] = _join(_stack[breakIndex], _current); _stack[breakIndex] = _join(_stack[breakIndex], _current);
@ -270,7 +300,7 @@ class FlowAnalysis<T> {
_current = _current.exit(); _current = _current.exit();
} }
void handleContinue(AstNode target) { void handleContinue(Statement target) {
var breakIndex = _statementToStackIndex[target]; var breakIndex = _statementToStackIndex[target];
if (breakIndex != null) { if (breakIndex != null) {
var continueIndex = breakIndex + 1; var continueIndex = breakIndex + 1;
@ -302,8 +332,8 @@ class FlowAnalysis<T> {
} }
void ifStatement_end(bool hasElse) { void ifStatement_end(bool hasElse) {
_State<T> afterThen; _State<Element, Type> afterThen;
_State<T> afterElse; _State<Element, Type> afterElse;
if (hasElse) { if (hasElse) {
afterThen = _stack.removeLast(); afterThen = _stack.removeLast();
afterElse = _current; afterElse = _current;
@ -314,8 +344,8 @@ class FlowAnalysis<T> {
_current = _join(afterThen, afterElse); _current = _join(afterThen, afterElse);
} }
void ifStatement_thenBegin(IfStatement ifStatement) { void ifStatement_thenBegin(Statement ifStatement, Expression condition) {
_conditionalEnd(ifStatement.condition); _conditionalEnd(condition);
// Tail of the stack: falseCondition, trueCondition // Tail of the stack: falseCondition, trueCondition
var trueCondition = _stack.removeLast(); var trueCondition = _stack.removeLast();
@ -323,33 +353,33 @@ class FlowAnalysis<T> {
} }
void isExpression_end( void isExpression_end(
IsExpression isExpression, VariableElement variable, T type) { Expression isExpression, Element variable, bool isNot, Type type) {
if (functionBody.isPotentiallyMutatedInClosure(variable)) { if (functionBody.isPotentiallyMutatedInClosure(variable)) {
return; return;
} }
_condition = isExpression; _condition = isExpression;
if (isExpression.notOperator == null) { if (isNot) {
_conditionTrue = _current.promote(typeOperations, variable, type);
_conditionFalse = _current;
} else {
_conditionTrue = _current; _conditionTrue = _current;
_conditionFalse = _current.promote(typeOperations, variable, type); _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. /// 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 !_current.notNonNullable.contains(variable);
} }
/// Return `true` if the [variable] is known to be be non-nullable. /// 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); return !_current.notNullable.contains(variable);
} }
void logicalAnd_end(BinaryExpression andExpression) { void logicalAnd_end(Expression andExpression, Expression rightOperand) {
_conditionalEnd(andExpression.rightOperand); _conditionalEnd(rightOperand);
// Tail of the stack: falseLeft, trueLeft, falseRight, trueRight // Tail of the stack: falseLeft, trueLeft, falseRight, trueRight
var trueRight = _stack.removeLast(); var trueRight = _stack.removeLast();
@ -369,16 +399,16 @@ class FlowAnalysis<T> {
_current = afterResult; _current = afterResult;
} }
void logicalAnd_rightBegin(BinaryExpression andExpression) { void logicalAnd_rightBegin(Expression andExpression, Expression leftOperand) {
_conditionalEnd(andExpression.leftOperand); _conditionalEnd(leftOperand);
// Tail of the stack: falseLeft, trueLeft // Tail of the stack: falseLeft, trueLeft
var trueLeft = _stack.last; var trueLeft = _stack.last;
_current = trueLeft; _current = trueLeft;
} }
void logicalNot_end(PrefixExpression notExpression) { void logicalNot_end(Expression notExpression, Expression operand) {
_conditionalEnd(notExpression.operand); _conditionalEnd(operand);
var trueExpr = _stack.removeLast(); var trueExpr = _stack.removeLast();
var falseExpr = _stack.removeLast(); var falseExpr = _stack.removeLast();
@ -387,8 +417,8 @@ class FlowAnalysis<T> {
_conditionFalse = trueExpr; _conditionFalse = trueExpr;
} }
void logicalOr_end(BinaryExpression orExpression) { void logicalOr_end(Expression orExpression, Expression rightOperand) {
_conditionalEnd(orExpression.rightOperand); _conditionalEnd(rightOperand);
// Tail of the stack: falseLeft, trueLeft, falseRight, trueRight // Tail of the stack: falseLeft, trueLeft, falseRight, trueRight
var trueRight = _stack.removeLast(); var trueRight = _stack.removeLast();
@ -408,8 +438,8 @@ class FlowAnalysis<T> {
_current = afterResult; _current = afterResult;
} }
void logicalOr_rightBegin(BinaryExpression orExpression) { void logicalOr_rightBegin(Expression orExpression, Expression leftOperand) {
_conditionalEnd(orExpression.leftOperand); _conditionalEnd(leftOperand);
// Tail of the stack: falseLeft, trueLeft // Tail of the stack: falseLeft, trueLeft
var falseLeft = _stack[_stack.length - 2]; var falseLeft = _stack[_stack.length - 2];
@ -418,12 +448,12 @@ class FlowAnalysis<T> {
/// Retrieves the type that the [variable] is promoted to, if the [variable] /// Retrieves the type that the [variable] is promoted to, if the [variable]
/// is currently promoted. Otherwise returns `null`. /// is currently promoted. Otherwise returns `null`.
T promotedType(VariableElement variable) { Type promotedType(Element variable) {
return _current.promoted[variable]; return _current.promoted[variable];
} }
/// Register read of the given [variable] in the current state. /// Register read of the given [variable] in the current state.
void read(LocalVariableElement variable) { void read(Element variable) {
if (_current.notAssigned.contains(variable)) { if (_current.notAssigned.contains(variable)) {
// Add to the list of violating variables, if not there yet. // Add to the list of violating variables, if not there yet.
for (var i = 0; i < readBeforeWritten.length; ++i) { for (var i = 0; i < readBeforeWritten.length; ++i) {
@ -440,11 +470,11 @@ class FlowAnalysis<T> {
/// assigned in other cases that might target this with `continue`, so /// assigned in other cases that might target this with `continue`, so
/// these variables might have different types and are "un-promoted" from /// these variables might have different types and are "un-promoted" from
/// the "afterExpression" state. /// the "afterExpression" state.
void switchStatement_beginCase(Set<VariableElement> notPromoted) { void switchStatement_beginCase(Set<Element> notPromoted) {
_current = _stack.last.removePromotedAll(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 // Tail of the stack: break, continue, afterExpression
var afterExpression = _current = _stack.removeLast(); var afterExpression = _current = _stack.removeLast();
_stack.removeLast(); // continue _stack.removeLast(); // continue
@ -457,14 +487,14 @@ class FlowAnalysis<T> {
} }
} }
void switchStatement_expressionEnd(SwitchStatement node) { void switchStatement_expressionEnd(Statement switchStatement) {
_statementToStackIndex[node] = _stack.length; _statementToStackIndex[switchStatement] = _stack.length;
_stack.add(_identity); // break _stack.add(_identity); // break
_stack.add(_identity); // continue _stack.add(_identity); // continue
_stack.add(_current); // afterExpression _stack.add(_current); // afterExpression
} }
void trueLiteral(BooleanLiteral expression) { void trueLiteral(Expression expression) {
_condition = expression; _condition = expression;
_conditionTrue = _current; _conditionTrue = _current;
_conditionFalse = _identity; _conditionFalse = _identity;
@ -475,7 +505,7 @@ class FlowAnalysis<T> {
// Tail of the stack: beforeBody // Tail of the stack: beforeBody
} }
void tryCatchStatement_bodyEnd(Set<VariableElement> assignedInBody) { void tryCatchStatement_bodyEnd(Set<Element> assignedInBody) {
var beforeBody = _stack.removeLast(); var beforeBody = _stack.removeLast();
var beforeCatch = beforeBody.removePromotedAll(assignedInBody); var beforeCatch = beforeBody.removePromotedAll(assignedInBody);
_stack.add(beforeCatch); _stack.add(beforeCatch);
@ -503,12 +533,17 @@ class FlowAnalysis<T> {
_stack.add(_current); // beforeTry _stack.add(_current); // beforeTry
} }
void tryFinallyStatement_end(Set<VariableElement> assignedInFinally) { void tryFinallyStatement_end(Set<Element> assignedInFinally) {
var afterBody = _stack.removeLast(); 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 beforeTry = _stack.removeLast();
var afterBody = _current; var afterBody = _current;
_stack.add(afterBody); _stack.add(afterBody);
@ -519,20 +554,21 @@ class FlowAnalysis<T> {
assert(_stack.isEmpty); assert(_stack.isEmpty);
} }
void whileStatement_bodyBegin(WhileStatement node) { void whileStatement_bodyBegin(
_conditionalEnd(node.condition); Statement whileStatement, Expression condition) {
_conditionalEnd(condition);
// Tail of the stack: falseCondition, trueCondition // Tail of the stack: falseCondition, trueCondition
var trueCondition = _stack.removeLast(); var trueCondition = _stack.removeLast();
_statementToStackIndex[node] = _stack.length; _statementToStackIndex[whileStatement] = _stack.length;
_stack.add(_identity); // break _stack.add(_identity); // break
_stack.add(_identity); // continue _stack.add(_identity); // continue
_current = trueCondition; _current = trueCondition;
} }
void whileStatement_conditionBegin(Set<VariableElement> loopAssigned) { void whileStatement_conditionBegin(Set<Element> loopAssigned) {
_current = _current.removePromotedAll(loopAssigned); _current = _current.removePromotedAll(loopAssigned);
} }
@ -546,17 +582,16 @@ class FlowAnalysis<T> {
/// Register write of the given [variable] in the current state. /// Register write of the given [variable] in the current state.
void write( void write(
VariableElement variable, { Element variable, {
bool isNull = false, bool isNull = false,
bool isNonNull = 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) { void _conditionalEnd(Expression condition) {
while (condition is ParenthesizedExpression) { condition = nodeOperations.unwrapParenthesized(condition);
condition = (condition as ParenthesizedExpression).expression;
}
if (identical(condition, _condition)) { if (identical(condition, _condition)) {
_stack.add(_conditionFalse); _stack.add(_conditionFalse);
_stack.add(_conditionTrue); _stack.add(_conditionTrue);
@ -566,7 +601,10 @@ class FlowAnalysis<T> {
} }
} }
_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(first, _identity)) return second;
if (identical(second, _identity)) return first; if (identical(second, _identity)) return first;
@ -590,14 +628,14 @@ class FlowAnalysis<T> {
); );
} }
Map<VariableElement, T> _joinPromoted( Map<Element, Type> _joinPromoted(
Map<VariableElement, T> first, Map<Element, Type> first,
Map<VariableElement, T> second, Map<Element, Type> second,
) { ) {
if (identical(first, second)) return first; if (identical(first, second)) return first;
if (first.isEmpty || second.isEmpty) return const {}; if (first.isEmpty || second.isEmpty) return const {};
var result = <VariableElement, T>{}; var result = <Element, Type>{};
var alwaysFirst = true; var alwaysFirst = true;
var alwaysSecond = true; var alwaysSecond = true;
for (var element in first.keys) { for (var element in first.keys) {
@ -627,32 +665,44 @@ class FlowAnalysis<T> {
} }
} }
/// 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. /// Operations on types, abstracted from concrete type interfaces.
abstract class TypeOperations<T> { abstract class TypeOperations<Element, Type> {
/// Return the static type of with the given [element]. /// Return the static type of the given [element].
T elementType(VariableElement 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]. /// 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. /// List based immutable set of elements.
class _ElementSet { class _ElementSet<Element> {
static final empty = _ElementSet._( final List<Element> elements;
List<VariableElement>(0),
);
final List<VariableElement> elements;
_ElementSet._(this.elements); _ElementSet._(this.elements);
_ElementSet add(VariableElement addedElement) { _ElementSet<Element> add(Element addedElement) {
if (contains(addedElement)) { if (contains(addedElement)) {
return this; return this;
} }
var length = elements.length; var length = elements.length;
var newElements = List<VariableElement>(length + 1); var newElements = List<Element>(length + 1);
for (var i = 0; i < length; ++i) { for (var i = 0; i < length; ++i) {
newElements[i] = elements[i]; newElements[i] = elements[i];
} }
@ -660,7 +710,7 @@ class _ElementSet {
return _ElementSet._(newElements); return _ElementSet._(newElements);
} }
_ElementSet addAll(Iterable<VariableElement> elements) { _ElementSet<Element> addAll(Iterable<Element> elements) {
var result = this; var result = this;
for (var element in elements) { for (var element in elements) {
result = result.add(element); result = result.add(element);
@ -668,7 +718,7 @@ class _ElementSet {
return result; return result;
} }
bool contains(VariableElement element) { bool contains(Element element) {
var length = elements.length; var length = elements.length;
for (var i = 0; i < length; ++i) { for (var i = 0; i < length; ++i) {
if (identical(elements[i], element)) { if (identical(elements[i], element)) {
@ -678,7 +728,10 @@ class _ElementSet {
return false; return false;
} }
_ElementSet intersect(_ElementSet other) { _ElementSet<Element> intersect({
_ElementSet<Element> empty,
_ElementSet<Element> other,
}) {
if (identical(other, empty)) return empty; if (identical(other, empty)) return empty;
// TODO(scheglov) optimize // TODO(scheglov) optimize
@ -689,7 +742,10 @@ class _ElementSet {
return _ElementSet._(newElements); return _ElementSet._(newElements);
} }
_ElementSet remove(VariableElement removedElement) { _ElementSet<Element> remove(
_ElementSet<Element> empty,
Element removedElement,
) {
if (!contains(removedElement)) { if (!contains(removedElement)) {
return this; return this;
} }
@ -699,7 +755,7 @@ class _ElementSet {
return empty; return empty;
} }
var newElements = List<VariableElement>(length - 1); var newElements = List<Element>(length - 1);
var newIndex = 0; var newIndex = 0;
for (var i = 0; i < length; ++i) { for (var i = 0; i < length; ++i) {
var element = elements[i]; var element = elements[i];
@ -711,7 +767,7 @@ class _ElementSet {
return _ElementSet._(newElements); return _ElementSet._(newElements);
} }
_ElementSet union(_ElementSet other) { _ElementSet<Element> union(_ElementSet<Element> other) {
if (other.elements.isEmpty) { if (other.elements.isEmpty) {
return this; return this;
} }
@ -726,12 +782,12 @@ class _ElementSet {
} }
} }
class _State<T> { class _State<Element, Type> {
final bool reachable; final bool reachable;
final _ElementSet notAssigned; final _ElementSet<Element> notAssigned;
final _ElementSet notNullable; final _ElementSet<Element> notNullable;
final _ElementSet notNonNullable; final _ElementSet<Element> notNonNullable;
final Map<VariableElement, T> promoted; final Map<Element, Type> promoted;
_State( _State(
this.reachable, this.reachable,
@ -742,7 +798,7 @@ class _State<T> {
); );
/// Add a new [variable] to track definite assignment. /// 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 newNotAssigned = assigned ? notAssigned : notAssigned.add(variable);
var newNotNullable = notNullable.add(variable); var newNotNullable = notNullable.add(variable);
var newNotNonNullable = notNonNullable.add(variable); var newNotNonNullable = notNonNullable.add(variable);
@ -753,7 +809,7 @@ class _State<T> {
return this; return this;
} }
return _State<T>( return _State<Element, Type>(
reachable, reachable,
newNotAssigned, newNotAssigned,
newNotNullable, newNotNullable,
@ -762,20 +818,27 @@ class _State<T> {
); );
} }
_State<T> exit() { _State<Element, Type> exit() {
return _State<T>(false, notAssigned, notNullable, notNonNullable, promoted); 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 newNotNullable = notNullable.add(variable);
var newNotNonNullable = notNonNullable.remove(variable); var newNotNonNullable = notNonNullable.remove(emptySet, variable);
if (identical(newNotNullable, notNullable) && if (identical(newNotNullable, notNullable) &&
identical(newNotNonNullable, notNonNullable)) { identical(newNotNonNullable, notNonNullable)) {
return this; return this;
} }
return _State<T>( return _State<Element, Type>(
reachable, reachable,
notAssigned, notAssigned,
newNotNullable, newNotNullable,
@ -784,8 +847,9 @@ class _State<T> {
); );
} }
_State<T> markNullable(VariableElement variable) { _State<Element, Type> markNullable(
var newNotNullable = notNullable.remove(variable); _ElementSet<Element> emptySet, Element variable) {
var newNotNullable = notNullable.remove(emptySet, variable);
var newNotNonNullable = notNonNullable.add(variable); var newNotNonNullable = notNonNullable.add(variable);
if (identical(newNotNullable, notNullable) && if (identical(newNotNullable, notNullable) &&
@ -793,7 +857,7 @@ class _State<T> {
return this; return this;
} }
return _State<T>( return _State<Element, Type>(
reachable, reachable,
notAssigned, notAssigned,
newNotNullable, newNotNullable,
@ -802,19 +866,19 @@ class _State<T> {
); );
} }
_State<T> promote( _State<Element, Type> promote(
TypeOperations<T> typeOperations, TypeOperations<Element, Type> typeOperations,
VariableElement variable, Element variable,
T type, Type type,
) { ) {
var previousType = promoted[variable]; var previousType = promoted[variable];
previousType ??= typeOperations.elementType(variable); previousType ??= typeOperations.elementType(variable);
if (typeOperations.isSubtypeOf(type, previousType) && if (typeOperations.isSubtypeOf(type, previousType) &&
type != previousType) { type != previousType) {
var newPromoted = <VariableElement, T>{}..addAll(promoted); var newPromoted = <Element, Type>{}..addAll(promoted);
newPromoted[variable] = type; newPromoted[variable] = type;
return _State<T>( return _State<Element, Type>(
reachable, reachable,
notAssigned, notAssigned,
notNullable, notNullable,
@ -826,7 +890,7 @@ class _State<T> {
return this; return this;
} }
_State<T> removePromotedAll(Set<VariableElement> variables) { _State<Element, Type> removePromotedAll(Set<Element> variables) {
var newNotNullable = notNullable.addAll(variables); var newNotNullable = notNullable.addAll(variables);
var newNotNonNullable = notNonNullable.addAll(variables); var newNotNonNullable = notNonNullable.addAll(variables);
var newPromoted = _removePromotedAll(promoted, variables); var newPromoted = _removePromotedAll(promoted, variables);
@ -835,7 +899,7 @@ class _State<T> {
identical(newNotNonNullable, notNonNullable) && identical(newNotNonNullable, notNonNullable) &&
identical(newPromoted, promoted)) return this; identical(newPromoted, promoted)) return this;
return _State<T>( return _State<Element, Type>(
reachable, reachable,
notAssigned, notAssigned,
newNotNullable, newNotNullable,
@ -844,22 +908,26 @@ class _State<T> {
); );
} }
_State<T> restrict( _State<Element, Type> restrict(
TypeOperations<T> typeOperations, TypeOperations<Element, Type> typeOperations,
_State<T> other, _ElementSet<Element> emptySet,
Set<VariableElement> unsafe, _State<Element, Type> other,
Set<Element> unsafe,
) { ) {
var newReachable = reachable && other.reachable; 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) { for (var variable in notNullable.elements) {
if (unsafe.contains(variable) || other.notNullable.contains(variable)) { if (unsafe.contains(variable) || other.notNullable.contains(variable)) {
newNotNullable = newNotNullable.add(variable); newNotNullable = newNotNullable.add(variable);
} }
} }
var newNotNonNullable = _ElementSet.empty; var newNotNonNullable = emptySet;
for (var variable in notNonNullable.elements) { for (var variable in notNonNullable.elements) {
if (unsafe.contains(variable) || if (unsafe.contains(variable) ||
other.notNonNullable.contains(variable)) { other.notNonNullable.contains(variable)) {
@ -867,7 +935,7 @@ class _State<T> {
} }
} }
var newPromoted = <VariableElement, T>{}; var newPromoted = <Element, Type>{};
for (var variable in promoted.keys) { for (var variable in promoted.keys) {
var thisType = promoted[variable]; var thisType = promoted[variable];
if (!unsafe.contains(variable)) { if (!unsafe.contains(variable)) {
@ -892,10 +960,10 @@ class _State<T> {
); );
} }
_State<T> setReachable(bool reachable) { _State<Element, Type> setReachable(bool reachable) {
if (this.reachable == reachable) return this; if (this.reachable == reachable) return this;
return _State<T>( return _State<Element, Type>(
reachable, reachable,
notAssigned, notAssigned,
notNullable, notNullable,
@ -904,20 +972,23 @@ class _State<T> {
); );
} }
_State<T> write( _State<Element, Type> write(
VariableElement variable, { TypeOperations<Element, Type> typeOperations,
_ElementSet<Element> emptySet,
Element variable, {
bool isNull = false, bool isNull = false,
bool isNonNull = false, bool isNonNull = false,
}) { }) {
var newNotAssigned = variable is LocalVariableElement var newNotAssigned = typeOperations.isLocalVariable(variable)
? notAssigned.remove(variable) ? notAssigned.remove(emptySet, variable)
: notAssigned; : notAssigned;
var newNotNullable = var newNotNullable = isNull
isNull ? notNullable.remove(variable) : notNullable.add(variable); ? notNullable.remove(emptySet, variable)
: notNullable.add(variable);
var newNotNonNullable = isNonNull var newNotNonNullable = isNonNull
? notNonNullable.remove(variable) ? notNonNullable.remove(emptySet, variable)
: notNonNullable.add(variable); : notNonNullable.add(variable);
var newPromoted = _removePromoted(promoted, variable); var newPromoted = _removePromoted(promoted, variable);
@ -929,7 +1000,7 @@ class _State<T> {
return this; return this;
} }
return _State<T>( return _State<Element, Type>(
reachable, reachable,
newNotAssigned, newNotAssigned,
newNotNullable, newNotNullable,
@ -938,13 +1009,10 @@ class _State<T> {
); );
} }
Map<VariableElement, T> _removePromoted( Map<Element, Type> _removePromoted(Map<Element, Type> map, Element variable) {
Map<VariableElement, T> map,
VariableElement variable,
) {
if (map.isEmpty) return const {}; if (map.isEmpty) return const {};
var result = <VariableElement, T>{}; var result = <Element, Type>{};
for (var key in map.keys) { for (var key in map.keys) {
if (!identical(key, variable)) { if (!identical(key, variable)) {
result[key] = map[key]; result[key] = map[key];
@ -955,14 +1023,14 @@ class _State<T> {
return result; return result;
} }
Map<VariableElement, T> _removePromotedAll( Map<Element, Type> _removePromotedAll(
Map<VariableElement, T> map, Map<Element, Type> map,
Set<VariableElement> variables, Set<Element> variables,
) { ) {
if (map.isEmpty) return const {}; if (map.isEmpty) return const {};
if (variables.isEmpty) return map; if (variables.isEmpty) return map;
var result = <VariableElement, T>{}; var result = <Element, Type>{};
var noChanges = true; var noChanges = true;
for (var key in map.keys) { for (var key in map.keys) {
if (variables.contains(key)) { if (variables.contains(key)) {
@ -977,14 +1045,14 @@ class _State<T> {
return result; return result;
} }
static _State<T> _identicalOrNew<T>( static _State<Element, Type> _identicalOrNew<Element, Type>(
_State<T> first, _State<Element, Type> first,
_State<T> second, _State<Element, Type> second,
bool newReachable, bool newReachable,
_ElementSet newNotAssigned, _ElementSet<Element> newNotAssigned,
_ElementSet newNotNullable, _ElementSet<Element> newNotNullable,
_ElementSet newNotNonNullable, _ElementSet<Element> newNotNonNullable,
Map<VariableElement, T> newPromoted, Map<Element, Type> newPromoted,
) { ) {
if (first.reachable == newReachable && if (first.reachable == newReachable &&
identical(first.notAssigned, newNotAssigned) && identical(first.notAssigned, newNotAssigned) &&
@ -1001,7 +1069,7 @@ class _State<T> {
return second; return second;
} }
return _State<T>( return _State<Element, Type>(
newReachable, newReachable,
newNotAssigned, newNotAssigned,
newNotNullable, newNotNullable,

View file

@ -1308,13 +1308,13 @@ void f() {
var unit = result.unit; var unit = result.unit;
var loopAssignedVariables = AssignedVariables(); var assignedVariables = AssignedVariables<Statement, VariableElement>();
unit.accept(_AssignedVariablesVisitor(loopAssignedVariables)); unit.accept(_AssignedVariablesVisitor(assignedVariables));
var typeSystem = unit.declaredElement.context.typeSystem; var typeSystem = unit.declaredElement.context.typeSystem;
unit.accept(_AstVisitor( unit.accept(_AstVisitor(
typeSystem, typeSystem,
loopAssignedVariables, assignedVariables,
{}, {},
readBeforeWritten, readBeforeWritten,
[], [],
@ -1623,13 +1623,13 @@ void f(int x) {
var unit = result.unit; var unit = result.unit;
var loopAssignedVariables = AssignedVariables(); var assignedVariables = AssignedVariables<Statement, VariableElement>();
unit.accept(_AssignedVariablesVisitor(loopAssignedVariables)); unit.accept(_AssignedVariablesVisitor(assignedVariables));
var typeSystem = unit.declaredElement.context.typeSystem; var typeSystem = unit.declaredElement.context.typeSystem;
unit.accept(_AstVisitor( unit.accept(_AstVisitor(
typeSystem, typeSystem,
loopAssignedVariables, assignedVariables,
{}, {},
[], [],
nullableNodes, nullableNodes,
@ -2070,13 +2070,13 @@ void f() { // f
var unit = result.unit; var unit = result.unit;
var loopAssignedVariables = AssignedVariables(); var assignedVariables = AssignedVariables<Statement, VariableElement>();
unit.accept(_AssignedVariablesVisitor(loopAssignedVariables)); unit.accept(_AssignedVariablesVisitor(assignedVariables));
var typeSystem = unit.declaredElement.context.typeSystem; var typeSystem = unit.declaredElement.context.typeSystem;
unit.accept(_AstVisitor( unit.accept(_AstVisitor(
typeSystem, typeSystem,
loopAssignedVariables, assignedVariables,
{}, {},
[], [],
[], [],
@ -2903,13 +2903,13 @@ void f(bool b, Object x) {
var unit = result.unit; var unit = result.unit;
var loopAssignedVariables = AssignedVariables(); var assignedVariables = AssignedVariables<Statement, VariableElement>();
unit.accept(_AssignedVariablesVisitor(loopAssignedVariables)); unit.accept(_AssignedVariablesVisitor(assignedVariables));
var typeSystem = unit.declaredElement.context.typeSystem; var typeSystem = unit.declaredElement.context.typeSystem;
unit.accept(_AstVisitor( unit.accept(_AstVisitor(
typeSystem, typeSystem,
loopAssignedVariables, assignedVariables,
promotedTypes, promotedTypes,
[], [],
[], [],
@ -3018,7 +3018,8 @@ class _AssignedVariablesVisitor extends RecursiveAstVisitor<void> {
class _AstVisitor extends GeneralizingAstVisitor<void> { class _AstVisitor extends GeneralizingAstVisitor<void> {
static final trueLiteral = astFactory.booleanLiteral(null, true); static final trueLiteral = astFactory.booleanLiteral(null, true);
final TypeOperations<DartType> typeOperations; final NodeOperations<Expression> nodeOperations;
final TypeOperations<VariableElement, DartType> typeOperations;
final AssignedVariables assignedVariables; final AssignedVariables assignedVariables;
final Map<AstNode, DartType> promotedTypes; final Map<AstNode, DartType> promotedTypes;
final List<LocalVariableElement> readBeforeWritten; final List<LocalVariableElement> readBeforeWritten;
@ -3027,7 +3028,7 @@ class _AstVisitor extends GeneralizingAstVisitor<void> {
final List<AstNode> unreachableNodes; final List<AstNode> unreachableNodes;
final List<FunctionBody> functionBodiesThatDontComplete; final List<FunctionBody> functionBodiesThatDontComplete;
FlowAnalysis<DartType> flow; FlowAnalysis<Statement, Expression, VariableElement, DartType> flow;
_AstVisitor( _AstVisitor(
TypeSystem typeSystem, TypeSystem typeSystem,
@ -3038,7 +3039,8 @@ class _AstVisitor extends GeneralizingAstVisitor<void> {
this.nonNullableNodes, this.nonNullableNodes,
this.unreachableNodes, this.unreachableNodes,
this.functionBodiesThatDontComplete) this.functionBodiesThatDontComplete)
: typeOperations = _TypeSystemTypeOperations(typeSystem); : nodeOperations = _NodeOperations(),
typeOperations = _TypeSystemTypeOperations(typeSystem);
@override @override
void visitAssignmentExpression(AssignmentExpression node) { void visitAssignmentExpression(AssignmentExpression node) {
@ -3080,19 +3082,19 @@ class _AstVisitor extends GeneralizingAstVisitor<void> {
if (operator == TokenType.AMPERSAND_AMPERSAND) { if (operator == TokenType.AMPERSAND_AMPERSAND) {
left.accept(this); left.accept(this);
flow.logicalAnd_rightBegin(node); flow.logicalAnd_rightBegin(node, node.leftOperand);
_checkUnreachableNode(node.rightOperand); _checkUnreachableNode(node.rightOperand);
right.accept(this); right.accept(this);
flow.logicalAnd_end(node); flow.logicalAnd_end(node, node.rightOperand);
} else if (operator == TokenType.BAR_BAR) { } else if (operator == TokenType.BAR_BAR) {
left.accept(this); left.accept(this);
flow.logicalOr_rightBegin(node); flow.logicalOr_rightBegin(node, node.leftOperand);
_checkUnreachableNode(node.rightOperand); _checkUnreachableNode(node.rightOperand);
right.accept(this); right.accept(this);
flow.logicalOr_end(node); flow.logicalOr_end(node, node.rightOperand);
} else if (operator == TokenType.BANG_EQ) { } else if (operator == TokenType.BANG_EQ) {
left.accept(this); left.accept(this);
right.accept(this); right.accept(this);
@ -3133,7 +3135,11 @@ class _AstVisitor extends GeneralizingAstVisitor<void> {
var isFlowOwner = flow == null; var isFlowOwner = flow == null;
if (isFlowOwner) { if (isFlowOwner) {
flow = FlowAnalysis<DartType>(typeOperations, node); flow = FlowAnalysis<Statement, Expression, VariableElement, DartType>(
nodeOperations,
typeOperations,
_FunctionBodyAccess(node),
);
var function = node.parent; var function = node.parent;
if (function is FunctionExpression) { if (function is FunctionExpression) {
@ -3149,7 +3155,10 @@ class _AstVisitor extends GeneralizingAstVisitor<void> {
super.visitBlockFunctionBody(node); super.visitBlockFunctionBody(node);
if (isFlowOwner) { if (isFlowOwner) {
readBeforeWritten.addAll(flow.readBeforeWritten); for (var variable in flow.readBeforeWritten) {
assert(variable is LocalVariableElement);
readBeforeWritten.add(variable);
}
if (!flow.isReachable) { if (!flow.isReachable) {
functionBodiesThatDontComplete.add(node); functionBodiesThatDontComplete.add(node);
@ -3186,16 +3195,16 @@ class _AstVisitor extends GeneralizingAstVisitor<void> {
condition.accept(this); condition.accept(this);
flow.conditional_thenBegin(node); flow.conditional_thenBegin(node, node.condition);
_checkUnreachableNode(node.thenExpression); _checkUnreachableNode(node.thenExpression);
thenExpression.accept(this); thenExpression.accept(this);
var isBool = thenExpression.staticType.isDartCoreBool; var isBool = thenExpression.staticType.isDartCoreBool;
flow.conditional_elseBegin(node, isBool); flow.conditional_elseBegin(node, node.thenExpression, isBool);
_checkUnreachableNode(node.elseExpression); _checkUnreachableNode(node.elseExpression);
elseExpression.accept(this); elseExpression.accept(this);
flow.conditional_end(node, isBool); flow.conditional_end(node, node.elseExpression, isBool);
} }
@override @override
@ -3218,7 +3227,7 @@ class _AstVisitor extends GeneralizingAstVisitor<void> {
flow.doStatement_conditionBegin(); flow.doStatement_conditionBegin();
condition.accept(this); condition.accept(this);
flow.doStatement_end(node); flow.doStatement_end(node, node.condition);
} }
@override @override
@ -3285,7 +3294,7 @@ class _AstVisitor extends GeneralizingAstVisitor<void> {
condition.accept(this); condition.accept(this);
flow.ifStatement_thenBegin(node); flow.ifStatement_thenBegin(node, node.condition);
thenStatement.accept(this); thenStatement.accept(this);
if (elseStatement != null) { if (elseStatement != null) {
@ -3305,7 +3314,12 @@ class _AstVisitor extends GeneralizingAstVisitor<void> {
if (expression is SimpleIdentifier) { if (expression is SimpleIdentifier) {
var element = expression.staticElement; var element = expression.staticElement;
if (element is VariableElement) { 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 @@ class _AstVisitor extends GeneralizingAstVisitor<void> {
var operator = node.operator.type; var operator = node.operator.type;
if (operator == TokenType.BANG) { if (operator == TokenType.BANG) {
operand.accept(this); operand.accept(this);
flow.logicalNot_end(node); flow.logicalNot_end(node, node.operand);
} else { } else {
operand.accept(this); operand.accept(this);
} }
@ -3385,7 +3399,7 @@ class _AstVisitor extends GeneralizingAstVisitor<void> {
var member = members[i]; var member = members[i];
flow.switchStatement_beginCase( flow.switchStatement_beginCase(
member.labels.isNotEmpty ? assignedInCases : AssignedVariables.emptySet, member.labels.isNotEmpty ? assignedInCases : assignedVariables.emptySet,
); );
member.accept(this); member.accept(this);
@ -3460,7 +3474,7 @@ class _AstVisitor extends GeneralizingAstVisitor<void> {
flow.whileStatement_conditionBegin(assignedVariables[node]); flow.whileStatement_conditionBegin(assignedVariables[node]);
condition.accept(this); condition.accept(this);
flow.whileStatement_bodyBegin(node); flow.whileStatement_bodyBegin(node, node.condition);
body.accept(this); body.accept(this);
flow.whileStatement_end(); flow.whileStatement_end();
@ -3532,7 +3546,34 @@ class _AstVisitor extends GeneralizingAstVisitor<void> {
} }
} }
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; final TypeSystem typeSystem;
_TypeSystemTypeOperations(this.typeSystem); _TypeSystemTypeOperations(this.typeSystem);
@ -3546,4 +3587,9 @@ class _TypeSystemTypeOperations implements TypeOperations<DartType> {
bool isSubtypeOf(DartType leftType, DartType rightType) { bool isSubtypeOf(DartType leftType, DartType rightType) {
return typeSystem.isSubtypeOf(leftType, rightType); return typeSystem.isSubtypeOf(leftType, rightType);
} }
@override
bool isLocalVariable(VariableElement element) {
return element is LocalVariableElement;
}
} }

View file

@ -9,7 +9,8 @@ import 'package:nnbd_migration/src/decorated_type.dart';
import 'package:nnbd_migration/src/node_builder.dart'; import 'package:nnbd_migration/src/node_builder.dart';
/// [TypeOperations] that works with [DecoratedType]s. /// [TypeOperations] that works with [DecoratedType]s.
class DecoratedTypeOperations implements TypeOperations<DecoratedType> { class DecoratedTypeOperations
implements TypeOperations<VariableElement, DecoratedType> {
final TypeSystem _typeSystem; final TypeSystem _typeSystem;
final VariableRepository _variableRepository; final VariableRepository _variableRepository;
@ -20,6 +21,11 @@ class DecoratedTypeOperations implements TypeOperations<DecoratedType> {
return _variableRepository.decoratedElementType(element); return _variableRepository.decoratedElementType(element);
} }
@override
bool isLocalVariable(VariableElement element) {
return element is LocalVariableElement;
}
@override @override
bool isSubtypeOf(DecoratedType leftType, DecoratedType rightType) { bool isSubtypeOf(DecoratedType leftType, DecoratedType rightType) {
return _typeSystem.isSubtypeOf(leftType.type, rightType.type); return _typeSystem.isSubtypeOf(leftType.type, rightType.type);