Version 2.12.0-232.0.dev
Merge commit '5ae02ab4c3de4af4ebffe7174019a65d9895edcc' into 'dev'
diff --git a/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart b/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
index ee22cc5..ade689e 100644
--- a/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
@@ -1567,7 +1567,7 @@
VariableModel<Variable, Type> newInfoForVar =
new VariableModel.fresh(assigned: initialized);
- return _updateVariableInfo(variable, newInfoForVar);
+ return _updateVariableInfo(new VariableReference(variable), newInfoForVar);
}
/// Gets the info for the given [variable], creating it if it doesn't exist.
@@ -1799,19 +1799,20 @@
String toString() => '($reachable, $variableInfo)';
/// Returns an [ExpressionInfo] indicating the result of checking whether the
- /// given [variable] is non-null.
+ /// given [reference] is non-null.
///
/// Note that the state is only changed if the previous type of [variable] was
/// potentially nullable.
ExpressionInfo<Variable, Type> tryMarkNonNullable(
- TypeOperations<Variable, Type> typeOperations, Variable variable) {
- VariableModel<Variable, Type> info = infoFor(variable);
+ TypeOperations<Variable, Type> typeOperations,
+ Reference<Variable, Type> reference) {
+ VariableModel<Variable, Type> info = reference.getInfo(variableInfo);
if (info.writeCaptured) {
return new _TrivialExpressionInfo<Variable, Type>(this);
}
Type? previousType = info.promotedTypes?.last;
- previousType ??= typeOperations.variableType(variable);
+ previousType ??= reference.getDeclaredType(typeOperations);
Type newType = typeOperations.promoteToNonNull(previousType);
if (typeOperations.isSameType(newType, previousType)) {
@@ -1820,7 +1821,7 @@
assert(typeOperations.isSubtypeOf(newType, previousType));
FlowModel<Variable, Type> modelIfSuccessful =
- _finishTypeTest(typeOperations, variable, info, null, newType);
+ _finishTypeTest(typeOperations, reference, info, null, newType);
FlowModel<Variable, Type> modelIfFailed = this;
@@ -1829,7 +1830,7 @@
}
/// Returns an [ExpressionInfo] indicating the result of casting the given
- /// [variable] to the given [type], as a consequence of an `as` expression.
+ /// [reference] to the given [type], as a consequence of an `as` expression.
///
/// Note that the state is only changed if [type] is a subtype of the
/// variable's previous (possibly promoted) type.
@@ -1838,15 +1839,15 @@
/// variable as definitely assigned? Does it matter?
FlowModel<Variable, Type> tryPromoteForTypeCast(
TypeOperations<Variable, Type> typeOperations,
- Variable variable,
+ Reference<Variable, Type> reference,
Type type) {
- VariableModel<Variable, Type> info = infoFor(variable);
+ VariableModel<Variable, Type> info = reference.getInfo(variableInfo);
if (info.writeCaptured) {
return this;
}
Type? previousType = info.promotedTypes?.last;
- previousType ??= typeOperations.variableType(variable);
+ previousType ??= reference.getDeclaredType(typeOperations);
Type? newType = typeOperations.tryPromoteToType(type, previousType);
if (newType == null || typeOperations.isSameType(newType, previousType)) {
@@ -1855,11 +1856,11 @@
assert(typeOperations.isSubtypeOf(newType, previousType),
"Expected $newType to be a subtype of $previousType.");
- return _finishTypeTest(typeOperations, variable, info, type, newType);
+ return _finishTypeTest(typeOperations, reference, info, type, newType);
}
/// Returns an [ExpressionInfo] indicating the result of checking whether the
- /// given [variable] satisfies the given [type], e.g. as a consequence of an
+ /// given [reference] satisfies the given [type], e.g. as a consequence of an
/// `is` expression as the condition of an `if` statement.
///
/// Note that the "ifTrue" state is only changed if [type] is a subtype of
@@ -1869,15 +1870,15 @@
/// variable as definitely assigned? Does it matter?
ExpressionInfo<Variable, Type> tryPromoteForTypeCheck(
TypeOperations<Variable, Type> typeOperations,
- Variable variable,
+ Reference<Variable, Type> reference,
Type type) {
- VariableModel<Variable, Type> info = infoFor(variable);
+ VariableModel<Variable, Type> info = reference.getInfo(variableInfo);
if (info.writeCaptured) {
return new _TrivialExpressionInfo<Variable, Type>(this);
}
Type? previousType = info.promotedTypes?.last;
- previousType ??= typeOperations.variableType(variable);
+ previousType ??= reference.getDeclaredType(typeOperations);
FlowModel<Variable, Type> modelIfSuccessful = this;
Type? typeIfSuccess = typeOperations.tryPromoteToType(type, previousType);
@@ -1886,7 +1887,7 @@
assert(typeOperations.isSubtypeOf(typeIfSuccess, previousType),
"Expected $typeIfSuccess to be a subtype of $previousType.");
modelIfSuccessful =
- _finishTypeTest(typeOperations, variable, info, type, typeIfSuccess);
+ _finishTypeTest(typeOperations, reference, info, type, typeIfSuccess);
}
Type factoredType = typeOperations.factor(previousType, type);
@@ -1902,7 +1903,7 @@
typeIfFailed = factoredType;
}
FlowModel<Variable, Type> modelIfFailed =
- _finishTypeTest(typeOperations, variable, info, type, typeIfFailed);
+ _finishTypeTest(typeOperations, reference, info, type, typeIfFailed);
return new ExpressionInfo<Variable, Type>(
this, modelIfSuccessful, modelIfFailed);
@@ -1939,12 +1940,12 @@
infoForVar.write(variable, writtenType, typeOperations, newSsaNode);
if (identical(newInfoForVar, infoForVar)) return this;
- return _updateVariableInfo(variable, newInfoForVar);
+ return _updateVariableInfo(new VariableReference(variable), newInfoForVar);
}
/// Common algorithm for [tryMarkNonNullable], [tryPromoteForTypeCast],
/// and [tryPromoteForTypeCheck]. Builds a [FlowModel] object describing the
- /// effect of updating the [variable] by adding the [testedType] to the
+ /// effect of updating the [reference] by adding the [testedType] to the
/// list of tested types (if not `null`, and not there already), adding the
/// [promotedType] to the chain of promoted types.
///
@@ -1955,7 +1956,7 @@
/// - The variable should not be write-captured.
FlowModel<Variable, Type> _finishTypeTest(
TypeOperations<Variable, Type> typeOperations,
- Variable variable,
+ Reference<Variable, Type> reference,
VariableModel<Variable, Type> info,
Type? testedType,
Type? promotedType,
@@ -1981,7 +1982,7 @@
newReachable == reachable
? this
: _updateVariableInfo(
- variable,
+ reference,
new VariableModel<Variable, Type>(
promotedTypes: newPromotedTypes,
tested: newTested,
@@ -1991,15 +1992,15 @@
reachable: newReachable);
}
- /// Returns a new [FlowModel] where the information for [variable] is replaced
- /// with [model].
+ /// Returns a new [FlowModel] where the information for [reference] is
+ /// replaced with [model].
FlowModel<Variable, Type> _updateVariableInfo(
- Variable variable, VariableModel<Variable, Type> model,
+ Reference<Variable, Type> reference, VariableModel<Variable, Type> model,
{Reachability? reachable}) {
reachable ??= this.reachable;
Map<Variable, VariableModel<Variable, Type>> newVariableInfo =
new Map<Variable, VariableModel<Variable, Type>>.from(variableInfo);
- newVariableInfo[variable] = model;
+ reference.storeInfo(newVariableInfo, model);
return new FlowModel<Variable, Type>.withInfo(reachable, newVariableInfo);
}
@@ -2296,6 +2297,33 @@
}
}
+/// Abstract base class representing a reference to a storage location that
+/// might be of interest to flow analysis to track. This could be a variable,
+/// `this`, or the result of a property access on some other reference.
+///
+/// Note that only variables can undergo promotion, but flow analysis may track
+/// other references in order to give useful error messages to the user about
+/// why promotion did not occur.
+@visibleForTesting
+abstract class Reference<Variable extends Object, Type extends Object> {
+ /// Retrieves the declared type of this reference. This is used as the
+ /// starting point for promotions.
+ Type getDeclaredType(TypeOperations<Variable, Type> typeOperations);
+
+ /// Gets the info for this reference, creating it if it doesn't exist.
+ VariableModel<Variable, Type> getInfo(
+ Map<Variable, VariableModel<Variable, Type>> variableInfo) =>
+ _getInfo(variableInfo) ?? new VariableModel<Variable, Type>.fresh();
+
+ /// Stores info for this reference in [variableInfo].
+ void storeInfo(Map<Variable, VariableModel<Variable, Type>> variableInfo,
+ VariableModel<Variable, Type> variableModel);
+
+ /// Gets the info for this reference, or `null` if it doesn't exist.
+ VariableModel<Variable, Type>? _getInfo(
+ Map<Variable, VariableModel<Variable, Type>> variableInfo);
+}
+
/// Data structure representing a unique value that a variable might take on
/// during execution of the code being analyzed. SSA nodes are immutable (so
/// they can be safety shared among data structures) and have identity (so that
@@ -2977,6 +3005,32 @@
}
}
+/// Specialization of [Reference] representing a reference to a local variable
+/// (or function parameter).
+@visibleForTesting
+class VariableReference<Variable extends Object, Type extends Object>
+ extends Reference<Variable, Type> {
+ /// The variable being referred to.
+ final Variable variable;
+
+ VariableReference(this.variable);
+
+ @override
+ Type getDeclaredType(TypeOperations<Variable, Type> typeOperations) =>
+ typeOperations.variableType(variable);
+
+ @override
+ void storeInfo(Map<Variable, VariableModel<Variable, Type>> variableInfo,
+ VariableModel<Variable, Type> variableModel) {
+ variableInfo[variable] = variableModel;
+ }
+
+ @override
+ VariableModel<Variable, Type>? _getInfo(
+ Map<Variable, VariableModel<Variable, Type>> variableInfo) =>
+ variableInfo[variable];
+}
+
/// [_FlowContext] representing an assert statement or assert initializer.
class _AssertContext<Variable extends Object, Type extends Object>
extends _SimpleContext<Variable, Type> {
@@ -3050,12 +3104,12 @@
/// The type of the expression on the LHS of `==` or `!=`.
final Type _leftOperandType;
- /// If the LHS of `==` or `!=` is a variable reference, the variable.
+ /// If the LHS of `==` or `!=` is a reference, the thing being referred to.
/// Otherwise `null`.
- final Variable? _leftOperandVariable;
+ final Reference<Variable, Type>? _leftOperandReference;
_EqualityOpContext(ExpressionInfo<Variable, Type>? conditionInfo,
- this._leftOperandType, this._leftOperandVariable)
+ this._leftOperandType, this._leftOperandReference)
: super(conditionInfo);
@override
@@ -3091,13 +3145,13 @@
/// corresponding to it. Otherwise `null`.
ExpressionInfo<Variable, Type>? _expressionInfo;
- /// The most recently visited expression which was a variable reference, or
- /// `null` if no expression has been visited that was a variable reference.
- Expression? _expressionWithVariable;
+ /// The most recently visited expression which was a reference, or `null` if
+ /// no such expression has been visited.
+ Expression? _expressionWithReference;
- /// If [_expressionVariable] is not `null`, the variable corresponding to it.
+ /// If [_expressionVariable] is not `null`, the reference corresponding to it.
/// Otherwise `null`.
- Variable? _expressionVariable;
+ Reference<Variable, Type>? _expressionReference;
int _functionNestingLevel = 0;
@@ -3123,9 +3177,10 @@
@override
void asExpression_end(Expression subExpression, Type type) {
- Variable? variable = _getExpressionVariable(subExpression);
- if (variable == null) return;
- _current = _current.tryPromoteForTypeCast(typeOperations, variable, type);
+ Reference<Variable, Type>? reference =
+ _getExpressionReference(subExpression);
+ if (reference == null) return;
+ _current = _current.tryPromoteForTypeCast(typeOperations, reference, type);
}
@override
@@ -3232,10 +3287,11 @@
_EqualityOpContext<Variable, Type> context =
_stack.removeLast() as _EqualityOpContext<Variable, Type>;
ExpressionInfo<Variable, Type>? lhsInfo = context._conditionInfo;
- Variable? lhsVariable = context._leftOperandVariable;
+ Reference<Variable, Type>? lhsReference = context._leftOperandReference;
Type leftOperandType = context._leftOperandType;
ExpressionInfo<Variable, Type>? rhsInfo = _getExpressionInfo(rightOperand);
- Variable? rhsVariable = _getExpressionVariable(rightOperand);
+ Reference<Variable, Type>? rhsReference =
+ _getExpressionReference(rightOperand);
TypeClassification leftOperandTypeClassification =
typeOperations.classifyType(leftOperandType);
TypeClassification rightOperandTypeClassification =
@@ -3253,14 +3309,14 @@
// but weak mode it might produce an "equal" result. We don't want flow
// analysis behavior to depend on mode, so we conservatively assume that
// either result is possible.
- } else if (lhsInfo is _NullInfo<Variable, Type> && rhsVariable != null) {
+ } else if (lhsInfo is _NullInfo<Variable, Type> && rhsReference != null) {
ExpressionInfo<Variable, Type> equalityInfo =
- _current.tryMarkNonNullable(typeOperations, rhsVariable);
+ _current.tryMarkNonNullable(typeOperations, rhsReference);
_storeExpressionInfo(
wholeExpression, notEqual ? equalityInfo : equalityInfo.invert());
- } else if (rhsInfo is _NullInfo<Variable, Type> && lhsVariable != null) {
+ } else if (rhsInfo is _NullInfo<Variable, Type> && lhsReference != null) {
ExpressionInfo<Variable, Type> equalityInfo =
- _current.tryMarkNonNullable(typeOperations, lhsVariable);
+ _current.tryMarkNonNullable(typeOperations, lhsReference);
_storeExpressionInfo(
wholeExpression, notEqual ? equalityInfo : equalityInfo.invert());
}
@@ -3271,7 +3327,7 @@
_stack.add(new _EqualityOpContext<Variable, Type>(
_getExpressionInfo(leftOperand),
leftOperandType,
- _getExpressionVariable(leftOperand)));
+ _getExpressionReference(leftOperand)));
}
@override
@@ -3351,8 +3407,8 @@
if (identical(_expressionWithInfo, oldExpression)) {
_expressionWithInfo = newExpression;
}
- if (identical(_expressionWithVariable, oldExpression)) {
- _expressionWithVariable = newExpression;
+ if (identical(_expressionWithReference, oldExpression)) {
+ _expressionWithReference = newExpression;
}
}
@@ -3411,12 +3467,13 @@
@override
void ifNullExpression_rightBegin(
Expression leftHandSide, Type leftHandSideType) {
- Variable? lhsVariable = _getExpressionVariable(leftHandSide);
+ Reference<Variable, Type>? lhsReference =
+ _getExpressionReference(leftHandSide);
FlowModel<Variable, Type> promoted;
_current = _current.split();
- if (lhsVariable != null) {
+ if (lhsReference != null) {
ExpressionInfo<Variable, Type> promotionInfo =
- _current.tryMarkNonNullable(typeOperations, lhsVariable);
+ _current.tryMarkNonNullable(typeOperations, lhsReference);
_current = promotionInfo.ifFalse;
promoted = promotionInfo.ifTrue;
} else {
@@ -3489,10 +3546,11 @@
@override
void isExpression_end(Expression isExpression, Expression subExpression,
bool isNot, Type type) {
- Variable? subExpressionVariable = _getExpressionVariable(subExpression);
- if (subExpressionVariable != null) {
+ Reference<Variable, Type>? subExpressionReference =
+ _getExpressionReference(subExpression);
+ if (subExpressionReference != null) {
ExpressionInfo<Variable, Type> expressionInfo = _current
- .tryPromoteForTypeCheck(typeOperations, subExpressionVariable, type);
+ .tryPromoteForTypeCheck(typeOperations, subExpressionReference, type);
_storeExpressionInfo(
isExpression, isNot ? expressionInfo.invert() : expressionInfo);
}
@@ -3582,10 +3640,11 @@
@override
void nonNullAssert_end(Expression operand) {
- Variable? operandVariable = _getExpressionVariable(operand);
- if (operandVariable != null) {
+ Reference<Variable, Type>? operandReference =
+ _getExpressionReference(operand);
+ if (operandReference != null) {
_current =
- _current.tryMarkNonNullable(typeOperations, operandVariable).ifTrue;
+ _current.tryMarkNonNullable(typeOperations, operandReference).ifTrue;
}
}
@@ -3602,10 +3661,11 @@
assert(targetType != null);
_current = _current.split();
_stack.add(new _NullAwareAccessContext<Variable, Type>(_current));
- Variable? targetVariable = _getExpressionVariable(target);
- if (targetVariable != null) {
+ Reference<Variable, Type>? targetReference =
+ _getExpressionReference(target);
+ if (targetReference != null) {
_current =
- _current.tryMarkNonNullable(typeOperations, targetVariable).ifTrue;
+ _current.tryMarkNonNullable(typeOperations, targetReference).ifTrue;
}
}
@@ -3622,8 +3682,10 @@
@override
void promote(Variable variable, Type type) {
- _current =
- _current.tryPromoteForTypeCheck(typeOperations, variable, type).ifTrue;
+ _current = _current
+ .tryPromoteForTypeCheck(typeOperations,
+ new VariableReference<Variable, Type>(variable), type)
+ .ifTrue;
}
@override
@@ -3761,8 +3823,11 @@
@override
Type? variableRead(Expression expression, Variable variable) {
- _storeExpressionVariable(expression, variable);
- VariableModel<Variable, Type> variableModel = _current.infoFor(variable);
+ VariableReference<Variable, Type> variableReference =
+ new VariableReference<Variable, Type>(variable);
+ _storeExpressionReference(expression, variableReference);
+ VariableModel<Variable, Type> variableModel =
+ variableReference.getInfo(_current.variableInfo);
if (allowLocalBooleanVarsToPromote) {
ExpressionInfo<Variable, Type>? expressionInfo = variableModel
.ssaNode?.expressionInfo
@@ -3818,8 +3883,8 @@
print(' current: $_current');
print(' expressionWithInfo: $_expressionWithInfo');
print(' expressionInfo: $_expressionInfo');
- print(' expressionWithVariable: $_expressionWithVariable');
- print(' expressionVariable: $_expressionVariable');
+ print(' expressionWithReference: $_expressionWithReference');
+ print(' expressionReference: $_expressionReference');
print(' stack:');
for (_FlowContext stackEntry in _stack.reversed) {
print(' $stackEntry');
@@ -3847,14 +3912,14 @@
}
}
- /// Gets the [Variable] associated with the [expression] (which should be the
- /// last expression that was traversed). If there is no [Variable] associated
- /// with the [expression], then `null` is returned.
- Variable? _getExpressionVariable(Expression? expression) {
- if (identical(expression, _expressionWithVariable)) {
- Variable? expressionVariable = _expressionVariable;
- _expressionVariable = null;
- return expressionVariable;
+ /// Gets the [Reference] associated with the [expression] (which should be the
+ /// last expression that was traversed). If there is no [Reference]
+ /// associated with the [expression], then `null` is returned.
+ Reference<Variable, Type>? _getExpressionReference(Expression? expression) {
+ if (identical(expression, _expressionWithReference)) {
+ Reference<Variable, Type>? expressionReference = _expressionReference;
+ _expressionReference = null;
+ return expressionReference;
} else {
return null;
}
@@ -3880,11 +3945,11 @@
}
/// Associates [expression], which should be the most recently visited
- /// expression, with the given [Variable] object.
- void _storeExpressionVariable(
- Expression expression, Variable expressionVariable) {
- _expressionWithVariable = expression;
- _expressionVariable = expressionVariable;
+ /// expression, with the given [Reference] object.
+ void _storeExpressionReference(
+ Expression expression, Reference<Variable, Type> expressionReference) {
+ _expressionWithReference = expression;
+ _expressionReference = expressionReference;
}
}
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart
index 4ce5600..5d522be 100644
--- a/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart
@@ -3171,28 +3171,34 @@
test('unpromoted -> unchanged (same)', () {
var h = Harness();
var s1 = FlowModel<Var, Type>(Reachability.initial);
- var s2 = s1.tryPromoteForTypeCheck(h, intVar, Type('int')).ifTrue;
+ var s2 =
+ s1.tryPromoteForTypeCheck(h, _varRef(intVar), Type('int')).ifTrue;
expect(s2, same(s1));
});
test('unpromoted -> unchanged (supertype)', () {
var h = Harness();
var s1 = FlowModel<Var, Type>(Reachability.initial);
- var s2 = s1.tryPromoteForTypeCheck(h, intVar, Type('Object')).ifTrue;
+ var s2 = s1
+ .tryPromoteForTypeCheck(h, _varRef(intVar), Type('Object'))
+ .ifTrue;
expect(s2, same(s1));
});
test('unpromoted -> unchanged (unrelated)', () {
var h = Harness();
var s1 = FlowModel<Var, Type>(Reachability.initial);
- var s2 = s1.tryPromoteForTypeCheck(h, intVar, Type('String')).ifTrue;
+ var s2 = s1
+ .tryPromoteForTypeCheck(h, _varRef(intVar), Type('String'))
+ .ifTrue;
expect(s2, same(s1));
});
test('unpromoted -> subtype', () {
var h = Harness();
var s1 = FlowModel<Var, Type>(Reachability.initial);
- var s2 = s1.tryPromoteForTypeCheck(h, intQVar, Type('int')).ifTrue;
+ var s2 =
+ s1.tryPromoteForTypeCheck(h, _varRef(intQVar), Type('int')).ifTrue;
expect(s2.reachable.overallReachable, true);
expect(s2.variableInfo, {
intQVar: _matchVariableModel(chain: ['int'], ofInterest: ['int'])
@@ -3202,38 +3208,44 @@
test('promoted -> unchanged (same)', () {
var h = Harness();
var s1 = FlowModel<Var, Type>(Reachability.initial)
- .tryPromoteForTypeCheck(h, objectQVar, Type('int'))
+ .tryPromoteForTypeCheck(h, _varRef(objectQVar), Type('int'))
.ifTrue;
- var s2 = s1.tryPromoteForTypeCheck(h, objectQVar, Type('int')).ifTrue;
+ var s2 = s1
+ .tryPromoteForTypeCheck(h, _varRef(objectQVar), Type('int'))
+ .ifTrue;
expect(s2, same(s1));
});
test('promoted -> unchanged (supertype)', () {
var h = Harness();
var s1 = FlowModel<Var, Type>(Reachability.initial)
- .tryPromoteForTypeCheck(h, objectQVar, Type('int'))
+ .tryPromoteForTypeCheck(h, _varRef(objectQVar), Type('int'))
.ifTrue;
- var s2 =
- s1.tryPromoteForTypeCheck(h, objectQVar, Type('Object')).ifTrue;
+ var s2 = s1
+ .tryPromoteForTypeCheck(h, _varRef(objectQVar), Type('Object'))
+ .ifTrue;
expect(s2, same(s1));
});
test('promoted -> unchanged (unrelated)', () {
var h = Harness();
var s1 = FlowModel<Var, Type>(Reachability.initial)
- .tryPromoteForTypeCheck(h, objectQVar, Type('int'))
+ .tryPromoteForTypeCheck(h, _varRef(objectQVar), Type('int'))
.ifTrue;
- var s2 =
- s1.tryPromoteForTypeCheck(h, objectQVar, Type('String')).ifTrue;
+ var s2 = s1
+ .tryPromoteForTypeCheck(h, _varRef(objectQVar), Type('String'))
+ .ifTrue;
expect(s2, same(s1));
});
test('promoted -> subtype', () {
var h = Harness();
var s1 = FlowModel<Var, Type>(Reachability.initial)
- .tryPromoteForTypeCheck(h, objectQVar, Type('int?'))
+ .tryPromoteForTypeCheck(h, _varRef(objectQVar), Type('int?'))
.ifTrue;
- var s2 = s1.tryPromoteForTypeCheck(h, objectQVar, Type('int')).ifTrue;
+ var s2 = s1
+ .tryPromoteForTypeCheck(h, _varRef(objectQVar), Type('int'))
+ .ifTrue;
expect(s2.reachable.overallReachable, true);
expect(s2.variableInfo, {
objectQVar: _matchVariableModel(
@@ -3290,7 +3302,7 @@
var h = Harness();
var s1 = FlowModel<Var, Type>(Reachability.initial)
.declare(objectQVar, true)
- .tryPromoteForTypeCheck(h, objectQVar, Type('int'))
+ .tryPromoteForTypeCheck(h, _varRef(objectQVar), Type('int'))
.ifTrue;
expect(s1.variableInfo, contains(objectQVar));
var s2 =
@@ -3309,9 +3321,9 @@
var h = Harness();
var s1 = FlowModel<Var, Type>(Reachability.initial)
.declare(objectQVar, true)
- .tryPromoteForTypeCheck(h, objectQVar, Type('num?'))
+ .tryPromoteForTypeCheck(h, _varRef(objectQVar), Type('num?'))
.ifTrue
- .tryPromoteForTypeCheck(h, objectQVar, Type('int'))
+ .tryPromoteForTypeCheck(h, _varRef(objectQVar), Type('int'))
.ifTrue;
expect(s1.variableInfo, {
objectQVar: _matchVariableModel(
@@ -3336,11 +3348,11 @@
var h = Harness();
var s1 = FlowModel<Var, Type>(Reachability.initial)
.declare(objectQVar, true)
- .tryPromoteForTypeCheck(h, objectQVar, Type('num?'))
+ .tryPromoteForTypeCheck(h, _varRef(objectQVar), Type('num?'))
.ifTrue
- .tryPromoteForTypeCheck(h, objectQVar, Type('num'))
+ .tryPromoteForTypeCheck(h, _varRef(objectQVar), Type('num'))
.ifTrue
- .tryPromoteForTypeCheck(h, objectQVar, Type('int'))
+ .tryPromoteForTypeCheck(h, _varRef(objectQVar), Type('int'))
.ifTrue;
expect(s1.variableInfo, {
objectQVar: _matchVariableModel(
@@ -3365,9 +3377,9 @@
var h = Harness();
var s1 = FlowModel<Var, Type>(Reachability.initial)
.declare(objectQVar, true)
- .tryPromoteForTypeCheck(h, objectQVar, Type('num?'))
+ .tryPromoteForTypeCheck(h, _varRef(objectQVar), Type('num?'))
.ifTrue
- .tryPromoteForTypeCheck(h, objectQVar, Type('num'))
+ .tryPromoteForTypeCheck(h, _varRef(objectQVar), Type('num'))
.ifTrue;
expect(s1.variableInfo, {
objectQVar: _matchVariableModel(
@@ -3393,9 +3405,9 @@
var h = Harness();
var s1 = FlowModel<Var, Type>(Reachability.initial)
.declare(objectQVar, true)
- .tryPromoteForTypeCheck(h, objectQVar, Type('num?'))
+ .tryPromoteForTypeCheck(h, _varRef(objectQVar), Type('num?'))
.ifTrue
- .tryPromoteForTypeCheck(h, objectQVar, Type('num'))
+ .tryPromoteForTypeCheck(h, _varRef(objectQVar), Type('num'))
.ifTrue;
expect(s1.variableInfo, {
objectQVar: _matchVariableModel(
@@ -3458,7 +3470,7 @@
var h = Harness();
var s1 = FlowModel<Var, Type>(Reachability.initial)
.declare(objectQVar, true)
- .tryPromoteForTypeCheck(h, objectQVar, Type('int?'))
+ .tryPromoteForTypeCheck(h, _varRef(objectQVar), Type('int?'))
.ifTrue;
expect(s1.variableInfo, {
objectQVar: _matchVariableModel(
@@ -3480,7 +3492,7 @@
var h = Harness();
var s1 = FlowModel<Var, Type>(Reachability.initial)
.declare(objectQVar, true)
- .tryPromoteForTypeCheck(h, objectQVar, Type('int?'))
+ .tryPromoteForTypeCheck(h, _varRef(objectQVar), Type('int?'))
.ifFalse;
expect(s1.variableInfo, {
objectQVar: _matchVariableModel(
@@ -3503,7 +3515,7 @@
var h = Harness();
var s1 = FlowModel<Var, Type>(Reachability.initial)
.declare(objectQVar, true)
- .tryPromoteForTypeCheck(h, objectQVar, Type('num?'))
+ .tryPromoteForTypeCheck(h, _varRef(objectQVar), Type('num?'))
.ifFalse;
expect(s1.variableInfo, {
objectQVar: _matchVariableModel(
@@ -3525,9 +3537,9 @@
var h = Harness();
var s1 = FlowModel<Var, Type>(Reachability.initial)
.declare(objectQVar, true)
- .tryPromoteForTypeCheck(h, objectQVar, Type('num?'))
+ .tryPromoteForTypeCheck(h, _varRef(objectQVar), Type('num?'))
.ifTrue
- .tryPromoteForTypeCheck(h, objectQVar, Type('int?'))
+ .tryPromoteForTypeCheck(h, _varRef(objectQVar), Type('int?'))
.ifFalse;
expect(s1.variableInfo, {
objectQVar: _matchVariableModel(
@@ -3594,9 +3606,9 @@
var s1 = FlowModel<Var, Type>(Reachability.initial)
.declare(x, true)
- .tryPromoteForTypeCheck(h, x, Type('B?'))
+ .tryPromoteForTypeCheck(h, _varRef(x), Type('B?'))
.ifFalse
- .tryPromoteForTypeCheck(h, x, Type('A?'))
+ .tryPromoteForTypeCheck(h, _varRef(x), Type('A?'))
.ifFalse;
expect(s1.variableInfo, {
x: _matchVariableModel(
@@ -3619,9 +3631,9 @@
var s1 = FlowModel<Var, Type>(Reachability.initial)
.declare(x, true)
- .tryPromoteForTypeCheck(h, x, Type('A?'))
+ .tryPromoteForTypeCheck(h, _varRef(x), Type('A?'))
.ifFalse
- .tryPromoteForTypeCheck(h, x, Type('B?'))
+ .tryPromoteForTypeCheck(h, _varRef(x), Type('B?'))
.ifFalse;
expect(s1.variableInfo, {
x: _matchVariableModel(
@@ -3644,9 +3656,9 @@
var s1 = FlowModel<Var, Type>(Reachability.initial)
.declare(x, true)
- .tryPromoteForTypeCheck(h, x, Type('A'))
+ .tryPromoteForTypeCheck(h, _varRef(x), Type('A'))
.ifFalse
- .tryPromoteForTypeCheck(h, x, Type('A?'))
+ .tryPromoteForTypeCheck(h, _varRef(x), Type('A?'))
.ifFalse;
expect(s1.variableInfo, {
x: _matchVariableModel(
@@ -3670,9 +3682,9 @@
var h = Harness();
var s1 = FlowModel<Var, Type>(Reachability.initial)
.declare(objectQVar, true)
- .tryPromoteForTypeCheck(h, objectQVar, Type('num?'))
+ .tryPromoteForTypeCheck(h, _varRef(objectQVar), Type('num?'))
.ifFalse
- .tryPromoteForTypeCheck(h, objectQVar, Type('num*'))
+ .tryPromoteForTypeCheck(h, _varRef(objectQVar), Type('num*'))
.ifFalse;
expect(s1.variableInfo, {
objectQVar: _matchVariableModel(
@@ -3698,9 +3710,9 @@
var h = Harness();
var s1 = FlowModel<Var, Type>(Reachability.initial)
.declare(objectQVar, true)
- .tryPromoteForTypeCheck(h, objectQVar, Type('num?'))
+ .tryPromoteForTypeCheck(h, _varRef(objectQVar), Type('num?'))
.ifFalse
- .tryPromoteForTypeCheck(h, objectQVar, Type('num*'))
+ .tryPromoteForTypeCheck(h, _varRef(objectQVar), Type('num*'))
.ifFalse;
expect(s1.variableInfo, {
objectQVar: _matchVariableModel(
@@ -3730,9 +3742,9 @@
var s1 = FlowModel<Var, Type>(Reachability.initial)
.declare(x, true)
- .tryPromoteForTypeCheck(h, x, Type('num?'))
+ .tryPromoteForTypeCheck(h, _varRef(x), Type('num?'))
.ifTrue
- .tryPromoteForTypeCheck(h, x, Type('int?'))
+ .tryPromoteForTypeCheck(h, _varRef(x), Type('int?'))
.ifTrue;
expect(s1.variableInfo, {
x: _matchVariableModel(
@@ -3775,14 +3787,14 @@
test('unpromoted -> unchanged', () {
var h = Harness();
var s1 = FlowModel<Var, Type>(Reachability.initial);
- var s2 = s1.tryMarkNonNullable(h, intVar).ifTrue;
+ var s2 = s1.tryMarkNonNullable(h, _varRef(intVar)).ifTrue;
expect(s2, same(s1));
});
test('unpromoted -> promoted', () {
var h = Harness();
var s1 = FlowModel<Var, Type>(Reachability.initial);
- var s2 = s1.tryMarkNonNullable(h, intQVar).ifTrue;
+ var s2 = s1.tryMarkNonNullable(h, _varRef(intQVar)).ifTrue;
expect(s2.reachable.overallReachable, true);
expect(s2.infoFor(intQVar),
_matchVariableModel(chain: ['int'], ofInterest: []));
@@ -3791,18 +3803,18 @@
test('promoted -> unchanged', () {
var h = Harness();
var s1 = FlowModel<Var, Type>(Reachability.initial)
- .tryPromoteForTypeCheck(h, objectQVar, Type('int'))
+ .tryPromoteForTypeCheck(h, _varRef(objectQVar), Type('int'))
.ifTrue;
- var s2 = s1.tryMarkNonNullable(h, objectQVar).ifTrue;
+ var s2 = s1.tryMarkNonNullable(h, _varRef(objectQVar)).ifTrue;
expect(s2, same(s1));
});
test('promoted -> re-promoted', () {
var h = Harness();
var s1 = FlowModel<Var, Type>(Reachability.initial)
- .tryPromoteForTypeCheck(h, objectQVar, Type('int?'))
+ .tryPromoteForTypeCheck(h, _varRef(objectQVar), Type('int?'))
.ifTrue;
- var s2 = s1.tryMarkNonNullable(h, objectQVar).ifTrue;
+ var s2 = s1.tryMarkNonNullable(h, _varRef(objectQVar)).ifTrue;
expect(s2.reachable.overallReachable, true);
expect(s2.variableInfo, {
objectQVar:
@@ -3813,7 +3825,7 @@
test('promote to Never', () {
var h = Harness();
var s1 = FlowModel<Var, Type>(Reachability.initial);
- var s2 = s1.tryMarkNonNullable(h, nullVar).ifTrue;
+ var s2 = s1.tryMarkNonNullable(h, _varRef(nullVar)).ifTrue;
expect(s2.reachable.overallReachable, false);
expect(s2.infoFor(nullVar),
_matchVariableModel(chain: ['Never'], ofInterest: []));
@@ -3825,7 +3837,7 @@
var h = Harness();
var s1 = FlowModel<Var, Type>(Reachability.initial)
.declare(intQVar, true)
- .tryPromoteForTypeCheck(h, objectQVar, Type('int'))
+ .tryPromoteForTypeCheck(h, _varRef(objectQVar), Type('int'))
.ifTrue;
var s2 = s1.conservativeJoin([intQVar], []);
expect(s2, isNot(same(s1)));
@@ -3839,9 +3851,9 @@
test('written', () {
var h = Harness();
var s1 = FlowModel<Var, Type>(Reachability.initial)
- .tryPromoteForTypeCheck(h, objectQVar, Type('int'))
+ .tryPromoteForTypeCheck(h, _varRef(objectQVar), Type('int'))
.ifTrue
- .tryPromoteForTypeCheck(h, intQVar, Type('int'))
+ .tryPromoteForTypeCheck(h, _varRef(intQVar), Type('int'))
.ifTrue;
var s2 = s1.conservativeJoin([intQVar], []);
expect(s2.reachable.overallReachable, true);
@@ -3854,9 +3866,9 @@
test('write captured', () {
var h = Harness();
var s1 = FlowModel<Var, Type>(Reachability.initial)
- .tryPromoteForTypeCheck(h, objectQVar, Type('int'))
+ .tryPromoteForTypeCheck(h, _varRef(objectQVar), Type('int'))
.ifTrue
- .tryPromoteForTypeCheck(h, intQVar, Type('int'))
+ .tryPromoteForTypeCheck(h, _varRef(intQVar), Type('int'))
.ifTrue;
var s2 = s1.conservativeJoin([], [intQVar]);
expect(s2.reachable.overallReachable, true);
@@ -3946,7 +3958,7 @@
var s0 = FlowModel<Var, Type>(Reachability.initial).declare(a, false);
// In s1, a is write captured. In s2 it's promoted.
var s1 = s0.conservativeJoin([a], [a]);
- var s2 = s0.tryPromoteForTypeCheck(h, a, Type('int')).ifTrue;
+ var s2 = s0.tryPromoteForTypeCheck(h, _varRef(a), Type('int')).ifTrue;
expect(
s1.rebaseForward(h, s2).infoFor(a),
_matchVariableModel(writeCaptured: true, chain: isNull),
@@ -3968,11 +3980,14 @@
s1 = s1.write(x, Type('Object?'), new SsaNode<Var, Type>(null), h);
}
if (thisType != null) {
- s1 = s1.tryPromoteForTypeCheck(h, x, Type(thisType)).ifTrue;
+ s1 =
+ s1.tryPromoteForTypeCheck(h, _varRef(x), Type(thisType)).ifTrue;
}
var s2 = otherType == null
? s0
- : s0.tryPromoteForTypeCheck(h, x, Type(otherType)).ifTrue;
+ : s0
+ .tryPromoteForTypeCheck(h, _varRef(x), Type(otherType))
+ .ifTrue;
var result = s2.rebaseForward(h, s1);
if (expectedChain == null) {
expect(result.variableInfo, contains(x));
@@ -4021,20 +4036,23 @@
var initialModel =
FlowModel<Var, Type>(Reachability.initial).declare(x, true);
for (var t in before) {
- initialModel =
- initialModel.tryPromoteForTypeCheck(h, x, Type(t)).ifTrue;
+ initialModel = initialModel
+ .tryPromoteForTypeCheck(h, _varRef(x), Type(t))
+ .ifTrue;
}
_checkChain(initialModel.infoFor(x).promotedTypes, before);
var tryModel = initialModel;
for (var t in inTry) {
- tryModel = tryModel.tryPromoteForTypeCheck(h, x, Type(t)).ifTrue;
+ tryModel =
+ tryModel.tryPromoteForTypeCheck(h, _varRef(x), Type(t)).ifTrue;
}
var expectedTryChain = before.toList()..addAll(inTry);
_checkChain(tryModel.infoFor(x).promotedTypes, expectedTryChain);
var finallyModel = initialModel;
for (var t in inFinally) {
- finallyModel =
- finallyModel.tryPromoteForTypeCheck(h, x, Type(t)).ifTrue;
+ finallyModel = finallyModel
+ .tryPromoteForTypeCheck(h, _varRef(x), Type(t))
+ .ifTrue;
}
var expectedFinallyChain = before.toList()..addAll(inFinally);
_checkChain(
@@ -4073,8 +4091,9 @@
var h = Harness();
var a = Var('a', 'Object');
var s0 = FlowModel<Var, Type>(Reachability.initial).declare(a, false);
- var s1 = s0.tryPromoteForTypeCheck(h, a, Type('int')).ifFalse;
- var s2 = s0.tryPromoteForTypeCheck(h, a, Type('String')).ifFalse;
+ var s1 = s0.tryPromoteForTypeCheck(h, _varRef(a), Type('int')).ifFalse;
+ var s2 =
+ s0.tryPromoteForTypeCheck(h, _varRef(a), Type('String')).ifFalse;
expect(
s1.rebaseForward(h, s2).infoFor(a),
_matchVariableModel(ofInterest: ['int', 'String']),
@@ -4651,3 +4670,6 @@
'unassigned: ${_describeMatcher(unassignedMatcher)}, '
'writeCaptured: ${_describeMatcher(writeCapturedMatcher)})');
}
+
+Reference<Var, Type> _varRef(Var variable) =>
+ new VariableReference<Var, Type>(variable);
diff --git a/pkg/analysis_server/test/abstract_single_unit.dart b/pkg/analysis_server/test/abstract_single_unit.dart
index 1dca4d3..1a04502 100644
--- a/pkg/analysis_server/test/abstract_single_unit.dart
+++ b/pkg/analysis_server/test/abstract_single_unit.dart
@@ -7,9 +7,7 @@
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/file_system/file_system.dart';
-import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:analyzer/src/dart/error/hint_codes.dart';
-import 'package:analyzer/src/generated/java_engine.dart';
import 'package:analyzer/src/test_utilities/find_element.dart';
import 'package:analyzer/src/test_utilities/find_node.dart';
import 'package:analyzer/src/test_utilities/platform.dart';
@@ -52,19 +50,6 @@
return findOffset(search) + search.length;
}
- AstNode findNodeAtOffset(int offset, [Predicate<AstNode> predicate]) {
- var result = NodeLocator(offset).searchWithin(testUnit);
- if (result != null && predicate != null) {
- result = result.thisOrAncestorMatching(predicate);
- }
- return result;
- }
-
- AstNode findNodeAtString(String search, [Predicate<AstNode> predicate]) {
- var offset = findOffset(search);
- return findNodeAtOffset(offset, predicate);
- }
-
int findOffset(String search) {
var offset = testCode.indexOf(search);
expect(offset, isNonNegative, reason: "Not found '$search' in\n$testCode");
diff --git a/pkg/analysis_server/test/services/correction/name_suggestion_test.dart b/pkg/analysis_server/test/services/correction/name_suggestion_test.dart
index a4dce49..243fd9b 100644
--- a/pkg/analysis_server/test/services/correction/name_suggestion_test.dart
+++ b/pkg/analysis_server/test/services/correction/name_suggestion_test.dart
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:analysis_server/src/services/correction/name_suggestion.dart';
-import 'package:analyzer/dart/ast/ast.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -25,7 +24,7 @@
}
''');
var excluded = <String>{};
- var expr = findNodeAtString('as String', (node) => node is AsExpression);
+ var expr = findNode.as_('as String');
expect(getVariableNameSuggestionsForExpression(null, expr, excluded),
unorderedEquals(['sortedNodes', 'nodes']));
}
@@ -39,8 +38,7 @@
''');
var excluded = <String>{};
var expectedType = findElement.localVar('node').type;
- Expression assignedExpression =
- findNodeAtString('null;', (node) => node is NullLiteral);
+ var assignedExpression = findNode.nullLiteral('null;');
var suggestions = getVariableNameSuggestionsForExpression(
expectedType, assignedExpression, excluded);
expect(suggestions, unorderedEquals(['treeNode', 'node']));
@@ -53,7 +51,7 @@
}
''');
var expectedType = findElement.localVar('res').type;
- Expression assignedExpression = findNodeAtString('0.0;');
+ var assignedExpression = findNode.doubleLiteral('0.0;');
// first choice for "double" is "d"
expect(
getVariableNameSuggestionsForExpression(
@@ -73,7 +71,7 @@
}
''');
var expectedType = findElement.localVar('res').type;
- Expression assignedExpression = findNodeAtString('0;');
+ var assignedExpression = findNode.integerLiteral('0;');
// first choice for "int" is "i"
expect(
getVariableNameSuggestionsForExpression(
@@ -93,7 +91,7 @@
}
''');
var expectedType = findElement.localVar('res').type;
- Expression assignedExpression = findNodeAtString("'abc';");
+ var assignedExpression = findNode.stringLiteral("'abc';");
// first choice for "String" is "s"
expect(
getVariableNameSuggestionsForExpression(
@@ -110,7 +108,7 @@
}
''');
var excluded = <String>{};
- var expr = findNodeAtString('new List');
+ var expr = findNode.instanceCreation('new List');
expect(
getVariableNameSuggestionsForExpression(null, expr, excluded,
isMethod: false),
@@ -129,7 +127,7 @@
}
''');
var excluded = <String>{};
- var expr = findNodeAtString('topNodes[0]').parent;
+ var expr = findNode.index('topNodes[0]');
var names = getVariableNameSuggestionsForExpression(null, expr, excluded);
expect(names, unorderedEquals(['topNode', 'node', 'object']));
}
@@ -147,11 +145,11 @@
var excluded = <String>{};
expect(
getVariableNameSuggestionsForExpression(
- null, findNodeAtString('new NoSuchClass()'), excluded),
+ null, findNode.instanceCreation('new NoSuchClass()'), excluded),
unorderedEquals(['noSuchClass', 'suchClass', 'class']));
expect(
- getVariableNameSuggestionsForExpression(
- null, findNodeAtString('new NoSuchClass.named()'), excluded),
+ getVariableNameSuggestionsForExpression(null,
+ findNode.instanceCreation('new NoSuchClass.named()'), excluded),
unorderedEquals(['noSuchClass', 'suchClass', 'class']));
// TODO(scheglov) This test does not work.
// In "p.NoSuchClass" the identifier "p" is not resolved to a PrefixElement.
@@ -172,17 +170,17 @@
''');
var excluded = <String>{};
{
- var expr = findNodeAtString('111');
+ var expr = findNode.integerLiteral('111');
expect(getVariableNameSuggestionsForExpression(null, expr, excluded),
unorderedEquals(['a']));
}
{
- var expr = findNodeAtString('222');
+ var expr = findNode.integerLiteral('222');
expect(getVariableNameSuggestionsForExpression(null, expr, excluded),
unorderedEquals(['b']));
}
{
- var expr = findNodeAtString('333');
+ var expr = findNode.integerLiteral('333');
expect(getVariableNameSuggestionsForExpression(null, expr, excluded),
unorderedEquals(['c']));
}
@@ -197,17 +195,17 @@
''');
var excluded = <String>{};
{
- var expr = findNodeAtString('111');
+ var expr = findNode.integerLiteral('111');
expect(getVariableNameSuggestionsForExpression(null, expr, excluded),
unorderedEquals(['a']));
}
{
- var expr = findNodeAtString('222');
+ var expr = findNode.integerLiteral('222');
expect(getVariableNameSuggestionsForExpression(null, expr, excluded),
unorderedEquals(['b']));
}
{
- var expr = findNodeAtString('333');
+ var expr = findNode.integerLiteral('333');
expect(getVariableNameSuggestionsForExpression(null, expr, excluded),
unorderedEquals(['c']));
}
@@ -222,12 +220,12 @@
''');
var excluded = <String>{};
{
- var expr = findNodeAtString('111');
+ var expr = findNode.integerLiteral('111');
expect(getVariableNameSuggestionsForExpression(null, expr, excluded),
unorderedEquals(['a']));
}
{
- var expr = findNodeAtString('222');
+ var expr = findNode.integerLiteral('222');
expect(getVariableNameSuggestionsForExpression(null, expr, excluded),
unorderedEquals(['b']));
}
@@ -240,7 +238,7 @@
}
''');
var excluded = <String>{};
- var expr = findNodeAtString('p.get', (node) => node is MethodInvocation);
+ var expr = findNode.methodInvocation('p.get');
expect(getVariableNameSuggestionsForExpression(null, expr, excluded),
unorderedEquals(['sortedNodes', 'nodes']));
}
@@ -252,7 +250,7 @@
}
''');
var excluded = <String>{};
- var expr = findNodeAtString('p.sorted', (node) => node is MethodInvocation);
+ var expr = findNode.methodInvocation('p.sorted');
expect(getVariableNameSuggestionsForExpression(null, expr, excluded),
unorderedEquals(['sortedNodes', 'nodes']));
}
@@ -264,7 +262,7 @@
}
''');
var excluded = <String>{};
- var expr = findNodeAtString('p.get', (node) => node is MethodInvocation);
+ var expr = findNode.methodInvocation('p.get');
expect(getVariableNameSuggestionsForExpression(null, expr, excluded),
unorderedEquals([]));
}
@@ -278,9 +276,7 @@
var excluded = <String>{};
expect(
getVariableNameSuggestionsForExpression(
- null,
- findNodeAtString('p.sorted', (node) => node is PrefixedIdentifier),
- excluded),
+ null, findNode.prefixed('p.sorted'), excluded),
unorderedEquals(['sortedNodes', 'nodes']));
}
@@ -294,15 +290,11 @@
var excluded = <String>{};
expect(
getVariableNameSuggestionsForExpression(
- null,
- findNodeAtString('p._name', (node) => node is PrefixedIdentifier),
- excluded),
+ null, findNode.prefixed('p._name'), excluded),
unorderedEquals(['name']));
expect(
getVariableNameSuggestionsForExpression(
- null,
- findNodeAtString('p._compute', (node) => node is MethodInvocation),
- excluded),
+ null, findNode.methodInvocation('p._compute'), excluded),
unorderedEquals(['computeSuffix', 'suffix']));
}
@@ -313,8 +305,7 @@
}
''');
var excluded = <String>{};
- PropertyAccess expression =
- findNodeAtString('p.q.sorted', (node) => node is PropertyAccess);
+ var expression = findNode.propertyAccess('.sorted');
expect(getVariableNameSuggestionsForExpression(null, expression, excluded),
unorderedEquals(['sortedNodes', 'nodes']));
}
@@ -327,7 +318,7 @@
}
''');
var excluded = <String>{};
- var expr = findNodeAtString('sortedNodes;');
+ var expr = findNode.simple('sortedNodes;');
expect(getVariableNameSuggestionsForExpression(null, expr, excluded),
unorderedEquals(['sortedNodes', 'nodes']));
}
@@ -342,10 +333,7 @@
var excluded = <String>{};
expect(
getVariableNameSuggestionsForExpression(
- null,
- findNodeAtString(
- 'getSortedNodes();', (node) => node is MethodInvocation),
- excluded),
+ null, findNode.methodInvocation('getSortedNodes();'), excluded),
unorderedEquals(['sortedNodes', 'nodes']));
}
diff --git a/pkg/analysis_server/test/services/correction/status_test.dart b/pkg/analysis_server/test/services/correction/status_test.dart
index ac7f555..96fb856 100644
--- a/pkg/analysis_server/test/services/correction/status_test.dart
+++ b/pkg/analysis_server/test/services/correction/status_test.dart
@@ -60,7 +60,7 @@
main() {
}
''');
- var node = findNodeAtString('main');
+ var node = findNode.simple('main');
// check
var location = newLocation_fromNode(node);
expect(location.file, testFile);
diff --git a/pkg/analysis_server/test/services/correction/util_test.dart b/pkg/analysis_server/test/services/correction/util_test.dart
index ad29a506..cba88ef 100644
--- a/pkg/analysis_server/test/services/correction/util_test.dart
+++ b/pkg/analysis_server/test/services/correction/util_test.dart
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:analysis_server/src/services/correction/util.dart';
-import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart';
import 'package:analyzer_plugin/src/utilities/string_utilities.dart';
@@ -33,7 +32,7 @@
}
}
''');
- IfStatement ifStatement = findNodeAtString('if (');
+ var ifStatement = findNode.ifStatement('if (');
var condition = ifStatement.condition;
var result = CorrectionUtils(testAnalysisResult).invertCondition(condition);
expect(result, expected);
diff --git a/pkg/analysis_server/test/services/refactoring/rename_import_test.dart b/pkg/analysis_server/test/services/refactoring/rename_import_test.dart
index da9fa1c..b0305ee 100644
--- a/pkg/analysis_server/test/services/refactoring/rename_import_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/rename_import_test.dart
@@ -2,7 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -225,8 +224,7 @@
}
void _createRefactoring(String search) {
- ImportDirective directive =
- findNodeAtString(search, (node) => node is ImportDirective);
+ var directive = findNode.import(search);
createRenameRefactoringForElement(directive.element);
}
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/element_matcher_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/element_matcher_test.dart
index 89ec9da..f462b36 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/element_matcher_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/element_matcher_test.dart
@@ -22,7 +22,7 @@
{List<String> expectedComponents,
List<ElementKind> expectedKinds,
List<String> expectedUris}) {
- var node = findNodeAtString(search);
+ var node = findNode.any(search);
var matcher = ElementMatcher.forNode(node);
if (expectedUris != null) {
expect(matcher.importedUris,
diff --git a/pkg/analysis_server/test/src/utilities/flutter_test.dart b/pkg/analysis_server/test/src/utilities/flutter_test.dart
index 0d8276a..cce042e 100644
--- a/pkg/analysis_server/test/src/utilities/flutter_test.dart
+++ b/pkg/analysis_server/test/src/utilities/flutter_test.dart
@@ -165,16 +165,14 @@
Text createText(String txt) => new Text(txt);
''');
{
- MethodInvocation invocation = findNodeAtString(
- 'createEmptyText();', (node) => node is MethodInvocation);
+ var invocation = findNode.methodInvocation('createEmptyText();');
expect(_flutter.identifyWidgetExpression(invocation), invocation);
var argumentList = invocation.argumentList;
expect(_flutter.identifyWidgetExpression(argumentList), isNull);
}
{
- MethodInvocation invocation = findNodeAtString(
- "createText('xyz');", (node) => node is MethodInvocation);
+ var invocation = findNode.methodInvocation("createText('xyz');");
expect(_flutter.identifyWidgetExpression(invocation), invocation);
var argumentList = invocation.argumentList;
expect(_flutter.identifyWidgetExpression(argumentList), isNull);
@@ -195,7 +193,7 @@
Text createEmptyText() => new Text('');
''');
- Expression childExpression = findNodeAtString('child: ');
+ var childExpression = findNode.namedExpression('child: ');
expect(_flutter.identifyWidgetExpression(childExpression), isNull);
}
@@ -212,7 +210,7 @@
foo.bar; // ref
}
''');
- SimpleIdentifier bar = findNodeAtString('bar; // ref');
+ var bar = findNode.simple('bar; // ref');
expect(_flutter.identifyWidgetExpression(bar), bar.parent);
}
@@ -229,7 +227,7 @@
foo.bar; // ref
}
''');
- SimpleIdentifier foo = findNodeAtString('foo.bar');
+ var foo = findNode.simple('foo.bar');
expect(_flutter.identifyWidgetExpression(foo), foo.parent);
}
@@ -241,7 +239,7 @@
widget; // ref
}
''');
- Expression expression = findNodeAtString('widget; // ref');
+ var expression = findNode.simple('widget; // ref');
expect(_flutter.identifyWidgetExpression(expression), expression);
}
@@ -258,12 +256,12 @@
''');
expect(_flutter.identifyWidgetExpression(null), isNull);
{
- Expression expression = findNodeAtString('42;');
+ var expression = findNode.integerLiteral('42;');
expect(_flutter.identifyWidgetExpression(expression), isNull);
}
{
- Expression expression = findNodeAtString('intVariable;');
+ var expression = findNode.simple('intVariable;');
expect(_flutter.identifyWidgetExpression(expression), isNull);
}
}
@@ -279,7 +277,7 @@
void useWidget(Widget w) {}
''');
- Expression expression = findNodeAtString('text); // ref');
+ var expression = findNode.simple('text); // ref');
expect(_flutter.identifyWidgetExpression(expression), expression);
}
@@ -323,10 +321,10 @@
condition ? w1 : w2;
}
''');
- Expression thenWidget = findNodeAtString('w1 :');
+ var thenWidget = findNode.simple('w1 :');
expect(_flutter.identifyWidgetExpression(thenWidget), thenWidget);
- Expression elseWidget = findNodeAtString('w2;');
+ var elseWidget = findNode.simple('w2;');
expect(_flutter.identifyWidgetExpression(elseWidget), elseWidget);
}
@@ -337,7 +335,7 @@
main(Widget widget) => widget; // ref
''');
- Expression expression = findNodeAtString('widget; // ref');
+ var expression = findNode.simple('widget; // ref');
expect(_flutter.identifyWidgetExpression(expression), expression);
}
@@ -350,7 +348,7 @@
widget; // ref
}
''');
- Expression expression = findNodeAtString('widget; // ref');
+ var expression = findNode.simple('widget; // ref');
expect(_flutter.identifyWidgetExpression(expression), expression);
}
@@ -400,7 +398,7 @@
return [widget]; // ref
}
''');
- Expression expression = findNodeAtString('widget]; // ref');
+ var expression = findNode.simple('widget]; // ref');
expect(_flutter.identifyWidgetExpression(expression), expression);
}
@@ -415,7 +413,7 @@
void useWidget({Widget child}) {}
''');
- Expression expression = findNodeAtString('text); // ref');
+ var expression = findNode.simple('text); // ref');
expect(_flutter.identifyWidgetExpression(expression), expression);
}
@@ -427,7 +425,7 @@
return widget; // ref
}
''');
- Expression expression = findNodeAtString('widget; // ref');
+ var expression = findNode.simple('widget; // ref');
expect(_flutter.identifyWidgetExpression(expression), expression);
}
@@ -494,46 +492,44 @@
Text createEmptyText() => new Text('');
''');
{
- Expression expression = findNodeAtString('named(); // use');
+ var expression = findNode.simple('named(); // use');
expect(_flutter.isWidgetExpression(expression), isFalse);
var creation = expression.parent.parent as InstanceCreationExpression;
expect(_flutter.isWidgetExpression(creation), isTrue);
}
{
- Expression expression = findNodeAtString("new Text('abc')");
+ var expression = findNode.instanceCreation("new Text('abc')");
expect(_flutter.isWidgetExpression(expression), isTrue);
}
{
- Expression expression = findNodeAtString('text;');
+ var expression = findNode.simple('text;');
expect(_flutter.isWidgetExpression(expression), isTrue);
}
{
- Expression expression = findNodeAtString(
- 'createEmptyText();', (node) => node is MethodInvocation);
+ var expression = findNode.methodInvocation('createEmptyText();');
expect(_flutter.isWidgetExpression(expression), isTrue);
}
{
- SimpleIdentifier expression = findNodeAtString('Container(');
+ var expression = findNode.simple('Container(');
expect(_flutter.isWidgetExpression(expression), isFalse);
}
{
- NamedExpression expression =
- findNodeAtString('child: ', (n) => n is NamedExpression);
+ var expression = findNode.namedExpression('child: ');
expect(_flutter.isWidgetExpression(expression), isFalse);
}
{
- Expression expression = findNodeAtString('42;');
+ var expression = findNode.integerLiteral('42;');
expect(_flutter.isWidgetExpression(expression), isFalse);
}
{
- Expression expression = findNodeAtString('intVariable;');
+ var expression = findNode.simple('intVariable;');
expect(_flutter.isWidgetExpression(expression), isFalse);
}
}
diff --git a/pkg/analysis_server/tool/code_completion/corpus.dart b/pkg/analysis_server/tool/code_completion/corpus.dart
index 71e09fb..8f711d2 100644
--- a/pkg/analysis_server/tool/code_completion/corpus.dart
+++ b/pkg/analysis_server/tool/code_completion/corpus.dart
@@ -91,8 +91,9 @@
Future<String> _getBody(String url) async => (await _getResponse(url)).body;
-Future<http.Response> _getResponse(String url) async => _client
- .get(url, headers: const {'User-Agent': 'dart.pkg.completion_metrics'});
+Future<http.Response> _getResponse(String url) async =>
+ _client.get(Uri.parse(url),
+ headers: const {'User-Agent': 'dart.pkg.completion_metrics'});
bool _hasPubspec(FileSystemEntity f) =>
f is Directory && File(path.join(f.path, 'pubspec.yaml')).existsSync();
diff --git a/pkg/analyzer/lib/src/test_utilities/find_node.dart b/pkg/analyzer/lib/src/test_utilities/find_node.dart
index 11bd738..91afc47 100644
--- a/pkg/analyzer/lib/src/test_utilities/find_node.dart
+++ b/pkg/analyzer/lib/src/test_utilities/find_node.dart
@@ -167,6 +167,10 @@
return _node(search, (n) => n is IfElement);
}
+ IfStatement ifStatement(String search) {
+ return _node(search, (n) => n is IfStatement);
+ }
+
ImportDirective import(String search) {
return _node(search, (n) => n is ImportDirective);
}
diff --git a/pkg/analyzer_plugin/test/support/abstract_single_unit.dart b/pkg/analyzer_plugin/test/support/abstract_single_unit.dart
index 96d22e0..ca144aa 100644
--- a/pkg/analyzer_plugin/test/support/abstract_single_unit.dart
+++ b/pkg/analyzer_plugin/test/support/abstract_single_unit.dart
@@ -21,8 +21,6 @@
String testCode;
String testFile;
CompilationUnit testUnit;
- CompilationUnitElement testUnitElement;
- LibraryElement testLibraryElement;
FindNode findNode;
FindElement findElement;
@@ -90,8 +88,12 @@
return length;
}
- Future<void> resolveTestUnit(String code) async {
+ Future<void> resolveTestCode(String code) async {
addTestSource(code);
+ await resolveTestFile();
+ }
+
+ Future resolveTestFile() async {
var result = await resolveFile(testFile);
testCode = result.content;
testUnit = result.unit;
@@ -106,8 +108,6 @@
error.errorCode != HintCode.UNUSED_LOCAL_VARIABLE;
}), isEmpty);
}
- testUnitElement = testUnit.declaredElement;
- testLibraryElement = testUnitElement.library;
findNode = FindNode(testCode, testUnit);
findElement = FindElement(testUnit);
}
diff --git a/pkg/analyzer_plugin/test/utilities/analyzer_converter_test.dart b/pkg/analyzer_plugin/test/utilities/analyzer_converter_test.dart
index 49b70c5..db1a6fc 100644
--- a/pkg/analyzer_plugin/test/utilities/analyzer_converter_test.dart
+++ b/pkg/analyzer_plugin/test/utilities/analyzer_converter_test.dart
@@ -199,7 +199,7 @@
}
Future<void> test_convertElement_class() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
@deprecated
abstract class _A {}
class B<K, V> {}''');
@@ -237,7 +237,7 @@
}
Future<void> test_convertElement_constructor() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
class A {
const A.myConstructor(int a, [String b]);
}''');
@@ -273,7 +273,7 @@
}
Future<void> test_convertElement_enum() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
@deprecated
enum _E1 { one, two }
enum E2 { three, four }''');
@@ -311,7 +311,7 @@
}
Future<void> test_convertElement_enumConstant() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
@deprecated
enum _E1 { one, two }
enum E2 { three, four }''');
@@ -399,7 +399,7 @@
}
Future<void> test_convertElement_field() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
class A {
static const myField = 42;
}''');
@@ -423,7 +423,7 @@
}
Future<void> test_convertElement_functionTypeAlias() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
typedef int F<T>(String x);
''');
var engineElement = findElement.typeAlias('F');
@@ -446,7 +446,7 @@
}
Future<void> test_convertElement_genericTypeAlias_function() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
typedef F<T> = int Function(String x);
''');
var engineElement = findElement.typeAlias('F');
@@ -469,7 +469,7 @@
}
Future<void> test_convertElement_getter() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
class A {
int get myGetter => 42;
}''');
@@ -492,7 +492,7 @@
}
Future<void> test_convertElement_method() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
class A {
static List<String> myMethod(int a, {String b, int c}) {
return null;
@@ -517,7 +517,7 @@
}
Future<void> test_convertElement_setter() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
class A {
set mySetter(String x) {}
}''');
@@ -590,7 +590,7 @@
}
Future<void> test_fromElement_LABEL() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
main() {
myLabel:
while (true) {
diff --git a/pkg/analyzer_plugin/test/utilities/range_factory_test.dart b/pkg/analyzer_plugin/test/utilities/range_factory_test.dart
index e157ab5..0250c35 100644
--- a/pkg/analyzer_plugin/test/utilities/range_factory_test.dart
+++ b/pkg/analyzer_plugin/test/utilities/range_factory_test.dart
@@ -32,7 +32,7 @@
}
Future<void> test_argumentRange_all_mixed_noTrailingComma() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
void f() {
g(0, 1, c: 2);
}
@@ -42,7 +42,7 @@
}
Future<void> test_argumentRange_all_mixed_trailingComma() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
void f() {
g(0, 1, c: 2, );
}
@@ -52,7 +52,7 @@
}
Future<void> test_argumentRange_all_named_noTrailingComma() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
void f() {
g(a: 0, b: 1, c: 2);
}
@@ -62,7 +62,7 @@
}
Future<void> test_argumentRange_all_named_trailingComma() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
void f() {
g(a: 0, b: 1, c: 2, );
}
@@ -72,7 +72,7 @@
}
Future<void> test_argumentRange_all_positional_noTrailingComma() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
void f() {
g(0, 1, 2);
}
@@ -82,7 +82,7 @@
}
Future<void> test_argumentRange_all_positional_trailingComma() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
void f() {
g(0, 1, 2, );
}
@@ -92,7 +92,7 @@
}
Future<void> test_argumentRange_first_noTrailingComma() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
void f() {
g(0, 1);
}
@@ -102,7 +102,7 @@
}
Future<void> test_argumentRange_first_trailingComma() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
void f() {
g(0, 1, );
}
@@ -112,7 +112,7 @@
}
Future<void> test_argumentRange_last_noTrailingComma() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
void f() {
g(0, 1);
}
@@ -122,7 +122,7 @@
}
Future<void> test_argumentRange_last_trailingComma() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
void f() {
g(0, 1, );
}
@@ -132,7 +132,7 @@
}
Future<void> test_argumentRange_middle_noTrailingComma() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
void f() {
g(0, 1, 2, 3);
}
@@ -142,7 +142,7 @@
}
Future<void> test_argumentRange_middle_trailingComma() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
void f() {
g(0, 1, 2, 3, );
}
@@ -152,7 +152,7 @@
}
Future<void> test_argumentRange_only_named() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
void f() {
g(a: 0);
}
@@ -162,7 +162,7 @@
}
Future<void> test_argumentRange_only_positional() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
void f() {
g(0);
}
@@ -172,13 +172,13 @@
}
Future<void> test_elementName() async {
- await resolveTestUnit('class ABC {}');
+ await resolveTestCode('class ABC {}');
var element = findElement.class_('ABC');
expect(range.elementName(element), SourceRange(6, 3));
}
Future<void> test_endEnd() async {
- await resolveTestUnit('main() {}');
+ await resolveTestCode('main() {}');
var mainFunction = testUnit.declarations[0] as FunctionDeclaration;
var mainName = mainFunction.name;
var mainBody = mainFunction.functionExpression.body;
@@ -186,14 +186,14 @@
}
Future<void> test_endLength() async {
- await resolveTestUnit('main() {}');
+ await resolveTestCode('main() {}');
var mainFunction = testUnit.declarations[0] as FunctionDeclaration;
var mainName = mainFunction.name;
expect(range.endLength(mainName, 3), SourceRange(4, 3));
}
Future<void> test_endStart() async {
- await resolveTestUnit('main() {}');
+ await resolveTestCode('main() {}');
var mainFunction = testUnit.declarations[0] as FunctionDeclaration;
var mainName = mainFunction.name;
var mainBody = mainFunction.functionExpression.body;
@@ -206,14 +206,14 @@
}
Future<void> test_node() async {
- await resolveTestUnit('main() {}');
+ await resolveTestCode('main() {}');
var mainFunction = testUnit.declarations[0] as FunctionDeclaration;
var mainName = mainFunction.name;
expect(range.node(mainName), SourceRange(0, 4));
}
Future<void> test_nodeInList_argumentList_first_named() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
void f() {
g(a: 1, b: 2);
}
@@ -224,7 +224,7 @@
}
Future<void> test_nodeInList_argumentList_first_positional() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
void f() {
g(1, 2);
}
@@ -235,7 +235,7 @@
}
Future<void> test_nodeInList_argumentList_last_named() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
void f() {
g(a: 1, b: 2);
}
@@ -246,7 +246,7 @@
}
Future<void> test_nodeInList_argumentList_last_positional() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
void f() {
g(1, 2);
}
@@ -257,7 +257,7 @@
}
Future<void> test_nodeInList_argumentList_middle_named() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
void f() {
g(a: 1, b: 2, c: 3);
}
@@ -268,7 +268,7 @@
}
Future<void> test_nodeInList_argumentList_middle_positional() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
void f() {
g(1, 2, 3);
}
@@ -279,7 +279,7 @@
}
Future<void> test_nodeInList_argumentList_only_named() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
void f() {
g(a: 1);
}
@@ -290,7 +290,7 @@
}
Future<void> test_nodeInList_argumentList_only_named_trailingComma() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
void f() {
g(a: 1,);
}
@@ -301,7 +301,7 @@
}
Future<void> test_nodeInList_argumentList_only_positional() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
void f() {
g(1);
}
@@ -313,7 +313,7 @@
Future<void>
test_nodeInList_argumentList_only_positional_trailingComma() async {
- await resolveTestUnit('''
+ await resolveTestCode('''
void f() {
g(1,);
}
@@ -324,7 +324,7 @@
}
Future<void> test_nodes() async {
- await resolveTestUnit(' main() {}');
+ await resolveTestCode(' main() {}');
var mainFunction = testUnit.declarations[0] as FunctionDeclaration;
var mainName = mainFunction.name;
var mainBody = mainFunction.functionExpression.body;
@@ -332,7 +332,7 @@
}
Future<void> test_nodes_empty() async {
- await resolveTestUnit('main() {}');
+ await resolveTestCode('main() {}');
expect(range.nodes([]), SourceRange(0, 0));
}
@@ -341,7 +341,7 @@
}
Future<void> test_startEnd_nodeNode() async {
- await resolveTestUnit(' main() {}');
+ await resolveTestCode(' main() {}');
var mainFunction = testUnit.declarations[0] as FunctionDeclaration;
var mainName = mainFunction.name;
var mainBody = mainFunction.functionExpression.body;
@@ -349,7 +349,7 @@
}
Future<void> test_startLength_node() async {
- await resolveTestUnit(' main() {}');
+ await resolveTestCode(' main() {}');
var mainFunction = testUnit.declarations[0] as FunctionDeclaration;
var mainName = mainFunction.name;
expect(range.startLength(mainName, 10), SourceRange(1, 10));
@@ -360,7 +360,7 @@
}
Future<void> test_startStart_nodeNode() async {
- await resolveTestUnit('main() {}');
+ await resolveTestCode('main() {}');
var mainFunction = testUnit.declarations[0] as FunctionDeclaration;
var mainName = mainFunction.name;
var mainBody = mainFunction.functionExpression.body;
@@ -368,7 +368,7 @@
}
Future<void> test_token() async {
- await resolveTestUnit(' main() {}');
+ await resolveTestCode(' main() {}');
var mainFunction = testUnit.declarations[0] as FunctionDeclaration;
var mainName = mainFunction.name;
expect(range.token(mainName.beginToken), SourceRange(1, 4));
diff --git a/pkg/compiler/test/sourcemaps/tools/translate_dart2js_stacktrace.dart b/pkg/compiler/test/sourcemaps/tools/translate_dart2js_stacktrace.dart
index c54f9a3..0451c24 100644
--- a/pkg/compiler/test/sourcemaps/tools/translate_dart2js_stacktrace.dart
+++ b/pkg/compiler/test/sourcemaps/tools/translate_dart2js_stacktrace.dart
@@ -29,7 +29,7 @@
String url = options.rest[0];
String data;
if (url.startsWith("http://") || url.startsWith("https://")) {
- data = (await http.get(url)).body;
+ data = (await http.get(Uri.parse(url))).body;
} else {
data = new File(url).readAsStringSync();
}
diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
index d1c4f9d..c7a0a91 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -6,7 +6,8 @@
import 'dart:core' hide MapEntry;
-import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart';
+import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart'
+ hide Reference; // Work around https://github.com/dart-lang/sdk/issues/44667
import 'package:_fe_analyzer_shared/src/messages/severity.dart' show Severity;
@@ -40,7 +41,8 @@
import 'package:_fe_analyzer_shared/src/util/link.dart';
-import 'package:kernel/ast.dart';
+import 'package:kernel/ast.dart'
+ hide Reference; // Work around https://github.com/dart-lang/sdk/issues/44667
import 'package:kernel/type_environment.dart';
import '../builder/builder.dart';
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
index 4c804fe..37f51ab 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
@@ -4,14 +4,16 @@
import 'dart:core' hide MapEntry;
-import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart';
+import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart'
+ hide Reference; // Work around https://github.com/dart-lang/sdk/issues/44667
import 'package:_fe_analyzer_shared/src/util/link.dart';
import 'package:front_end/src/fasta/kernel/internal_ast.dart';
import 'package:front_end/src/fasta/type_inference/type_demotion.dart';
-import 'package:kernel/ast.dart';
+import 'package:kernel/ast.dart'
+ hide Reference; // Work around https://github.com/dart-lang/sdk/issues/44667
import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
import 'package:kernel/core_types.dart' show CoreTypes;
import 'package:kernel/type_algebra.dart';
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index f719e36e..1324c7e 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -1100,6 +1100,7 @@
sticky
stmt
stopped
+storage
str
strategies
streak
@@ -1239,6 +1240,7 @@
unconditionally
unconstrained
undeclare
+undergo
undo
undoable
unequal
diff --git a/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart b/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart
index 23be8f3..4779532 100644
--- a/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart
@@ -396,16 +396,34 @@
VersionConstraint currentConstraint;
if (node.value is String) {
currentConstraint = VersionConstraint.parse(node.value as String);
- if (currentConstraint is VersionRange &&
- currentConstraint.min >= minimumVersion) {
- // The current version constraint is already up to date. Do not edit.
+ var invalidVersionMessage =
+ 'The current SDK constraint in pubspec.yaml is invalid. A '
+ 'minimum version, such as ">=2.7.0", is required when launching '
+ "'dart migrate'.";
+ if (currentConstraint is Version) {
+ // In this case, the constraint is an exact version, like 2.0.0.
+ _logger.stderr(invalidVersionMessage);
return false;
+ } else if (currentConstraint is VersionRange) {
+ if (currentConstraint.min == null) {
+ _logger.stderr(invalidVersionMessage);
+ return false;
+ } else if (currentConstraint.min >= minimumVersion) {
+ // The current version constraint is already up to date. Do not
+ // edit.
+ return false;
+ } else {
+ // TODO(srawlins): This overwrites the current maximum version. In
+ // the uncommon situation that there is a special maximum, it should
+ // not.
+ pubspec._replaceSpan(node.span, fullVersionConstraint, listener);
+ return true;
+ }
} else {
- // TODO(srawlins): This overwrites the current maximum version. In
- // the uncommon situation that there is a special maximum, it should
- // not.
- pubspec._replaceSpan(node.span, fullVersionConstraint, listener);
- return true;
+ // The constraint is something different, like a union, like
+ // '>=1.0.0 <2.0.0 >=3.0.0 <4.0.0', which is not valid.
+ _logger.stderr(invalidVersionMessage);
+ return false;
}
} else {
// Something is odd with the constraint we've found in pubspec.yaml;
diff --git a/pkg/nnbd_migration/lib/src/front_end/resources/index.html b/pkg/nnbd_migration/lib/src/front_end/resources/index.html
index f56877d..f24e6a8 100644
--- a/pkg/nnbd_migration/lib/src/front_end/resources/index.html
+++ b/pkg/nnbd_migration/lib/src/front_end/resources/index.html
@@ -26,7 +26,7 @@
<h2 class="before-apply">Proposed null safety changes</h2>
<h2 class="after-apply">✓ Null safety migration applied</h2>
<a target="_blank"
- href="https://goo.gle/dart-null-safety-migration-tool">
+ href="https://dart.dev/go/null-safety-migration">
<button class="action-button">
<i class="material-icons">launch</i>
<span>Help</span>
diff --git a/pkg/nnbd_migration/lib/src/front_end/resources/resources.g.dart b/pkg/nnbd_migration/lib/src/front_end/resources/resources.g.dart
index 6b0f34f..735dc5d 100644
--- a/pkg/nnbd_migration/lib/src/front_end/resources/resources.g.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/resources/resources.g.dart
@@ -7422,7 +7422,7 @@
''';
String _index_html;
-// index_html md5 is '4dc4d54418f267d0f9189690626e0f2a'
+// index_html md5 is 'c4127573314e7a91769cf9e0bcb489b1'
String _index_html_base64 = '''
PGh0bWw+CjxoZWFkPgogICAgPHRpdGxlPk51bGwgU2FmZXR5IFByZXZpZXc8L3RpdGxlPgogICAgPHNj
cmlwdCBzcmM9Int7IGhpZ2hsaWdodEpzUGF0aCB9fSI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0Pnt7IGRh
@@ -7443,53 +7443,53 @@
YWx0PSJEYXJ0UGFkIExvZ28iIGNsYXNzPSJsb2dvIi8+CiAgICA8aDE+RGFydDwvaDE+CiAgICA8aDIg
Y2xhc3M9ImJlZm9yZS1hcHBseSI+UHJvcG9zZWQgbnVsbCBzYWZldHkgY2hhbmdlczwvaDI+CiAgICA8
aDIgY2xhc3M9ImFmdGVyLWFwcGx5Ij4mIzEwMDAzOyBOdWxsIHNhZmV0eSBtaWdyYXRpb24gYXBwbGll
-ZDwvaDI+CiAgICA8YSB0YXJnZXQ9Il9ibGFuayIKICAgICAgIGhyZWY9Imh0dHBzOi8vZ29vLmdsZS9k
-YXJ0LW51bGwtc2FmZXR5LW1pZ3JhdGlvbi10b29sIj4KICAgICAgPGJ1dHRvbiBjbGFzcz0iYWN0aW9u
-LWJ1dHRvbiI+CiAgICAgICAgPGkgY2xhc3M9Im1hdGVyaWFsLWljb25zIj5sYXVuY2g8L2k+CiAgICAg
-ICAgPHNwYW4+SGVscDwvc3Bhbj4KICAgICAgPC9idXR0b24+CiAgICA8L2E+CiAgICA8YnV0dG9uIGNs
-YXNzPSJhY3Rpb24tYnV0dG9uIGFwcGx5LW1pZ3JhdGlvbiI+CiAgICAgICAgPGkgY2xhc3M9Im1hdGVy
-aWFsLWljb25zIj5lZGl0PC9pPgogICAgICAgIDxzcGFuIGNsYXNzPSJsYWJlbCI+CiAgICAgICAgICBB
-cHBseSBNaWdyYXRpb24KICAgICAgICA8L3NwYW4+CiAgICA8L2J1dHRvbj4KICAgIDxidXR0b24gY2xh
-c3M9ImFjdGlvbi1idXR0b24gYXBwbHktbWlncmF0aW9uIiBkaXNhYmxlZD4KICAgICAgICA8aSBjbGFz
-cz0ibWF0ZXJpYWwtaWNvbnMiPmVkaXQ8L2k+CiAgICAgICAgPHNwYW4gY2xhc3M9ImxhYmVsIj4KICAg
-ICAgICAgIEFwcGx5IE1pZ3JhdGlvbgogICAgICAgIDwvc3Bhbj4KICAgIDwvYnV0dG9uPgogICAgPGJ1
-dHRvbiBjbGFzcz0iYWN0aW9uLWJ1dHRvbiByZXJ1bi1taWdyYXRpb24gYmVmb3JlLWFwcGx5Ij4KICAg
-ICAgPHNwYW4gY2xhc3M9Im9wdGlvbmFsIj4KICAgICAgICA8aSBjbGFzcz0ibWF0ZXJpYWwtaWNvbnMi
-PnJlcGxheTwvaT4KICAgICAgICBSZXJ1biBGcm9tIFNvdXJjZXMKICAgICAgPC9zcGFuPgogICAgICA8
-c3BhbiBjbGFzcz0icmVxdWlyZWQiPgogICAgICAgIDxpIGNsYXNzPSJtYXRlcmlhbC1pY29ucyI+d2Fy
-bmluZzwvaT4KICAgICAgICBSZXJ1biBXaXRoIENoYW5nZXMKICAgICAgPC9zcGFuPgogICAgPC9idXR0
-b24+CjwvaGVhZGVyPgo8ZGl2IGNsYXNzPSJwYW5lbHMgaG9yaXpvbnRhbCI+CiAgICA8ZGl2IGNsYXNz
-PSJuYXYtcGFuZWwiPgogICAgICAgIDxkaXYgY2xhc3M9Im5hdi1pbm5lciI+CiAgICAgICAgICAgIDxk
-aXYgY2xhc3M9InBhbmVsLWhlYWRpbmciPlByb2plY3QgRmlsZXM8L2Rpdj4KICAgICAgICAgICAgPGRp
-diBjbGFzcz0ibmF2LXRyZWUiPjwvZGl2PgogICAgICAgIDwvZGl2PjwhLS0gL25hdi1pbm5lciAtLT4K
-ICAgIDwvZGl2PjwhLS0gL25hdiAtLT4KICAgIDxkaXYgY2xhc3M9ImZpbGUiPgogICAgICAgIDxkaXYg
-Y2xhc3M9InRpdGxlLWJhciI+CiAgICAgICAgICAgIDxoMyBpZD0idW5pdC1uYW1lIj4mbmJzcDs8L2gz
-PgogICAgICAgICAgICA8c3BhbiBpZD0ibWlncmF0ZS11bml0LXN0YXR1cy1pY29uLWxhYmVsIj5NaWdy
-YXRlCiAgICAgICAgICAgICAgICA8c3BhbgogICAgICAgICAgICAgICAgICAgIGNsYXNzPSJtYXRlcmlh
-bC1pY29ucyBzdGF0dXMtaWNvbiBtaWdyYXRpbmciCiAgICAgICAgICAgICAgICAgICAgaWQ9Im1pZ3Jh
-dGUtdW5pdC1zdGF0dXMtaWNvbiI+Y2hlY2tfYm94PC9zcGFuPgogICAgICAgICAgICA8L3NwYW4+CiAg
-ICAgICAgPC9kaXY+CiAgICAgICAgPGRpdiBjbGFzcz0iY29udGVudCI+CiAgICAgICAgICAgIDxkaXYg
-Y2xhc3M9InJlZ2lvbnMiPgogICAgICAgICAgICAgICAgPCEtLSBUaGUgcmVnaW9ucyBvdmVybGF5IGNv
-ZGUgY29weSBvZiB0aGUgY29udGVudCB0byBwcm92aWRlIC0tPgogICAgICAgICAgICAgICAgPCEtLSB0
-b29sdGlwcyBmb3IgbW9kaWZpZWQgcmVnaW9ucy4gLS0+CiAgICAgICAgICAgIDwvZGl2PjwhLS0gL3Jl
-Z2lvbnMgLS0+CiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImNvZGUiPgogICAgICAgICAgICAgICAgPCEt
-LSBDb21waWxhdGlvbiB1bml0IGNvbnRlbnQgaXMgd3JpdHRlbiBoZXJlLiAtLT4KICAgICAgICAgICAg
-ICAgIDxwIGNsYXNzPSJ3ZWxjb21lIj4KICAgICAgICAgICAgICAgICAgICBTZWxlY3QgYSBzb3VyY2Ug
-ZmlsZSBvbiB0aGUgbGVmdCB0byBwcmV2aWV3IHRoZSBwcm9wb3NlZCBlZGl0cy4KICAgICAgICAgICAg
-ICAgIDwvcD4KICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgPC9kaXY+CiAgICA8L2Rpdj48IS0tIC9j
-b250ZW50IC0tPgogICAgPGRpdiBjbGFzcz0iaW5mby1wYW5lbCI+CiAgICAgICAgPGRpdiBjbGFzcz0i
-ZWRpdC1saXN0Ij4KICAgICAgICAgICAgPGRpdiBjbGFzcz0icGFuZWwtaGVhZGluZyI+UHJvcG9zZWQg
-RWRpdHM8L2Rpdj4KICAgICAgICAgICAgPGRpdiBjbGFzcz0icGFuZWwtY29udGVudCI+PC9kaXY+CiAg
-ICAgICAgPC9kaXY+PCEtLSAvZWRpdC1saXN0IC0tPgogICAgICAgIDxkaXYgY2xhc3M9ImVkaXQtcGFu
-ZWwiPgogICAgICAgICAgICA8ZGl2IGNsYXNzPSJwYW5lbC1oZWFkaW5nIj5FZGl0IERldGFpbHM8L2Rp
-dj4KICAgICAgICAgICAgPGRpdiBjbGFzcz0icGFuZWwtY29udGVudCI+CiAgICAgICAgICAgICAgICA8
-cCBjbGFzcz0icGxhY2Vob2xkZXIiPlNlZSBkZXRhaWxzIGFib3V0IGEgcHJvcG9zZWQgZWRpdC48L3A+
-CiAgICAgICAgICAgIDwvZGl2PjwhLS0gL3BhbmVsLWNvbnRlbnQgLS0+CiAgICAgICAgPC9kaXY+PCEt
-LSAvZWRpdC1wYW5lbCAtLT4KICAgIDwvZGl2PjwhLS0gL2luZm8tcGFuZWwgLS0+CjwvZGl2PjwhLS0g
-L3BhbmVscyAtLT4KPGZvb3Rlcj4KICA8YnV0dG9uIGNsYXNzPSJyZXBvcnQtcHJvYmxlbSI+U2VuZCBG
-ZWVkYmFjazwvYnV0dG9uPgogICAgPHNwYW4gY2xhc3M9IndpZGUiPiA8L3NwYW4+CiAgICA8ZGl2IGNs
-YXNzPSJzZGstdmVyc2lvbiI+QmFzZWQgb24gPHNwYW4gaWQ9InNkay12ZXJzaW9uIj57eyBzZGtWZXJz
-aW9uIH19PC9zcGFuPjwvZGl2Pgo8L2Zvb3Rlcj4KPC9ib2R5Pgo8L2h0bWw+Cg==
+ZDwvaDI+CiAgICA8YSB0YXJnZXQ9Il9ibGFuayIKICAgICAgIGhyZWY9Imh0dHBzOi8vZGFydC5kZXYv
+Z28vbnVsbC1zYWZldHktbWlncmF0aW9uIj4KICAgICAgPGJ1dHRvbiBjbGFzcz0iYWN0aW9uLWJ1dHRv
+biI+CiAgICAgICAgPGkgY2xhc3M9Im1hdGVyaWFsLWljb25zIj5sYXVuY2g8L2k+CiAgICAgICAgPHNw
+YW4+SGVscDwvc3Bhbj4KICAgICAgPC9idXR0b24+CiAgICA8L2E+CiAgICA8YnV0dG9uIGNsYXNzPSJh
+Y3Rpb24tYnV0dG9uIGFwcGx5LW1pZ3JhdGlvbiI+CiAgICAgICAgPGkgY2xhc3M9Im1hdGVyaWFsLWlj
+b25zIj5lZGl0PC9pPgogICAgICAgIDxzcGFuIGNsYXNzPSJsYWJlbCI+CiAgICAgICAgICBBcHBseSBN
+aWdyYXRpb24KICAgICAgICA8L3NwYW4+CiAgICA8L2J1dHRvbj4KICAgIDxidXR0b24gY2xhc3M9ImFj
+dGlvbi1idXR0b24gYXBwbHktbWlncmF0aW9uIiBkaXNhYmxlZD4KICAgICAgICA8aSBjbGFzcz0ibWF0
+ZXJpYWwtaWNvbnMiPmVkaXQ8L2k+CiAgICAgICAgPHNwYW4gY2xhc3M9ImxhYmVsIj4KICAgICAgICAg
+IEFwcGx5IE1pZ3JhdGlvbgogICAgICAgIDwvc3Bhbj4KICAgIDwvYnV0dG9uPgogICAgPGJ1dHRvbiBj
+bGFzcz0iYWN0aW9uLWJ1dHRvbiByZXJ1bi1taWdyYXRpb24gYmVmb3JlLWFwcGx5Ij4KICAgICAgPHNw
+YW4gY2xhc3M9Im9wdGlvbmFsIj4KICAgICAgICA8aSBjbGFzcz0ibWF0ZXJpYWwtaWNvbnMiPnJlcGxh
+eTwvaT4KICAgICAgICBSZXJ1biBGcm9tIFNvdXJjZXMKICAgICAgPC9zcGFuPgogICAgICA8c3BhbiBj
+bGFzcz0icmVxdWlyZWQiPgogICAgICAgIDxpIGNsYXNzPSJtYXRlcmlhbC1pY29ucyI+d2FybmluZzwv
+aT4KICAgICAgICBSZXJ1biBXaXRoIENoYW5nZXMKICAgICAgPC9zcGFuPgogICAgPC9idXR0b24+Cjwv
+aGVhZGVyPgo8ZGl2IGNsYXNzPSJwYW5lbHMgaG9yaXpvbnRhbCI+CiAgICA8ZGl2IGNsYXNzPSJuYXYt
+cGFuZWwiPgogICAgICAgIDxkaXYgY2xhc3M9Im5hdi1pbm5lciI+CiAgICAgICAgICAgIDxkaXYgY2xh
+c3M9InBhbmVsLWhlYWRpbmciPlByb2plY3QgRmlsZXM8L2Rpdj4KICAgICAgICAgICAgPGRpdiBjbGFz
+cz0ibmF2LXRyZWUiPjwvZGl2PgogICAgICAgIDwvZGl2PjwhLS0gL25hdi1pbm5lciAtLT4KICAgIDwv
+ZGl2PjwhLS0gL25hdiAtLT4KICAgIDxkaXYgY2xhc3M9ImZpbGUiPgogICAgICAgIDxkaXYgY2xhc3M9
+InRpdGxlLWJhciI+CiAgICAgICAgICAgIDxoMyBpZD0idW5pdC1uYW1lIj4mbmJzcDs8L2gzPgogICAg
+ICAgICAgICA8c3BhbiBpZD0ibWlncmF0ZS11bml0LXN0YXR1cy1pY29uLWxhYmVsIj5NaWdyYXRlCiAg
+ICAgICAgICAgICAgICA8c3BhbgogICAgICAgICAgICAgICAgICAgIGNsYXNzPSJtYXRlcmlhbC1pY29u
+cyBzdGF0dXMtaWNvbiBtaWdyYXRpbmciCiAgICAgICAgICAgICAgICAgICAgaWQ9Im1pZ3JhdGUtdW5p
+dC1zdGF0dXMtaWNvbiI+Y2hlY2tfYm94PC9zcGFuPgogICAgICAgICAgICA8L3NwYW4+CiAgICAgICAg
+PC9kaXY+CiAgICAgICAgPGRpdiBjbGFzcz0iY29udGVudCI+CiAgICAgICAgICAgIDxkaXYgY2xhc3M9
+InJlZ2lvbnMiPgogICAgICAgICAgICAgICAgPCEtLSBUaGUgcmVnaW9ucyBvdmVybGF5IGNvZGUgY29w
+eSBvZiB0aGUgY29udGVudCB0byBwcm92aWRlIC0tPgogICAgICAgICAgICAgICAgPCEtLSB0b29sdGlw
+cyBmb3IgbW9kaWZpZWQgcmVnaW9ucy4gLS0+CiAgICAgICAgICAgIDwvZGl2PjwhLS0gL3JlZ2lvbnMg
+LS0+CiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImNvZGUiPgogICAgICAgICAgICAgICAgPCEtLSBDb21w
+aWxhdGlvbiB1bml0IGNvbnRlbnQgaXMgd3JpdHRlbiBoZXJlLiAtLT4KICAgICAgICAgICAgICAgIDxw
+IGNsYXNzPSJ3ZWxjb21lIj4KICAgICAgICAgICAgICAgICAgICBTZWxlY3QgYSBzb3VyY2UgZmlsZSBv
+biB0aGUgbGVmdCB0byBwcmV2aWV3IHRoZSBwcm9wb3NlZCBlZGl0cy4KICAgICAgICAgICAgICAgIDwv
+cD4KICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgPC9kaXY+CiAgICA8L2Rpdj48IS0tIC9jb250ZW50
+IC0tPgogICAgPGRpdiBjbGFzcz0iaW5mby1wYW5lbCI+CiAgICAgICAgPGRpdiBjbGFzcz0iZWRpdC1s
+aXN0Ij4KICAgICAgICAgICAgPGRpdiBjbGFzcz0icGFuZWwtaGVhZGluZyI+UHJvcG9zZWQgRWRpdHM8
+L2Rpdj4KICAgICAgICAgICAgPGRpdiBjbGFzcz0icGFuZWwtY29udGVudCI+PC9kaXY+CiAgICAgICAg
+PC9kaXY+PCEtLSAvZWRpdC1saXN0IC0tPgogICAgICAgIDxkaXYgY2xhc3M9ImVkaXQtcGFuZWwiPgog
+ICAgICAgICAgICA8ZGl2IGNsYXNzPSJwYW5lbC1oZWFkaW5nIj5FZGl0IERldGFpbHM8L2Rpdj4KICAg
+ICAgICAgICAgPGRpdiBjbGFzcz0icGFuZWwtY29udGVudCI+CiAgICAgICAgICAgICAgICA8cCBjbGFz
+cz0icGxhY2Vob2xkZXIiPlNlZSBkZXRhaWxzIGFib3V0IGEgcHJvcG9zZWQgZWRpdC48L3A+CiAgICAg
+ICAgICAgIDwvZGl2PjwhLS0gL3BhbmVsLWNvbnRlbnQgLS0+CiAgICAgICAgPC9kaXY+PCEtLSAvZWRp
+dC1wYW5lbCAtLT4KICAgIDwvZGl2PjwhLS0gL2luZm8tcGFuZWwgLS0+CjwvZGl2PjwhLS0gL3BhbmVs
+cyAtLT4KPGZvb3Rlcj4KICA8YnV0dG9uIGNsYXNzPSJyZXBvcnQtcHJvYmxlbSI+U2VuZCBGZWVkYmFj
+azwvYnV0dG9uPgogICAgPHNwYW4gY2xhc3M9IndpZGUiPiA8L3NwYW4+CiAgICA8ZGl2IGNsYXNzPSJz
+ZGstdmVyc2lvbiI+QmFzZWQgb24gPHNwYW4gaWQ9InNkay12ZXJzaW9uIj57eyBzZGtWZXJzaW9uIH19
+PC9zcGFuPjwvZGl2Pgo8L2Zvb3Rlcj4KPC9ib2R5Pgo8L2h0bWw+Cg==
''';
String _migration_css;
@@ -7724,7 +7724,7 @@
dEZ1bmN0aW9uTmFtZXNJZk5lY2Vzc2FyeShhKXtmdW5jdGlvbiB0KCl7fTtpZih0eXBlb2YgdC5uYW1l
PT0ic3RyaW5nIilyZXR1cm4KZm9yKHZhciBzPTA7czxhLmxlbmd0aDtzKyspe3ZhciByPWFbc10KdmFy
IHE9T2JqZWN0LmtleXMocikKZm9yKHZhciBwPTA7cDxxLmxlbmd0aDtwKyspe3ZhciBvPXFbcF0KdmFy
-IG49cltvXQppZih0eXBlb2Ygbj09J2Z1bmN0aW9uJyluLm5hbWU9b319fWZ1bmN0aW9uIGluaGVyaXQo
+IG49cltvXQppZih0eXBlb2Ygbj09ImZ1bmN0aW9uIiluLm5hbWU9b319fWZ1bmN0aW9uIGluaGVyaXQo
YSxiKXthLnByb3RvdHlwZS5jb25zdHJ1Y3Rvcj1hCmEucHJvdG90eXBlWyIkaSIrYS5uYW1lXT1hCmlm
KGIhPW51bGwpe2lmKHope2EucHJvdG90eXBlLl9fcHJvdG9fXz1iLnByb3RvdHlwZQpyZXR1cm59dmFy
IHM9T2JqZWN0LmNyZWF0ZShiLnByb3RvdHlwZSkKY29weVByb3BlcnRpZXMoYS5wcm90b3R5cGUscykK
@@ -7758,7 +7758,7 @@
S3EodGhpcyxhLGIsYyx0cnVlLGZhbHNlLGUpLnByb3RvdHlwZQpyZXR1cm4gc306dGVhck9mZkdldHRl
cihhLGIsYyxlLGYpfXZhciB4PTAKZnVuY3Rpb24gaW5zdGFsbFRlYXJPZmYoYSxiLGMsZCxlLGYsZyxo
LGksail7dmFyIHM9W10KZm9yKHZhciByPTA7cjxoLmxlbmd0aDtyKyspe3ZhciBxPWhbcl0KaWYodHlw
-ZW9mIHE9PSdzdHJpbmcnKXE9YVtxXQpxLiRjYWxsTmFtZT1nW3JdCnMucHVzaChxKX12YXIgcT1zWzBd
+ZW9mIHE9PSJzdHJpbmciKXE9YVtxXQpxLiRjYWxsTmFtZT1nW3JdCnMucHVzaChxKX12YXIgcT1zWzBd
CnEuJFI9ZQpxLiREPWYKdmFyIHA9aQppZih0eXBlb2YgcD09Im51bWJlciIpcCs9eAp2YXIgbz1oWzBd
CnEuJHN0dWJOYW1lPW8KdmFyIG49dGVhck9mZihzLGp8fDAscCxjLG8sZCkKYVtiXT1uCmlmKGMpcS4k
dGVhck9mZj1ufWZ1bmN0aW9uIGluc3RhbGxTdGF0aWNUZWFyT2ZmKGEsYixjLGQsZSxmLGcsaCl7cmV0
@@ -7916,18 +7916,18 @@
cm9wZXJ0eShzLCJtZXNzYWdlIix7Z2V0OnJ9KQpzLm5hbWU9IiJ9ZWxzZSBzLnRvU3RyaW5nPXIKcmV0
dXJuIHN9LAp4OmZ1bmN0aW9uKCl7cmV0dXJuIEouaih0aGlzLmRhcnRFeGNlcHRpb24pfSwKdjpmdW5j
dGlvbihhKXt0aHJvdyBILmIoYSl9LApsazpmdW5jdGlvbihhKXt0aHJvdyBILmIoUC5hNChhKSl9LApj
-TTpmdW5jdGlvbihhKXt2YXIgcyxyLHEscCxvLG4KYT1ILmVBKGEucmVwbGFjZShTdHJpbmcoe30pLCck
-cmVjZWl2ZXIkJykpCnM9YS5tYXRjaCgvXFxcJFthLXpBLVpdK1xcXCQvZykKaWYocz09bnVsbClzPUgu
+TTpmdW5jdGlvbihhKXt2YXIgcyxyLHEscCxvLG4KYT1ILmVBKGEucmVwbGFjZShTdHJpbmcoe30pLCIk
+cmVjZWl2ZXIkIikpCnM9YS5tYXRjaCgvXFxcJFthLXpBLVpdK1xcXCQvZykKaWYocz09bnVsbClzPUgu
Vk0oW10sdC5zKQpyPXMuaW5kZXhPZigiXFwkYXJndW1lbnRzXFwkIikKcT1zLmluZGV4T2YoIlxcJGFy
Z3VtZW50c0V4cHJcXCQiKQpwPXMuaW5kZXhPZigiXFwkZXhwclxcJCIpCm89cy5pbmRleE9mKCJcXCRt
ZXRob2RcXCQiKQpuPXMuaW5kZXhPZigiXFwkcmVjZWl2ZXJcXCQiKQpyZXR1cm4gbmV3IEguZjkoYS5y
-ZXBsYWNlKG5ldyBSZWdFeHAoJ1xcXFxcXCRhcmd1bWVudHNcXFxcXFwkJywnZycpLCcoKD86eHxbXnhd
-KSopJykucmVwbGFjZShuZXcgUmVnRXhwKCdcXFxcXFwkYXJndW1lbnRzRXhwclxcXFxcXCQnLCdnJyks
-JygoPzp4fFteeF0pKiknKS5yZXBsYWNlKG5ldyBSZWdFeHAoJ1xcXFxcXCRleHByXFxcXFxcJCcsJ2cn
-KSwnKCg/Onh8W154XSkqKScpLnJlcGxhY2UobmV3IFJlZ0V4cCgnXFxcXFxcJG1ldGhvZFxcXFxcXCQn
-LCdnJyksJygoPzp4fFteeF0pKiknKS5yZXBsYWNlKG5ldyBSZWdFeHAoJ1xcXFxcXCRyZWNlaXZlclxc
-XFxcXCQnLCdnJyksJygoPzp4fFteeF0pKiknKSxyLHEscCxvLG4pfSwKUzc6ZnVuY3Rpb24oYSl7cmV0
-dXJuIGZ1bmN0aW9uKCRleHByJCl7dmFyICRhcmd1bWVudHNFeHByJD0nJGFyZ3VtZW50cyQnCnRyeXsk
+ZXBsYWNlKG5ldyBSZWdFeHAoIlxcXFxcXCRhcmd1bWVudHNcXFxcXFwkIiwiZyIpLCIoKD86eHxbXnhd
+KSopIikucmVwbGFjZShuZXcgUmVnRXhwKCJcXFxcXFwkYXJndW1lbnRzRXhwclxcXFxcXCQiLCJnIiks
+IigoPzp4fFteeF0pKikiKS5yZXBsYWNlKG5ldyBSZWdFeHAoIlxcXFxcXCRleHByXFxcXFxcJCIsImci
+KSwiKCg/Onh8W154XSkqKSIpLnJlcGxhY2UobmV3IFJlZ0V4cCgiXFxcXFxcJG1ldGhvZFxcXFxcXCQi
+LCJnIiksIigoPzp4fFteeF0pKikiKS5yZXBsYWNlKG5ldyBSZWdFeHAoIlxcXFxcXCRyZWNlaXZlclxc
+XFxcXCQiLCJnIiksIigoPzp4fFteeF0pKikiKSxyLHEscCxvLG4pfSwKUzc6ZnVuY3Rpb24oYSl7cmV0
+dXJuIGZ1bmN0aW9uKCRleHByJCl7dmFyICRhcmd1bWVudHNFeHByJD0iJGFyZ3VtZW50cyQiCnRyeXsk
ZXhwciQuJG1ldGhvZCQoJGFyZ3VtZW50c0V4cHIkKX1jYXRjaChzKXtyZXR1cm4gcy5tZXNzYWdlfX0o
YSl9LApNajpmdW5jdGlvbihhKXtyZXR1cm4gZnVuY3Rpb24oJGV4cHIkKXt0cnl7JGV4cHIkLiRtZXRo
b2QkfWNhdGNoKHMpe3JldHVybiBzLm1lc3NhZ2V9fShhKX0sCklqOmZ1bmN0aW9uKGEsYil7cmV0dXJu
@@ -11519,10 +11519,10 @@
dXJuIEguY00oSC5TNyh7JG1ldGhvZCQ6bnVsbCwKdG9TdHJpbmc6ZnVuY3Rpb24oKXtyZXR1cm4iJHJl
Y2VpdmVyJCJ9fSkpfSkKcygkLCJSMSIsIk45IixmdW5jdGlvbigpe3JldHVybiBILmNNKEguUzcobnVs
bCkpfSkKcygkLCJmTiIsImlJIixmdW5jdGlvbigpe3JldHVybiBILmNNKGZ1bmN0aW9uKCl7dmFyICRh
-cmd1bWVudHNFeHByJD0nJGFyZ3VtZW50cyQnCnRyeXtudWxsLiRtZXRob2QkKCRhcmd1bWVudHNFeHBy
+cmd1bWVudHNFeHByJD0iJGFyZ3VtZW50cyQiCnRyeXtudWxsLiRtZXRob2QkKCRhcmd1bWVudHNFeHBy
JCl9Y2F0Y2gocSl7cmV0dXJuIHEubWVzc2FnZX19KCkpfSkKcygkLCJxaSIsIlVOIixmdW5jdGlvbigp
e3JldHVybiBILmNNKEguUzcodm9pZCAwKSl9KQpzKCQsInJaIiwiWmgiLGZ1bmN0aW9uKCl7cmV0dXJu
-IEguY00oZnVuY3Rpb24oKXt2YXIgJGFyZ3VtZW50c0V4cHIkPSckYXJndW1lbnRzJCcKdHJ5eyh2b2lk
+IEguY00oZnVuY3Rpb24oKXt2YXIgJGFyZ3VtZW50c0V4cHIkPSIkYXJndW1lbnRzJCIKdHJ5eyh2b2lk
IDApLiRtZXRob2QkKCRhcmd1bWVudHNFeHByJCl9Y2F0Y2gocSl7cmV0dXJuIHEubWVzc2FnZX19KCkp
fSkKcygkLCJrcSIsInJOIixmdW5jdGlvbigpe3JldHVybiBILmNNKEguTWoobnVsbCkpfSkKcygkLCJ0
dCIsImMzIixmdW5jdGlvbigpe3JldHVybiBILmNNKGZ1bmN0aW9uKCl7dHJ5e251bGwuJG1ldGhvZCR9
@@ -11812,7 +11812,7 @@
dXBlcmNsYXNzVGFnPSJBcnJheUJ1ZmZlclZpZXciCkguUGcuJG5hdGl2ZVN1cGVyY2xhc3NUYWc9IkFy
cmF5QnVmZmVyVmlldyJ9KSgpCmNvbnZlcnRBbGxUb0Zhc3RPYmplY3QodykKY29udmVydFRvRmFzdE9i
amVjdCgkKTsoZnVuY3Rpb24oYSl7aWYodHlwZW9mIGRvY3VtZW50PT09InVuZGVmaW5lZCIpe2EobnVs
-bCkKcmV0dXJufWlmKHR5cGVvZiBkb2N1bWVudC5jdXJyZW50U2NyaXB0IT0ndW5kZWZpbmVkJyl7YShk
+bCkKcmV0dXJufWlmKHR5cGVvZiBkb2N1bWVudC5jdXJyZW50U2NyaXB0IT0idW5kZWZpbmVkIil7YShk
b2N1bWVudC5jdXJyZW50U2NyaXB0KQpyZXR1cm59dmFyIHM9ZG9jdW1lbnQuc2NyaXB0cwpmdW5jdGlv
biBvbkxvYWQoYil7Zm9yKHZhciBxPTA7cTxzLmxlbmd0aDsrK3Epc1txXS5yZW1vdmVFdmVudExpc3Rl
bmVyKCJsb2FkIixvbkxvYWQsZmFsc2UpCmEoYi50YXJnZXQpfWZvcih2YXIgcj0wO3I8cy5sZW5ndGg7
diff --git a/pkg/nnbd_migration/test/migration_cli_test.dart b/pkg/nnbd_migration/test/migration_cli_test.dart
index f07ef19..6121240 100644
--- a/pkg/nnbd_migration/test/migration_cli_test.dart
+++ b/pkg/nnbd_migration/test/migration_cli_test.dart
@@ -297,7 +297,7 @@
}
Future assertPreviewServerResponsive(String url) async {
- var response = await httpGet(url);
+ var response = await httpGet(Uri.parse(url));
assertHttpSuccess(response);
}
@@ -372,13 +372,13 @@
/// Performs an HTTP get, verifying that the response received (if any) is
/// reasonable.
- Future<http.Response> httpGet(dynamic url, {Map<String, String> headers}) {
+ Future<http.Response> httpGet(Uri url, {Map<String, String> headers}) {
return checkHttpResponse(http.get(url, headers: headers));
}
/// Performs an HTTP post, verifying that the response received (if any) is
/// reasonable.
- Future<http.Response> httpPost(dynamic url,
+ Future<http.Response> httpPost(Uri url,
{Map<String, String> headers, dynamic body, Encoding encoding}) {
return checkHttpResponse(
http.post(url, headers: headers, body: body, encoding: encoding));
@@ -399,7 +399,7 @@
await callback(url);
});
// Server should be stopped now
- expect(httpGet(url), throwsA(anything));
+ expect(httpGet(Uri.parse(url)), throwsA(anything));
assertNormalExit(cliRunner);
}
}
@@ -1863,6 +1863,48 @@
projectDir, simpleProject(migrated: true, pubspecText: pubspecText));
}
+ test_pubspec_environment_sdk_is_exact_version() async {
+ var pubspecText = '''
+name: test
+environment:
+ sdk: '2.0.0'
+''';
+ var projectContents = simpleProject(pubspecText: pubspecText);
+ var projectDir = createProjectDir(projectContents);
+ var cliRunner = _createCli()
+ .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ await cliRunner.run();
+ // The Dart source code should still be migrated.
+ assertProjectContents(
+ projectDir,
+ simpleProject(
+ migrated: true,
+ pubspecText: pubspecText,
+ // The package config file should not have been touched.
+ packageConfigText: _getPackageConfigText(migrated: false)));
+ }
+
+ test_pubspec_environment_sdk_is_missing_min() async {
+ var pubspecText = '''
+name: test
+environment:
+ sdk: '<3.0.0'
+''';
+ var projectContents = simpleProject(pubspecText: pubspecText);
+ var projectDir = createProjectDir(projectContents);
+ var cliRunner = _createCli()
+ .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ await cliRunner.run();
+ // The Dart source code should still be migrated.
+ assertProjectContents(
+ projectDir,
+ simpleProject(
+ migrated: true,
+ pubspecText: pubspecText,
+ // The package config file should not have been touched.
+ packageConfigText: _getPackageConfigText(migrated: false)));
+ }
+
test_pubspec_environment_sdk_is_not_string() async {
var pubspecText = '''
name: test
@@ -1884,6 +1926,27 @@
packageConfigText: _getPackageConfigText(migrated: false)));
}
+ test_pubspec_environment_sdk_is_union() async {
+ var pubspecText = '''
+name: test
+environment:
+ sdk: '>=2.0.0 <2.1.0 >=2.2.0 <3.0.0'
+''';
+ var projectContents = simpleProject(pubspecText: pubspecText);
+ var projectDir = createProjectDir(projectContents);
+ var cliRunner = _createCli()
+ .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ await cliRunner.run();
+ // The Dart source code should still be migrated.
+ assertProjectContents(
+ projectDir,
+ simpleProject(
+ migrated: true,
+ pubspecText: pubspecText,
+ // The package config file should not have been touched.
+ packageConfigText: _getPackageConfigText(migrated: false)));
+ }
+
test_pubspec_has_unusual_max_sdk_constraint() async {
// No one should be using a weird max SDK constraint like this. If they are
// doing so, we'll fix it to 3.0.0.
diff --git a/samples/ffi/resource_management/pool.dart b/samples/ffi/resource_management/pool.dart
index 3bc0e2a..14e07f4 100644
--- a/samples/ffi/resource_management/pool.dart
+++ b/samples/ffi/resource_management/pool.dart
@@ -11,30 +11,47 @@
import '../calloc.dart';
-/// Keeps track of all allocated memory and frees all allocated memory on
-/// [releaseAll].
+/// An [Allocator] which frees all allocations at the same time.
///
-/// Wraps an [Allocator] to do the actual allocation and freeing.
+/// The pool allows you to allocate heap memory, but ignores calls to [free].
+/// Instead you call [releaseAll] to release all the allocations at the same
+/// time.
+///
+/// Also allows other resources to be associated with the pool, through the
+/// [using] method, to have a release function called for them when the pool is
+/// released.
+///
+/// An [Allocator] can be provided to do the actual allocation and freeing.
+/// Defaults to using [calloc].
class Pool implements Allocator {
/// The [Allocator] used for allocation and freeing.
final Allocator _wrappedAllocator;
- Pool(this._wrappedAllocator);
-
/// Native memory under management by this [Pool].
final List<Pointer<NativeType>> _managedMemoryPointers = [];
/// Callbacks for releasing native resources under management by this [Pool].
final List<Function()> _managedResourceReleaseCallbacks = [];
- /// Allocates memory on the native heap by using the allocator supplied to
- /// the constructor.
+ bool _inUse = true;
+
+ /// Creates a pool of allocations.
+ ///
+ /// The [allocator] is used to do the actual allocation and freeing of
+ /// memory. It defaults to using [calloc].
+ Pool([Allocator allocator = calloc]) : _wrappedAllocator = allocator;
+
+ /// Allocates memory and includes it in the pool.
+ ///
+ /// Uses the allocator provided to the [Pool] constructor to do the
+ /// allocation.
///
/// Throws an [ArgumentError] if the number of bytes or alignment cannot be
/// satisfied.
@override
- Pointer<T> allocate<T extends NativeType>(int numBytes, {int? alignment}) {
- final p = _wrappedAllocator.allocate<T>(numBytes, alignment: alignment);
+ Pointer<T> allocate<T extends NativeType>(int byteCount, {int? alignment}) {
+ _ensureInUse();
+ final p = _wrappedAllocator.allocate<T>(byteCount, alignment: alignment);
_managedMemoryPointers.add(p);
return p;
}
@@ -43,6 +60,8 @@
///
/// Executes [releaseCallback] on [releaseAll].
T using<T>(T resource, Function(T) releaseCallback) {
+ _ensureInUse();
+ releaseCallback = Zone.current.bindUnaryCallback(releaseCallback);
_managedResourceReleaseCallbacks.add(() => releaseCallback(resource));
return resource;
}
@@ -53,59 +72,102 @@
}
/// Releases all resources that this [Pool] manages.
- void releaseAll() {
- for (final c in _managedResourceReleaseCallbacks) {
- c();
+ ///
+ /// If [reuse] is `true`, the pool can be used again after resources
+ /// have been released. If not, the default, then the [allocate]
+ /// and [using] methods must not be called after a call to `releaseAll`.
+ void releaseAll({bool reuse = false}) {
+ if (!reuse) {
+ _inUse = false;
}
- _managedResourceReleaseCallbacks.clear();
+ while (_managedResourceReleaseCallbacks.isNotEmpty) {
+ _managedResourceReleaseCallbacks.removeLast()();
+ }
for (final p in _managedMemoryPointers) {
_wrappedAllocator.free(p);
}
_managedMemoryPointers.clear();
}
+ /// Does nothing, invoke [releaseAll] instead.
@override
- void free(Pointer<NativeType> pointer) => throw UnsupportedError(
- "Individually freeing Pool allocated memory is not allowed");
+ void free(Pointer<NativeType> pointer) {}
+
+ void _ensureInUse() {
+ if (!_inUse) {
+ throw StateError(
+ "Pool no longer in use, `releaseAll(reuse: false)` was called.");
+ }
+ }
}
-/// Creates a [Pool] to manage native resources.
+/// Runs [computation] with a new [Pool], and releases all allocations at the end.
///
-/// If the isolate is shut down, through `Isolate.kill()`, resources are _not_ cleaned up.
-R using<R>(R Function(Pool) f, [Allocator wrappedAllocator = calloc]) {
- final p = Pool(wrappedAllocator);
+/// If [R] is a [Future], all allocations are released when the future completes.
+///
+/// If the isolate is shut down, through `Isolate.kill()`, resources are _not_
+/// cleaned up.
+R using<R>(R Function(Pool) computation,
+ [Allocator wrappedAllocator = calloc]) {
+ final pool = Pool(wrappedAllocator);
+ bool isAsync = false;
try {
- return f(p);
+ final result = computation(pool);
+ if (result is Future) {
+ isAsync = true;
+ return (result.whenComplete(pool.releaseAll) as R);
+ }
+ return result;
} finally {
- p.releaseAll();
+ if (!isAsync) {
+ pool.releaseAll();
+ }
}
}
/// Creates a zoned [Pool] to manage native resources.
///
-/// Pool is availabe through [currentPool].
-///
-/// Please note that all throws are caught and packaged in [RethrownError].
+/// The pool is availabe through [zonePool].
///
/// If the isolate is shut down, through `Isolate.kill()`, resources are _not_ cleaned up.
-R usePool<R>(R Function() f, [Allocator wrappedAllocator = calloc]) {
- final p = Pool(wrappedAllocator);
+R withZonePool<R>(R Function() computation,
+ [Allocator wrappedAllocator = calloc]) {
+ final pool = Pool(wrappedAllocator);
+ var poolHolder = [pool];
+ bool isAsync = false;
try {
- return runZoned(() => f(),
- zoneValues: {#_pool: p},
- onError: (error, st) => throw RethrownError(error, st));
+ return runZoned(() {
+ final result = computation();
+ if (result is Future) {
+ isAsync = true;
+ result.whenComplete(pool.releaseAll);
+ }
+ return result;
+ }, zoneValues: {#_pool: poolHolder});
} finally {
- p.releaseAll();
+ if (!isAsync) {
+ pool.releaseAll();
+ poolHolder.remove(pool);
+ }
}
}
-/// The [Pool] in the current zone.
-Pool get currentPool => Zone.current[#_pool];
-
-class RethrownError {
- dynamic original;
- StackTrace originalStackTrace;
- RethrownError(this.original, this.originalStackTrace);
- toString() => """RethrownError(${original})
-${originalStackTrace}""";
+/// A zone-specific [Pool].
+///
+/// Asynchronous computations can share a [Pool]. Use [withZonePool] to create
+/// a new zone with a fresh [Pool], and that pool will then be released
+/// automatically when the function passed to [withZonePool] completes.
+/// All code inside that zone can use `zonePool` to access the pool.
+///
+/// The current pool must not be accessed by code which is not running inside
+/// a zone created by [withZonePool].
+Pool get zonePool {
+ final List<Pool>? poolHolder = Zone.current[#_pool];
+ if (poolHolder == null) {
+ throw StateError("Not inside a zone created by `usePool`");
+ }
+ if (!poolHolder.isEmpty) {
+ return poolHolder.single;
+ }
+ throw StateError("Pool as already been cleared with releaseAll.");
}
diff --git a/samples/ffi/resource_management/pool_sample.dart b/samples/ffi/resource_management/pool_sample.dart
index ef8b415..1f75165 100644
--- a/samples/ffi/resource_management/pool_sample.dart
+++ b/samples/ffi/resource_management/pool_sample.dart
@@ -4,6 +4,7 @@
//
// Sample illustrating resource management with an explicit pool.
+import 'dart:async';
import 'dart:ffi';
import 'package:expect/expect.dart';
@@ -12,7 +13,7 @@
import 'utf8_helpers.dart';
import '../dylib_utils.dart';
-main() {
+main() async {
final ffiTestDynamicLibrary =
dlopenPlatformSpecific("ffi_test_dynamic_library");
@@ -108,6 +109,24 @@
} on Exception catch (e) {
print("Caught exception: $e");
}
+
+ /// [using] waits with releasing its resources until after [Future]s
+ /// complete.
+ List<int> freed = [];
+ freeInt(int i) {
+ freed.add(i);
+ }
+
+ Future<int> myFutureInt = using((Pool pool) {
+ return Future.microtask(() {
+ pool.using(1, freeInt);
+ return 1;
+ });
+ });
+
+ Expect.isTrue(freed.isEmpty);
+ await myFutureInt;
+ Expect.equals(1, freed.single);
}
/// Represents some opaque resource being managed by a library.
diff --git a/samples/ffi/resource_management/pool_zoned_sample.dart b/samples/ffi/resource_management/pool_zoned_sample.dart
index 4c99fdc..b5b9eed 100644
--- a/samples/ffi/resource_management/pool_zoned_sample.dart
+++ b/samples/ffi/resource_management/pool_zoned_sample.dart
@@ -12,7 +12,7 @@
import 'utf8_helpers.dart';
import '../dylib_utils.dart';
-main() {
+main() async {
final ffiTestDynamicLibrary =
dlopenPlatformSpecific("ffi_test_dynamic_library");
@@ -20,9 +20,9 @@
Void Function(Pointer<Void>, Pointer<Void>, IntPtr),
void Function(Pointer<Void>, Pointer<Void>, int)>("MemMove");
- // To ensure resources are freed, wrap them in a [using] call.
- usePool(() {
- final p = currentPool<Int64>(2);
+ // To ensure resources are freed, wrap them in a [withZonePool] call.
+ withZonePool(() {
+ final p = zonePool<Int64>(2);
p[0] = 24;
MemMove(p.elementAt(1).cast<Void>(), p.cast<Void>(), sizeOf<Int64>());
print(p[1]);
@@ -31,24 +31,23 @@
// Resources are freed also when abnormal control flow occurs.
try {
- usePool(() {
- final p = currentPool<Int64>(2);
+ withZonePool(() {
+ final p = zonePool<Int64>(2);
p[0] = 25;
MemMove(p.elementAt(1).cast<Void>(), p.cast<Void>(), 8);
print(p[1]);
Expect.equals(25, p[1]);
throw Exception("Some random exception");
});
- } on RethrownError catch (e) {
- // Note that exceptions are wrapped when using zones.
- print("Caught exception: ${e.original}");
+ } catch (e) {
+ print("Caught exception: ${e}");
}
// In a pool multiple resources can be allocated, which will all be freed
// at the end of the scope.
- usePool(() {
- final p = currentPool<Int64>(2);
- final p2 = currentPool<Int64>(2);
+ withZonePool(() {
+ final p = zonePool<Int64>(2);
+ final p2 = zonePool<Int64>(2);
p[0] = 1;
p[1] = 2;
MemMove(p2.cast<Void>(), p.cast<Void>(), 2 * sizeOf<Int64>());
@@ -59,10 +58,10 @@
// If the resource allocation happens in a different scope, it is in the
// same zone, so it's lifetime is automatically managed by the pool.
f1() {
- return currentPool<Int64>(2);
+ return zonePool<Int64>(2);
}
- usePool(() {
+ withZonePool(() {
final p = f1();
final p2 = f1();
p[0] = 1;
@@ -73,8 +72,8 @@
});
// Using Strings.
- usePool(() {
- final p = "Hello world!".toUtf8(currentPool);
+ withZonePool(() {
+ final p = "Hello world!".toUtf8(zonePool);
print(p.contents());
});
@@ -91,24 +90,42 @@
void Function(Pointer<SomeResource>)>("ReleaseResource");
// Using an FFI call to release a resource.
- usePool(() {
- final r = currentPool.using(allocateResource(), releaseResource);
+ withZonePool(() {
+ final r = zonePool.using(allocateResource(), releaseResource);
useResource(r);
});
// Using an FFI call to release a resource with abnormal control flow.
try {
- usePool(() {
- final r = currentPool.using(allocateResource(), releaseResource);
+ withZonePool(() {
+ final r = zonePool.using(allocateResource(), releaseResource);
useResource(r);
throw Exception("Some random exception");
});
// Resource has been freed.
- } on RethrownError catch (e) {
- // Note that exceptions are wrapped when using zones.
- print("Caught exception: ${e.original}");
+ } catch (e) {
+ print("Caught exception: ${e}");
}
+
+ /// [using] waits with releasing its resources until after [Future]s
+ /// complete.
+
+ List<int> freed = [];
+ freeInt(int i) {
+ freed.add(i);
+ }
+
+ Future<int> myFutureInt = withZonePool(() {
+ return Future.microtask(() {
+ zonePool.using(1, freeInt);
+ return 1;
+ });
+ });
+
+ Expect.isTrue(freed.isEmpty);
+ await myFutureInt;
+ Expect.equals(1, freed.single);
}
/// Represents some opaque resource being managed by a library.
diff --git a/samples_2/ffi/resource_management/pool.dart b/samples_2/ffi/resource_management/pool.dart
index 477a78e..d4610b0 100644
--- a/samples_2/ffi/resource_management/pool.dart
+++ b/samples_2/ffi/resource_management/pool.dart
@@ -13,30 +13,47 @@
import '../calloc.dart';
-/// Keeps track of all allocated memory and frees all allocated memory on
-/// [releaseAll].
+/// An [Allocator] which frees all allocations at the same time.
///
-/// Wraps an [Allocator] to do the actual allocation and freeing.
+/// The pool allows you to allocate heap memory, but ignores calls to [free].
+/// Instead you call [releaseAll] to release all the allocations at the same
+/// time.
+///
+/// Also allows other resources to be associated with the pool, through the
+/// [using] method, to have a release function called for them when the pool is
+/// released.
+///
+/// An [Allocator] can be provided to do the actual allocation and freeing.
+/// Defaults to using [calloc].
class Pool implements Allocator {
/// The [Allocator] used for allocation and freeing.
final Allocator _wrappedAllocator;
- Pool(this._wrappedAllocator);
-
/// Native memory under management by this [Pool].
final List<Pointer<NativeType>> _managedMemoryPointers = [];
/// Callbacks for releasing native resources under management by this [Pool].
final List<Function()> _managedResourceReleaseCallbacks = [];
- /// Allocates memory on the native heap by using the allocator supplied to
- /// the constructor.
+ bool _inUse = true;
+
+ /// Creates a pool of allocations.
+ ///
+ /// The [allocator] is used to do the actual allocation and freeing of
+ /// memory. It defaults to using [calloc].
+ Pool([Allocator allocator = calloc]) : _wrappedAllocator = allocator;
+
+ /// Allocates memory and includes it in the pool.
+ ///
+ /// Uses the allocator provided to the [Pool] constructor to do the
+ /// allocation.
///
/// Throws an [ArgumentError] if the number of bytes or alignment cannot be
/// satisfied.
@override
- Pointer<T> allocate<T extends NativeType>(int numBytes, {int alignment}) {
- final p = _wrappedAllocator.allocate<T>(numBytes, alignment: alignment);
+ Pointer<T> allocate<T extends NativeType>(int byteCount, {int alignment}) {
+ _ensureInUse();
+ final p = _wrappedAllocator.allocate<T>(byteCount, alignment: alignment);
_managedMemoryPointers.add(p);
return p;
}
@@ -45,6 +62,8 @@
///
/// Executes [releaseCallback] on [releaseAll].
T using<T>(T resource, Function(T) releaseCallback) {
+ _ensureInUse();
+ releaseCallback = Zone.current.bindUnaryCallback(releaseCallback);
_managedResourceReleaseCallbacks.add(() => releaseCallback(resource));
return resource;
}
@@ -55,59 +74,102 @@
}
/// Releases all resources that this [Pool] manages.
- void releaseAll() {
- for (final c in _managedResourceReleaseCallbacks) {
- c();
+ ///
+ /// If [reuse] is `true`, the pool can be used again after resources
+ /// have been released. If not, the default, then the [allocate]
+ /// and [using] methods must not be called after a call to `releaseAll`.
+ void releaseAll({bool reuse = false}) {
+ if (!reuse) {
+ _inUse = false;
}
- _managedResourceReleaseCallbacks.clear();
+ while (_managedResourceReleaseCallbacks.isNotEmpty) {
+ _managedResourceReleaseCallbacks.removeLast()();
+ }
for (final p in _managedMemoryPointers) {
_wrappedAllocator.free(p);
}
_managedMemoryPointers.clear();
}
+ /// Does nothing, invoke [releaseAll] instead.
@override
- void free(Pointer<NativeType> pointer) => throw UnsupportedError(
- "Individually freeing Pool allocated memory is not allowed");
+ void free(Pointer<NativeType> pointer) {}
+
+ void _ensureInUse() {
+ if (!_inUse) {
+ throw StateError(
+ "Pool no longer in use, `releaseAll(reuse: false)` was called.");
+ }
+ }
}
-/// Creates a [Pool] to manage native resources.
+/// Runs [computation] with a new [Pool], and releases all allocations at the end.
///
-/// If the isolate is shut down, through `Isolate.kill()`, resources are _not_ cleaned up.
-R using<R>(R Function(Pool) f, [Allocator wrappedAllocator = calloc]) {
- final p = Pool(wrappedAllocator);
+/// If [R] is a [Future], all allocations are released when the future completes.
+///
+/// If the isolate is shut down, through `Isolate.kill()`, resources are _not_
+/// cleaned up.
+R using<R>(R Function(Pool) computation,
+ [Allocator wrappedAllocator = calloc]) {
+ final pool = Pool(wrappedAllocator);
+ bool isAsync = false;
try {
- return f(p);
+ final result = computation(pool);
+ if (result is Future) {
+ isAsync = true;
+ return (result.whenComplete(pool.releaseAll) as R);
+ }
+ return result;
} finally {
- p.releaseAll();
+ if (!isAsync) {
+ pool.releaseAll();
+ }
}
}
/// Creates a zoned [Pool] to manage native resources.
///
-/// Pool is availabe through [currentPool].
-///
-/// Please note that all throws are caught and packaged in [RethrownError].
+/// The pool is availabe through [zonePool].
///
/// If the isolate is shut down, through `Isolate.kill()`, resources are _not_ cleaned up.
-R usePool<R>(R Function() f, [Allocator wrappedAllocator = calloc]) {
- final p = Pool(wrappedAllocator);
+R withZonePool<R>(R Function() computation,
+ [Allocator wrappedAllocator = calloc]) {
+ final pool = Pool(wrappedAllocator);
+ var poolHolder = [pool];
+ bool isAsync = false;
try {
- return runZoned(() => f(),
- zoneValues: {#_pool: p},
- onError: (error, st) => throw RethrownError(error, st));
+ return runZoned(() {
+ final result = computation();
+ if (result is Future) {
+ isAsync = true;
+ result.whenComplete(pool.releaseAll);
+ }
+ return result;
+ }, zoneValues: {#_pool: poolHolder});
} finally {
- p.releaseAll();
+ if (!isAsync) {
+ pool.releaseAll();
+ poolHolder.remove(pool);
+ }
}
}
-/// The [Pool] in the current zone.
-Pool get currentPool => Zone.current[#_pool];
-
-class RethrownError {
- dynamic original;
- StackTrace originalStackTrace;
- RethrownError(this.original, this.originalStackTrace);
- toString() => """RethrownError(${original})
-${originalStackTrace}""";
+/// A zone-specific [Pool].
+///
+/// Asynchronous computations can share a [Pool]. Use [withZonePool] to create
+/// a new zone with a fresh [Pool], and that pool will then be released
+/// automatically when the function passed to [withZonePool] completes.
+/// All code inside that zone can use `zonePool` to access the pool.
+///
+/// The current pool must not be accessed by code which is not running inside
+/// a zone created by [withZonePool].
+Pool get zonePool {
+ final List<Pool> poolHolder = Zone.current[#_pool];
+ if (poolHolder == null) {
+ throw StateError("Not inside a zone created by `usePool`");
+ }
+ if (!poolHolder.isEmpty) {
+ return poolHolder.single;
+ }
+ throw StateError("Pool as already been cleared with releaseAll.");
}
diff --git a/samples_2/ffi/resource_management/pool_sample.dart b/samples_2/ffi/resource_management/pool_sample.dart
index c794a40..e3441c1 100644
--- a/samples_2/ffi/resource_management/pool_sample.dart
+++ b/samples_2/ffi/resource_management/pool_sample.dart
@@ -14,7 +14,7 @@
import 'utf8_helpers.dart';
import '../dylib_utils.dart';
-main() {
+main() async {
final ffiTestDynamicLibrary =
dlopenPlatformSpecific("ffi_test_dynamic_library");
@@ -110,6 +110,24 @@
} on Exception catch (e) {
print("Caught exception: $e");
}
+
+ /// [using] waits with releasing its resources until after [Future]s
+ /// complete.
+ List<int> freed = [];
+ freeInt(int i) {
+ freed.add(i);
+ }
+
+ Future<int> myFutureInt = using((Pool pool) {
+ return Future.microtask(() {
+ pool.using(1, freeInt);
+ return 1;
+ });
+ });
+
+ Expect.isTrue(freed.isEmpty);
+ await myFutureInt;
+ Expect.equals(1, freed.single);
}
/// Represents some opaque resource being managed by a library.
diff --git a/samples_2/ffi/resource_management/pool_zoned_sample.dart b/samples_2/ffi/resource_management/pool_zoned_sample.dart
index a1dc9c1..b9142c2 100644
--- a/samples_2/ffi/resource_management/pool_zoned_sample.dart
+++ b/samples_2/ffi/resource_management/pool_zoned_sample.dart
@@ -14,7 +14,7 @@
import 'utf8_helpers.dart';
import '../dylib_utils.dart';
-main() {
+main() async {
final ffiTestDynamicLibrary =
dlopenPlatformSpecific("ffi_test_dynamic_library");
@@ -22,9 +22,9 @@
Void Function(Pointer<Void>, Pointer<Void>, IntPtr),
void Function(Pointer<Void>, Pointer<Void>, int)>("MemMove");
- // To ensure resources are freed, wrap them in a [using] call.
- usePool(() {
- final p = currentPool<Int64>(2);
+ // To ensure resources are freed, wrap them in a [withZonePool] call.
+ withZonePool(() {
+ final p = zonePool<Int64>(2);
p[0] = 24;
MemMove(p.elementAt(1).cast<Void>(), p.cast<Void>(), sizeOf<Int64>());
print(p[1]);
@@ -33,24 +33,23 @@
// Resources are freed also when abnormal control flow occurs.
try {
- usePool(() {
- final p = currentPool<Int64>(2);
+ withZonePool(() {
+ final p = zonePool<Int64>(2);
p[0] = 25;
MemMove(p.elementAt(1).cast<Void>(), p.cast<Void>(), 8);
print(p[1]);
Expect.equals(25, p[1]);
throw Exception("Some random exception");
});
- } on RethrownError catch (e) {
- // Note that exceptions are wrapped when using zones.
- print("Caught exception: ${e.original}");
+ } catch (e) {
+ print("Caught exception: ${e}");
}
// In a pool multiple resources can be allocated, which will all be freed
// at the end of the scope.
- usePool(() {
- final p = currentPool<Int64>(2);
- final p2 = currentPool<Int64>(2);
+ withZonePool(() {
+ final p = zonePool<Int64>(2);
+ final p2 = zonePool<Int64>(2);
p[0] = 1;
p[1] = 2;
MemMove(p2.cast<Void>(), p.cast<Void>(), 2 * sizeOf<Int64>());
@@ -61,10 +60,10 @@
// If the resource allocation happens in a different scope, it is in the
// same zone, so it's lifetime is automatically managed by the pool.
f1() {
- return currentPool<Int64>(2);
+ return zonePool<Int64>(2);
}
- usePool(() {
+ withZonePool(() {
final p = f1();
final p2 = f1();
p[0] = 1;
@@ -75,8 +74,8 @@
});
// Using Strings.
- usePool(() {
- final p = "Hello world!".toUtf8(currentPool);
+ withZonePool(() {
+ final p = "Hello world!".toUtf8(zonePool);
print(p.contents());
});
@@ -93,24 +92,42 @@
void Function(Pointer<SomeResource>)>("ReleaseResource");
// Using an FFI call to release a resource.
- usePool(() {
- final r = currentPool.using(allocateResource(), releaseResource);
+ withZonePool(() {
+ final r = zonePool.using(allocateResource(), releaseResource);
useResource(r);
});
// Using an FFI call to release a resource with abnormal control flow.
try {
- usePool(() {
- final r = currentPool.using(allocateResource(), releaseResource);
+ withZonePool(() {
+ final r = zonePool.using(allocateResource(), releaseResource);
useResource(r);
throw Exception("Some random exception");
});
// Resource has been freed.
- } on RethrownError catch (e) {
- // Note that exceptions are wrapped when using zones.
- print("Caught exception: ${e.original}");
+ } catch (e) {
+ print("Caught exception: ${e}");
}
+
+ /// [using] waits with releasing its resources until after [Future]s
+ /// complete.
+
+ List<int> freed = [];
+ freeInt(int i) {
+ freed.add(i);
+ }
+
+ Future<int> myFutureInt = withZonePool(() {
+ return Future.microtask(() {
+ zonePool.using(1, freeInt);
+ return 1;
+ });
+ });
+
+ Expect.isTrue(freed.isEmpty);
+ await myFutureInt;
+ Expect.equals(1, freed.single);
}
/// Represents some opaque resource being managed by a library.
diff --git a/tests/language/nnbd/flow_analysis/expr_property_not_promoted_test.dart b/tests/language/nnbd/flow_analysis/expr_property_not_promoted_test.dart
new file mode 100644
index 0000000..bfbfc95
--- /dev/null
+++ b/tests/language/nnbd/flow_analysis/expr_property_not_promoted_test.dart
@@ -0,0 +1,38 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import '../../static_type_helper.dart';
+
+// This test verifies that neither an `== null` nor an `is` test can promote the
+// type of a property access on an arbitrary expression. (Such accesses cannot
+// be promoted soundly).
+
+class _C {
+ final int? _f;
+
+ _C(this._f);
+}
+
+void equality(_C Function() c) {
+ if (c()._f == null) {
+ c()._f.expectStaticType<Exactly<int?>>();
+ } else {
+ c()._f.expectStaticType<Exactly<int?>>();
+ }
+}
+
+void is_(_C Function() c) {
+ if (c()._f is int) {
+ c()._f.expectStaticType<Exactly<int?>>();
+ } else {
+ c()._f.expectStaticType<Exactly<int?>>();
+ }
+}
+
+main() {
+ equality(() => _C(1));
+ is_(() => _C(1));
+ equality(() => _C(null));
+ is_(() => _C(null));
+}
diff --git a/tests/language/nnbd/flow_analysis/this_not_promoted_test.dart b/tests/language/nnbd/flow_analysis/this_not_promoted_test.dart
new file mode 100644
index 0000000..ec329b4
--- /dev/null
+++ b/tests/language/nnbd/flow_analysis/this_not_promoted_test.dart
@@ -0,0 +1,110 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import '../../static_type_helper.dart';
+
+// This test verifies that neither an `== null` nor an `is` test can promote the
+// type of `this`. (In principle, we could soundly do so, but we have decided
+// not to do so at this time).
+
+class C {
+ void equality() {
+ if (this == null) {
+ this.expectStaticType<Exactly<C>>();
+ } else {
+ this.expectStaticType<Exactly<C>>();
+ }
+ }
+
+ void isSameType() {
+ if (this is C) {
+ this.expectStaticType<Exactly<C>>();
+ } else {
+ this.expectStaticType<Exactly<C>>();
+ }
+ }
+
+ void isSubtype() {
+ if (this is D) {
+ this.expectStaticType<Exactly<C>>();
+ } else {
+ this.expectStaticType<Exactly<C>>();
+ }
+ }
+}
+
+class D extends C {}
+
+class E {}
+
+class F extends E {}
+
+extension on E {
+ void equality() {
+ if (this == null) {
+ this.expectStaticType<Exactly<E>>();
+ } else {
+ this.expectStaticType<Exactly<E>>();
+ }
+ }
+
+ void isSameType() {
+ if (this is E) {
+ this.expectStaticType<Exactly<E>>();
+ } else {
+ this.expectStaticType<Exactly<E>>();
+ }
+ }
+
+ void isSubtype() {
+ if (this is F) {
+ this.expectStaticType<Exactly<E>>();
+ } else {
+ this.expectStaticType<Exactly<E>>();
+ }
+ }
+}
+
+class G {}
+
+extension on G? {
+ void equality() {
+ if (this == null) {
+ this.expectStaticType<Exactly<G?>>();
+ } else {
+ this.expectStaticType<Exactly<G?>>();
+ }
+ }
+
+ void isSameType() {
+ if (this is G?) {
+ this.expectStaticType<Exactly<G?>>();
+ } else {
+ this.expectStaticType<Exactly<G?>>();
+ }
+ }
+
+ void isSubtype() {
+ if (this is G) {
+ this.expectStaticType<Exactly<G?>>();
+ } else {
+ this.expectStaticType<Exactly<G?>>();
+ }
+ }
+}
+
+main() {
+ C().equality();
+ C().isSameType();
+ C().isSubtype();
+ E().equality();
+ E().isSameType();
+ E().isSubtype();
+ G().equality();
+ G().isSameType();
+ G().isSubtype();
+ (null as G?).equality();
+ (null as G?).isSameType();
+ (null as G?).isSubtype();
+}
diff --git a/tests/language/nnbd/flow_analysis/this_property_not_promoted_test.dart b/tests/language/nnbd/flow_analysis/this_property_not_promoted_test.dart
new file mode 100644
index 0000000..375221b
--- /dev/null
+++ b/tests/language/nnbd/flow_analysis/this_property_not_promoted_test.dart
@@ -0,0 +1,106 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import '../../static_type_helper.dart';
+
+// This test verifies that neither an `== null` nor an `is` test can promote the
+// type of a property access on `this`. (In principle, we could soundly promote
+// some such accesses, but we have decided not to do so at this time).
+
+class _C {
+ final int? _f;
+
+ _C(this._f);
+
+ void equality_implicitThis() {
+ if (_f == null) {
+ _f.expectStaticType<Exactly<int?>>();
+ } else {
+ _f.expectStaticType<Exactly<int?>>();
+ }
+ }
+
+ void equality_explicitThis() {
+ if (this._f == null) {
+ this._f.expectStaticType<Exactly<int?>>();
+ } else {
+ this._f.expectStaticType<Exactly<int?>>();
+ }
+ }
+
+ void is_implicitThis() {
+ if (_f is int) {
+ _f.expectStaticType<Exactly<int?>>();
+ } else {
+ _f.expectStaticType<Exactly<int?>>();
+ }
+ }
+
+ void is_explicitThis() {
+ if (this._f is int) {
+ this._f.expectStaticType<Exactly<int?>>();
+ } else {
+ this._f.expectStaticType<Exactly<int?>>();
+ }
+ }
+}
+
+class _D {
+ final int? _f;
+
+ _D(this._f);
+}
+
+extension on _D {
+ void equality_implicitThis() {
+ if (_f == null) {
+ _f.expectStaticType<Exactly<int?>>();
+ } else {
+ _f.expectStaticType<Exactly<int?>>();
+ }
+ }
+
+ void equality_explicitThis() {
+ if (this._f == null) {
+ this._f.expectStaticType<Exactly<int?>>();
+ } else {
+ this._f.expectStaticType<Exactly<int?>>();
+ }
+ }
+
+ void is_implicitThis() {
+ if (_f is int) {
+ _f.expectStaticType<Exactly<int?>>();
+ } else {
+ _f.expectStaticType<Exactly<int?>>();
+ }
+ }
+
+ void is_explicitThis() {
+ if (this._f is int) {
+ this._f.expectStaticType<Exactly<int?>>();
+ } else {
+ this._f.expectStaticType<Exactly<int?>>();
+ }
+ }
+}
+
+main() {
+ _C(1).equality_implicitThis();
+ _C(1).equality_explicitThis();
+ _C(1).is_implicitThis();
+ _C(1).is_explicitThis();
+ _C(null).equality_implicitThis();
+ _C(null).equality_explicitThis();
+ _C(null).is_implicitThis();
+ _C(null).is_explicitThis();
+ _D(1).equality_implicitThis();
+ _D(1).equality_explicitThis();
+ _D(1).is_implicitThis();
+ _D(1).is_explicitThis();
+ _D(null).equality_implicitThis();
+ _D(null).equality_explicitThis();
+ _D(null).is_implicitThis();
+ _D(null).is_explicitThis();
+}
diff --git a/tests/language/nnbd/flow_analysis/unreachable_via_expr_property_test.dart b/tests/language/nnbd/flow_analysis/unreachable_via_expr_property_test.dart
new file mode 100644
index 0000000..a6e9676
--- /dev/null
+++ b/tests/language/nnbd/flow_analysis/unreachable_via_expr_property_test.dart
@@ -0,0 +1,163 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// 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.
+
+/// This test verifies that `is` and `==` tests performed on a property get of
+/// an arbitrary expression do not lead to code being considered unreachable.
+/// (In principle, we could soundly mark some such code as unreachable, but we
+/// have decided not to do so at this time).
+///
+/// Exception: when the static type of the property access is guaranteed to be
+/// Null, and we are performing an `== null` test, then we do mark the non-null
+/// branch as unreachable.
+
+import '../../static_type_helper.dart';
+
+class C {
+ Null get nullProperty => null;
+ Object? get objectQProperty => null;
+}
+
+void equalitySimple(int? x, int? y, C Function() f) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (f().nullProperty == null) {
+ x = null;
+ } else {
+ y = null;
+ }
+ // Since the assignment to x was reachable, it should have static type
+ // `int?` now. But y should still have static type `int`.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int>>();
+}
+
+void equalityWithBogusPromotion(int? x, int? y, C Function() f) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (f().objectQProperty is Null) {
+ if (f().objectQProperty == null) {
+ x = null;
+ } else {
+ y = null;
+ }
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+}
+
+void isSimple(int? x, int? y, C Function() f) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (f().nullProperty is Never) {
+ x = null;
+ } else {
+ y = null;
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+}
+
+void isWithBogusPromotion(int? x, int? y, C Function() f) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (f().objectQProperty is Null) {
+ if (f().objectQProperty is Never) {
+ x = null;
+ } else {
+ y = null;
+ }
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+}
+
+class _C {
+ final Null _nullField = null;
+ final Object? _objectQField = null;
+}
+
+void equalitySimplePrivate(int? x, int? y, _C Function() f) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (f()._nullField == null) {
+ x = null;
+ } else {
+ y = null;
+ }
+ // Since the assignment to x was reachable, it should have static type
+ // `int?` now. But y should still have static type `int`.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int>>();
+}
+
+void equalityWithBogusPromotionPrivate(int? x, int? y, _C Function() f) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (f()._objectQField is Null) {
+ if (f()._objectQField == null) {
+ x = null;
+ } else {
+ y = null;
+ }
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+}
+
+void isSimplePrivate(int? x, int? y, _C Function() f) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (f()._nullField is Never) {
+ x = null;
+ } else {
+ y = null;
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+}
+
+void isWithBogusPromotionPrivate(int? x, int? y, _C Function() f) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (f()._objectQField is Null) {
+ if (f()._objectQField is Never) {
+ x = null;
+ } else {
+ y = null;
+ }
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+}
+
+main() {
+ equalitySimple(1, 1, () => C());
+ equalityWithBogusPromotion(1, 1, () => C());
+ isSimple(1, 1, () => C());
+ isWithBogusPromotion(1, 1, () => C());
+ equalitySimplePrivate(1, 1, () => _C());
+ equalityWithBogusPromotionPrivate(1, 1, () => _C());
+ isSimplePrivate(1, 1, () => _C());
+ isWithBogusPromotionPrivate(1, 1, () => _C());
+}
diff --git a/tests/language/nnbd/flow_analysis/unreachable_via_this_property_test.dart b/tests/language/nnbd/flow_analysis/unreachable_via_this_property_test.dart
new file mode 100644
index 0000000..ced63a5
--- /dev/null
+++ b/tests/language/nnbd/flow_analysis/unreachable_via_this_property_test.dart
@@ -0,0 +1,583 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// 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.
+
+/// This test verifies that `is` and `==` tests performed on a property get of
+/// `this` do not lead to code being considered unreachable. (In principle, we
+/// could soundly mark some such code as unreachable, but we have decided not to
+/// do so at this time).
+///
+/// Exception: when the static type of the property access is guaranteed to be
+/// Null, and we are performing an `== null` test, then we do mark the non-null
+/// branch as unreachable.
+
+import '../../static_type_helper.dart';
+
+class B {
+ Null get nullProperty => null;
+ Object? get objectQProperty => null;
+}
+
+class C extends B {
+ void equalitySimple_implicitThis(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (nullProperty == null) {
+ x = null;
+ } else {
+ y = null;
+ }
+ // Since the assignment to x was reachable, it should have static type
+ // `int?` now. But y should still have static type `int`.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int>>();
+ }
+
+ void equalitySimple_explicitThis(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (this.nullProperty == null) {
+ x = null;
+ } else {
+ y = null;
+ }
+ // Since the assignment to x was reachable, it should have static type
+ // `int?` now. But y should still have static type `int`.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int>>();
+ }
+
+ void equalityWithBogusPromotion_implicitThis(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (objectQProperty is Null) {
+ if (objectQProperty == null) {
+ x = null;
+ } else {
+ y = null;
+ }
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+
+ void equalityWithBogusPromotion_explicitThis(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (this.objectQProperty is Null) {
+ if (this.objectQProperty == null) {
+ x = null;
+ } else {
+ y = null;
+ }
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+
+ void isSimple_implicitThis(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (nullProperty is Never) {
+ x = null;
+ } else {
+ y = null;
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+
+ void isSimple_explicitThis(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (this.nullProperty is Never) {
+ x = null;
+ } else {
+ y = null;
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+
+ void isWithBogusPromotion_implicitThis(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (objectQProperty is Null) {
+ if (objectQProperty is Never) {
+ x = null;
+ } else {
+ y = null;
+ }
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+
+ void isWithBogusPromotion_explicitThis(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (this.objectQProperty is Null) {
+ if (this.objectQProperty is Never) {
+ x = null;
+ } else {
+ y = null;
+ }
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+}
+
+class D extends B {}
+
+extension on D {
+ void equalitySimple_implicitThis(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (nullProperty == null) {
+ x = null;
+ } else {
+ y = null;
+ }
+ // Since the assignment to x was reachable, it should have static type
+ // `int?` now. But y should still have static type `int`.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int>>();
+ }
+
+ void equalitySimple_explicitThis(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (this.nullProperty == null) {
+ x = null;
+ } else {
+ y = null;
+ }
+ // Since the assignment to x was reachable, it should have static type
+ // `int?` now. But y should still have static type `int`.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int>>();
+ }
+
+ void equalityWithBogusPromotion_implicitThis(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (objectQProperty is Null) {
+ if (objectQProperty == null) {
+ x = null;
+ } else {
+ y = null;
+ }
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+
+ void equalityWithBogusPromotion_explicitThis(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (this.objectQProperty is Null) {
+ if (this.objectQProperty == null) {
+ x = null;
+ } else {
+ y = null;
+ }
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+
+ void isSimple_implicitThis(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (nullProperty is Never) {
+ x = null;
+ } else {
+ y = null;
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+
+ void isSimple_explicitThis(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (this.nullProperty is Never) {
+ x = null;
+ } else {
+ y = null;
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+
+ void isWithBogusPromotion_implicitThis(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (objectQProperty is Null) {
+ if (objectQProperty is Never) {
+ x = null;
+ } else {
+ y = null;
+ }
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+
+ void isWithBogusPromotion_explicitThis(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (this.objectQProperty is Null) {
+ if (this.objectQProperty is Never) {
+ x = null;
+ } else {
+ y = null;
+ }
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+}
+
+class _B {
+ final Null _nullField = null;
+ final Object? _objectQField = null;
+}
+
+class _C extends _B {
+ void equalitySimple_implicitThis(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (_nullField == null) {
+ x = null;
+ } else {
+ y = null;
+ }
+ // Since the assignment to x was reachable, it should have static type
+ // `int?` now. But y should still have static type `int`.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int>>();
+ }
+
+ void equalitySimple_explicitThis(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (this._nullField == null) {
+ x = null;
+ } else {
+ y = null;
+ }
+ // Since the assignment to x was reachable, it should have static type
+ // `int?` now. But y should still have static type `int`.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int>>();
+ }
+
+ void equalityWithBogusPromotion_implicitThis(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (_objectQField is Null) {
+ if (_objectQField == null) {
+ x = null;
+ } else {
+ y = null;
+ }
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+
+ void equalityWithBogusPromotion_explicitThis(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (this._objectQField is Null) {
+ if (this._objectQField == null) {
+ x = null;
+ } else {
+ y = null;
+ }
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+
+ void isSimple_implicitThis(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (_nullField is Never) {
+ x = null;
+ } else {
+ y = null;
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+
+ void isSimple_explicitThis(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (this._nullField is Never) {
+ x = null;
+ } else {
+ y = null;
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+
+ void isWithBogusPromotion_implicitThis(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (_objectQField is Null) {
+ if (_objectQField is Never) {
+ x = null;
+ } else {
+ y = null;
+ }
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+
+ void isWithBogusPromotion_explicitThis(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (this._objectQField is Null) {
+ if (this._objectQField is Never) {
+ x = null;
+ } else {
+ y = null;
+ }
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+}
+
+class _D extends _B {}
+
+extension on _D {
+ void equalitySimple_implicitThis(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (_nullField == null) {
+ x = null;
+ } else {
+ y = null;
+ }
+ // Since the assignment to x was reachable, it should have static type
+ // `int?` now. But y should still have static type `int`.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int>>();
+ }
+
+ void equalitySimple_explicitThis(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (this._nullField == null) {
+ x = null;
+ } else {
+ y = null;
+ }
+ // Since the assignment to x was reachable, it should have static type
+ // `int?` now. But y should still have static type `int`.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int>>();
+ }
+
+ void equalityWithBogusPromotion_implicitThis(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (_objectQField is Null) {
+ if (_objectQField == null) {
+ x = null;
+ } else {
+ y = null;
+ }
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+
+ void equalityWithBogusPromotion_explicitThis(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (this._objectQField is Null) {
+ if (this._objectQField == null) {
+ x = null;
+ } else {
+ y = null;
+ }
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+
+ void isSimple_implicitThis(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (_nullField is Never) {
+ x = null;
+ } else {
+ y = null;
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+
+ void isSimple_explicitThis(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (this._nullField is Never) {
+ x = null;
+ } else {
+ y = null;
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+
+ void isWithBogusPromotion_implicitThis(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (_objectQField is Null) {
+ if (_objectQField is Never) {
+ x = null;
+ } else {
+ y = null;
+ }
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+
+ void isWithBogusPromotion_explicitThis(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (this._objectQField is Null) {
+ if (this._objectQField is Never) {
+ x = null;
+ } else {
+ y = null;
+ }
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+}
+
+main() {
+ C().equalitySimple_implicitThis(1, 1);
+ C().equalitySimple_explicitThis(1, 1);
+ C().equalityWithBogusPromotion_implicitThis(1, 1);
+ C().equalityWithBogusPromotion_explicitThis(1, 1);
+ C().isSimple_implicitThis(1, 1);
+ C().isSimple_explicitThis(1, 1);
+ C().isWithBogusPromotion_implicitThis(1, 1);
+ C().isWithBogusPromotion_explicitThis(1, 1);
+ D().equalitySimple_implicitThis(1, 1);
+ D().equalitySimple_explicitThis(1, 1);
+ D().equalityWithBogusPromotion_implicitThis(1, 1);
+ D().equalityWithBogusPromotion_explicitThis(1, 1);
+ D().isSimple_implicitThis(1, 1);
+ D().isSimple_explicitThis(1, 1);
+ D().isWithBogusPromotion_implicitThis(1, 1);
+ D().isWithBogusPromotion_explicitThis(1, 1);
+ _C().equalitySimple_implicitThis(1, 1);
+ _C().equalitySimple_explicitThis(1, 1);
+ _C().equalityWithBogusPromotion_implicitThis(1, 1);
+ _C().equalityWithBogusPromotion_explicitThis(1, 1);
+ _C().isSimple_implicitThis(1, 1);
+ _C().isSimple_explicitThis(1, 1);
+ _C().isWithBogusPromotion_implicitThis(1, 1);
+ _C().isWithBogusPromotion_explicitThis(1, 1);
+ _D().equalitySimple_implicitThis(1, 1);
+ _D().equalitySimple_explicitThis(1, 1);
+ _D().equalityWithBogusPromotion_implicitThis(1, 1);
+ _D().equalityWithBogusPromotion_explicitThis(1, 1);
+ _D().isSimple_implicitThis(1, 1);
+ _D().isSimple_explicitThis(1, 1);
+ _D().isWithBogusPromotion_implicitThis(1, 1);
+ _D().isWithBogusPromotion_explicitThis(1, 1);
+}
diff --git a/tests/language/nnbd/flow_analysis/unreachable_via_this_test.dart b/tests/language/nnbd/flow_analysis/unreachable_via_this_test.dart
new file mode 100644
index 0000000..cc478bf
--- /dev/null
+++ b/tests/language/nnbd/flow_analysis/unreachable_via_this_test.dart
@@ -0,0 +1,231 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// 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.
+
+/// This test verifies that `is` and `==` tests performed on `this` do not lead
+/// to code being considered unreachable. (In principle, we could soundly mark
+/// some such code as unreachable, but we have decided not to do so at this
+/// time).
+
+import '../../static_type_helper.dart';
+
+class C {
+ void equalitySimple(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (this == null) {
+ x = null;
+ } else {
+ y = null;
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+
+ void equalityWithBogusPromotion(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (this is Null) {
+ if (this == null) {
+ x = null;
+ } else {
+ y = null;
+ }
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+
+ void isSimple(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (this is Never) {
+ x = null;
+ } else {
+ y = null;
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+
+ void isWithBogusPromotion(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (this is Null) {
+ if (this is Never) {
+ x = null;
+ } else {
+ y = null;
+ }
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+}
+
+class D {}
+
+extension on D {
+ void equalitySimple(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (this == null) {
+ x = null;
+ } else {
+ y = null;
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+
+ void equalityWithBogusPromotion(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (this is Null) {
+ if (this == null) {
+ x = null;
+ } else {
+ y = null;
+ }
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+
+ void isSimple(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (this is Never) {
+ x = null;
+ } else {
+ y = null;
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+
+ void isWithBogusPromotion(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (this is Null) {
+ if (this is Never) {
+ x = null;
+ } else {
+ y = null;
+ }
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+}
+
+class E {}
+
+extension on E? {
+ void equalitySimple(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (this == null) {
+ x = null;
+ } else {
+ y = null;
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+
+ void equalityWithBogusPromotion(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (this is Null) {
+ if (this == null) {
+ x = null;
+ } else {
+ y = null;
+ }
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+
+ void isSimple(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (this is Never) {
+ x = null;
+ } else {
+ y = null;
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+
+ void isWithBogusPromotion(int? x, int? y) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (this is Null) {
+ if (this is Never) {
+ x = null;
+ } else {
+ y = null;
+ }
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+ }
+}
+
+main() {
+ C().equalitySimple(1, 1);
+ C().equalityWithBogusPromotion(1, 1);
+ C().isSimple(1, 1);
+ C().isWithBogusPromotion(1, 1);
+ D().equalitySimple(1, 1);
+ D().equalityWithBogusPromotion(1, 1);
+ D().isSimple(1, 1);
+ D().isWithBogusPromotion(1, 1);
+ E().equalitySimple(1, 1);
+ E().equalityWithBogusPromotion(1, 1);
+ E().isSimple(1, 1);
+ E().isWithBogusPromotion(1, 1);
+ (null as E?).equalitySimple(1, 1);
+ (null as E?).equalityWithBogusPromotion(1, 1);
+ (null as E?).isSimple(1, 1);
+ (null as E?).isWithBogusPromotion(1, 1);
+}
diff --git a/tests/language/nnbd/flow_analysis/unreachable_via_variable_property_test.dart b/tests/language/nnbd/flow_analysis/unreachable_via_variable_property_test.dart
new file mode 100644
index 0000000..2268dc5
--- /dev/null
+++ b/tests/language/nnbd/flow_analysis/unreachable_via_variable_property_test.dart
@@ -0,0 +1,163 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// 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.
+
+/// This test verifies that `is` and `==` tests performed on a property get of a
+/// variable do not lead to code being considered unreachable. (In principle,
+/// we could soundly mark some such code as unreachable, but we have decided not
+/// to do so at this time).
+///
+/// Exception: when the static type of the property access is guaranteed to be
+/// Null, and we are performing an `== null` test, then we do mark the non-null
+/// branch as unreachable.
+
+import '../../static_type_helper.dart';
+
+class C {
+ Null get nullProperty => null;
+ Object? get objectQProperty => null;
+}
+
+void equalitySimple(int? x, int? y, C c) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (c.nullProperty == null) {
+ x = null;
+ } else {
+ y = null;
+ }
+ // Since the assignment to x was reachable, it should have static type
+ // `int?` now. But y should still have static type `int`.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int>>();
+}
+
+void equalityWithBogusPromotion(int? x, int? y, C c) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (c.objectQProperty is Null) {
+ if (c.objectQProperty == null) {
+ x = null;
+ } else {
+ y = null;
+ }
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+}
+
+void isSimple(int? x, int? y, C c) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (c.nullProperty is Never) {
+ x = null;
+ } else {
+ y = null;
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+}
+
+void isWithBogusPromotion(int? x, int? y, C c) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (c.objectQProperty is Null) {
+ if (c.objectQProperty is Never) {
+ x = null;
+ } else {
+ y = null;
+ }
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+}
+
+class _C {
+ final Null _nullField = null;
+ final Object? _objectQField = null;
+}
+
+void equalitySimplePrivate(int? x, int? y, _C c) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (c._nullField == null) {
+ x = null;
+ } else {
+ y = null;
+ }
+ // Since the assignment to x was reachable, it should have static type
+ // `int?` now. But y should still have static type `int`.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int>>();
+}
+
+void equalityWithBogusPromotionPrivate(int? x, int? y, _C c) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (c._objectQField is Null) {
+ if (c._objectQField == null) {
+ x = null;
+ } else {
+ y = null;
+ }
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+}
+
+void isSimplePrivate(int? x, int? y, _C c) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (c._nullField is Never) {
+ x = null;
+ } else {
+ y = null;
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+}
+
+void isWithBogusPromotionPrivate(int? x, int? y, _C c) {
+ if (x == null || y == null) return;
+ x.expectStaticType<Exactly<int>>();
+ y.expectStaticType<Exactly<int>>();
+ if (c._objectQField is Null) {
+ if (c._objectQField is Never) {
+ x = null;
+ } else {
+ y = null;
+ }
+ }
+ // Since the assignments to x and y were both reachable, they should have
+ // static type `int?` now.
+ x.expectStaticType<Exactly<int?>>();
+ y.expectStaticType<Exactly<int?>>();
+}
+
+main() {
+ equalitySimple(1, 1, C());
+ equalityWithBogusPromotion(1, 1, C());
+ isSimple(1, 1, C());
+ isWithBogusPromotion(1, 1, C());
+ equalitySimplePrivate(1, 1, _C());
+ equalityWithBogusPromotionPrivate(1, 1, _C());
+ isSimplePrivate(1, 1, _C());
+ isWithBogusPromotionPrivate(1, 1, _C());
+}
diff --git a/tests/language/nnbd/flow_analysis/variable_property_not_promoted_test.dart b/tests/language/nnbd/flow_analysis/variable_property_not_promoted_test.dart
new file mode 100644
index 0000000..6aa9d0c
--- /dev/null
+++ b/tests/language/nnbd/flow_analysis/variable_property_not_promoted_test.dart
@@ -0,0 +1,38 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import '../../static_type_helper.dart';
+
+// This test verifies that neither an `== null` nor an `is` test can promote the
+// type of a property access on a variable. (In principle, we could soundly
+// promote some such accesses, but we have decided not to do so at this time).
+
+class _C {
+ final int? _f;
+
+ _C(this._f);
+}
+
+void equality(_C c) {
+ if (c._f == null) {
+ c._f.expectStaticType<Exactly<int?>>();
+ } else {
+ c._f.expectStaticType<Exactly<int?>>();
+ }
+}
+
+void is_(_C c) {
+ if (c._f is int) {
+ c._f.expectStaticType<Exactly<int?>>();
+ } else {
+ c._f.expectStaticType<Exactly<int?>>();
+ }
+}
+
+main() {
+ equality(_C(1));
+ is_(_C(1));
+ equality(_C(null));
+ is_(_C(null));
+}
diff --git a/tests/standalone/io/shared_socket_test.dart b/tests/standalone/io/shared_socket_test.dart
index 2fb755f..3195b63 100644
--- a/tests/standalone/io/shared_socket_test.dart
+++ b/tests/standalone/io/shared_socket_test.dart
@@ -82,7 +82,7 @@
}
}
-Future<String> get(String url) async {
+Future<String> get(Uri url) async {
while (true) {
try {
await http.get(url);
@@ -96,7 +96,7 @@
final futures = <Future>[];
final numAtOnce = 16; // enough to keep the server busy
for (int i = 0; i < numAtOnce; ++i) {
- futures.add(get('http://localhost:$port').then((_) {}));
+ futures.add(get(Uri.http('localhost:$port', '')).then((_) {}));
}
await Future.wait(futures);
}
diff --git a/tests/standalone_2/io/shared_socket_test.dart b/tests/standalone_2/io/shared_socket_test.dart
index bd1a041..2324884a1 100644
--- a/tests/standalone_2/io/shared_socket_test.dart
+++ b/tests/standalone_2/io/shared_socket_test.dart
@@ -84,7 +84,7 @@
}
}
-Future<String> get(String url) async {
+Future<String> get(Uri url) async {
while (true) {
try {
await http.get(url);
@@ -98,7 +98,7 @@
final futures = <Future>[];
final numAtOnce = 16; // enough to keep the server busy
for (int i = 0; i < numAtOnce; ++i) {
- futures.add(get('http://localhost:$port').then((_) {}));
+ futures.add(get(Uri.http('localhost:$port', '')).then((_) {}));
}
await Future.wait(futures);
}
diff --git a/tools/VERSION b/tools/VERSION
index 61c91e3..cdb060a 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 12
PATCH 0
-PRERELEASE 231
+PRERELEASE 232
PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/bots/get_builder_status.dart b/tools/bots/get_builder_status.dart
index e058e42..d66167f 100755
--- a/tools/bots/get_builder_status.dart
+++ b/tools/bots/get_builder_status.dart
@@ -22,10 +22,10 @@
/*late*/ bool useStagingDatabase;
-String get queryUrl {
- var project = useStagingDatabase ? "dart-ci-staging" : "dart-ci";
- return 'https://firestore.googleapis.com/v1/'
- 'projects/$project/databases/(default)/documents:runQuery';
+Uri get _queryUrl {
+ var project = useStagingDatabase ? 'dart-ci-staging' : 'dart-ci';
+ return Uri.https('firestore.googleapis.com',
+ '/v1/projects/$project/databases/(default)/documents:runQuery');
}
/*late*/ String builder;
@@ -226,7 +226,7 @@
'Accept': 'application/json',
'Content-Type': 'application/json'
};
- return client.post(queryUrl, headers: headers, body: query);
+ return client.post(_queryUrl, headers: headers, body: query);
}
String buildQuery() => jsonEncode({
diff --git a/tools/bots/post_results_to_pubsub.dart b/tools/bots/post_results_to_pubsub.dart
index 0ad3df2..1134188 100644
--- a/tools/bots/post_results_to_pubsub.dart
+++ b/tools/bots/post_results_to_pubsub.dart
@@ -30,10 +30,8 @@
const resultsPerMessage = 100;
-String getPostUrl(String project) {
- return 'https://pubsub.googleapis.com/v1/projects/$project'
- '/topics/results:publish';
-}
+Uri _postUrl(String project) => Uri.https(
+ 'pubsub.googleapis.com', 'v1/projects/$project/topics/results:publish');
main(List<String> args) async {
final parser = new ArgParser();
@@ -114,7 +112,7 @@
]
});
final headers = {'Authorization': 'Bearer $token'};
- final postUrl = getPostUrl(project);
+ final postUrl = _postUrl(project);
final response =
await client.post(postUrl, headers: headers, body: jsonMessage);