mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 09:43:57 +00:00
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:
parent
6762d95c88
commit
c5567c0031
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue