Version 2.10.0-29.0.dev
Merge commit 'a904d72a02c10d9c52ac0868a8ec5c79993ef9dd' into 'dev'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4aa79ce..2ac349f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -38,6 +38,14 @@
* Updated `unnecessary_statements` to skip `as` expressions.
* Fixed `prefer_relative_imports` to work with path dependencies.
+#### Pub
+
+* `pub run` and `pub global run` accepts a `--(no-)-sound-null-safety` flag,
+ that is passed to the VM.
+* Fix: Avoid multiple recompilation of binaries in global packages.
+* Fix: Avoid exponential behaviour of error reporting from the solver.
+* Fix: Refresh binstubs after recompile in global run.
+
## 2.9.1 - 2020-08-12
This is a patch release that fixes unhandled exceptions in some Flutter
@@ -163,8 +171,6 @@
### Pub
* `pub run` and `pub global run` accepts a `--enable-experiment` flag enabling
experiments in the Dart VM (and language).
-* `pub run` and `pub global run` accepts a `--(no-)-sound-null-safety flag, that
- is passed to the VM.
* Warn when publishing the first null-safe version of a package.
* `pub outdated`:
* If the current version of a dependency is a prerelease
@@ -184,9 +190,6 @@
* Fix git folder names in cache, allowing for ssh-style git
dependencies.
* Fix: Avoid precompilation of dependencies of global packages.
-* Fix: Avoid multiple recompilation of binaries in global packages.
-* Fix: Avoid exponential behaviour of error reporting from the solver.
-* Fix: Refresh binstubs after recompile in global run.
## 2.8.4 - 2020-06-04
diff --git a/DEPS b/DEPS
index 984e608..6edb3b0 100644
--- a/DEPS
+++ b/DEPS
@@ -44,11 +44,11 @@
# co19 is a cipd package. Use update.sh in tests/co19[_2] to update these
# hashes. It requires access to the dart-build-access group, which EngProd
# has.
- "co19_rev": "74eec903ea06fa09dc8799b0552d55b581a82996",
+ "co19_rev": "12eb7bb3dd199c817babce7eb65266b90eb8deca",
"co19_2_rev": "e48b3090826cf40b8037648f19d211e8eab1b4b6",
# The internal benchmarks to use. See go/dart-benchmarks-internal
- "benchmarks_internal_rev": "760986cf29594acf424ee6e8af23a4322825935a",
+ "benchmarks_internal_rev": "01b76c69ca61d3184d4896d230c5bfd439b84d27",
"checkout_benchmarks_internal": False,
# As Flutter does, we use Fuchsia's GN and Clang toolchain. These revision
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 82c40dc..8c4699a 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
@@ -363,11 +363,12 @@
/// Call this method just after visiting a binary `==` or `!=` expression.
void equalityOp_end(Expression wholeExpression, Expression rightOperand,
+ Type rightOperandType,
{bool notEqual = false});
/// Call this method just after visiting the left hand side of a binary `==`
/// or `!=` expression.
- void equalityOp_rightBegin(Expression leftOperand);
+ void equalityOp_rightBegin(Expression leftOperand, Type leftOperandType);
/// This method should be called at the conclusion of flow analysis for a top
/// level function or method. Performs assertion checks.
@@ -811,17 +812,20 @@
@override
void equalityOp_end(Expression wholeExpression, Expression rightOperand,
+ Type rightOperandType,
{bool notEqual = false}) {
_wrap(
- 'equalityOp_end($wholeExpression, $rightOperand, notEqual: $notEqual)',
- () => _wrapped.equalityOp_end(wholeExpression, rightOperand,
+ 'equalityOp_end($wholeExpression, $rightOperand, $rightOperandType, '
+ 'notEqual: $notEqual)',
+ () => _wrapped.equalityOp_end(
+ wholeExpression, rightOperand, rightOperandType,
notEqual: notEqual));
}
@override
- void equalityOp_rightBegin(Expression leftOperand) {
- _wrap('equalityOp_rightBegin($leftOperand)',
- () => _wrapped.equalityOp_rightBegin(leftOperand));
+ void equalityOp_rightBegin(Expression leftOperand, Type leftOperandType) {
+ _wrap('equalityOp_rightBegin($leftOperand, $leftOperandType)',
+ () => _wrapped.equalityOp_rightBegin(leftOperand, leftOperandType));
}
@override
@@ -1651,8 +1655,26 @@
}
}
+/// Enum representing the different classifications of types that can be
+/// returned by [TypeOperations.classifyType].
+enum TypeClassification {
+ /// The type is `Null` or an equivalent type (e.g. `Never?`)
+ nullOrEquivalent,
+
+ /// The type is a potentially nullable type, but not equivalent to `Null`
+ /// (e.g. `int?`, or a type variable whose bound is potentially nullable)
+ potentiallyNullable,
+
+ /// The type is a non-nullable type.
+ nonNullable,
+}
+
/// Operations on types, abstracted from concrete type interfaces.
abstract class TypeOperations<Variable, Type> {
+ /// Classifies the given type into one of the three categories defined by
+ /// the [TypeClassification] enum.
+ TypeClassification classifyType(Type type);
+
/// Returns the "remainder" of [from] when [what] has been removed from
/// consideration by an instance check.
Type factor(Type from, Type what);
@@ -2277,6 +2299,21 @@
'thenInfo: $_thenInfo)';
}
+/// [_FlowContext] representing an equality comparison using `==` or `!=`.
+class _EqualityOpContext<Variable, Type> extends _BranchContext {
+ /// The type of the expression on the LHS of `==` or `!=`.
+ final Type _leftOperandType;
+
+ _EqualityOpContext(
+ ExpressionInfo<Variable, Type> conditionInfo, this._leftOperandType)
+ : super(conditionInfo);
+
+ @override
+ String toString() =>
+ '_EqualityOpContext(conditionInfo: $_conditionInfo, lhsType: '
+ '$_leftOperandType)';
+}
+
class _FlowAnalysisImpl<Node, Statement extends Node, Expression, Variable,
Type> implements FlowAnalysis<Node, Statement, Expression, Variable, Type> {
/// The [TypeOperations], used to access types, and check subtyping.
@@ -2420,31 +2457,49 @@
@override
void equalityOp_end(Expression wholeExpression, Expression rightOperand,
+ Type rightOperandType,
{bool notEqual = false}) {
- _BranchContext<Variable, Type> context =
- _stack.removeLast() as _BranchContext<Variable, Type>;
+ _EqualityOpContext<Variable, Type> context =
+ _stack.removeLast() as _EqualityOpContext<Variable, Type>;
ExpressionInfo<Variable, Type> lhsInfo = context._conditionInfo;
+ Type leftOperandType = context._leftOperandType;
ExpressionInfo<Variable, Type> rhsInfo = _getExpressionInfo(rightOperand);
- Variable variable;
- if (lhsInfo is _NullInfo<Variable, Type> &&
+ ExpressionInfo<Variable, Type> equalityInfo;
+ TypeClassification leftOperandTypeClassification =
+ typeOperations.classifyType(leftOperandType);
+ TypeClassification rightOperandTypeClassification =
+ typeOperations.classifyType(rightOperandType);
+ if (leftOperandTypeClassification == TypeClassification.nullOrEquivalent &&
+ rightOperandTypeClassification == TypeClassification.nullOrEquivalent) {
+ return booleanLiteral(wholeExpression, !notEqual);
+ } else if ((leftOperandTypeClassification ==
+ TypeClassification.nullOrEquivalent &&
+ rightOperandTypeClassification == TypeClassification.nonNullable) ||
+ (rightOperandTypeClassification ==
+ TypeClassification.nullOrEquivalent &&
+ leftOperandTypeClassification == TypeClassification.nonNullable)) {
+ return booleanLiteral(wholeExpression, notEqual);
+ } else if (lhsInfo is _NullInfo<Variable, Type> &&
rhsInfo is _VariableReadInfo<Variable, Type>) {
- variable = rhsInfo._variable;
+ assert(
+ leftOperandTypeClassification == TypeClassification.nullOrEquivalent);
+ equalityInfo =
+ _current.tryMarkNonNullable(typeOperations, rhsInfo._variable);
} else if (rhsInfo is _NullInfo<Variable, Type> &&
lhsInfo is _VariableReadInfo<Variable, Type>) {
- variable = lhsInfo._variable;
+ equalityInfo =
+ _current.tryMarkNonNullable(typeOperations, lhsInfo._variable);
} else {
return;
}
- ExpressionInfo<Variable, Type> expressionInfo =
- _current.tryMarkNonNullable(typeOperations, variable);
_storeExpressionInfo(wholeExpression,
- notEqual ? expressionInfo : ExpressionInfo.invert(expressionInfo));
+ notEqual ? equalityInfo : ExpressionInfo.invert(equalityInfo));
}
@override
- void equalityOp_rightBegin(Expression leftOperand) {
- _stack.add(
- new _BranchContext<Variable, Type>(_getExpressionInfo(leftOperand)));
+ void equalityOp_rightBegin(Expression leftOperand, Type leftOperandType) {
+ _stack.add(new _EqualityOpContext<Variable, Type>(
+ _getExpressionInfo(leftOperand), leftOperandType));
}
@override
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/definite_unassignment/data/binary_expression.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/definite_unassignment/data/binary_expression.dart
index f666e45..ec3bf0e 100644
--- a/pkg/_fe_analyzer_shared/test/flow_analysis/definite_unassignment/data/binary_expression.dart
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/definite_unassignment/data/binary_expression.dart
@@ -8,7 +8,7 @@
v;
}
-ifNull_right(int a) {
+ifNull_right(int? a) {
late int v;
a ?? (v = 0);
v;
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 a3c666f..09e6023 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
@@ -33,7 +33,7 @@
var x = h.addVar('x', 'int?');
h.run((flow) {
flow.assert_begin();
- var expr = h.eqNull(x)();
+ var expr = h.eqNull(x, _Type('int?'))();
flow.assert_afterCondition(expr);
expect(flow.promotedType(x).type, 'int');
flow.assert_end();
@@ -55,7 +55,8 @@
flow.assert_begin();
flow.write(x, _Type('int?'));
flow.write(z, _Type('int?'));
- var expr = h.and(h.notNull(x), h.notNull(y))();
+ var expr =
+ h.and(h.notNull(x, _Type('int?')), h.notNull(y, _Type('int?')))();
flow.assert_afterCondition(expr);
flow.assert_end();
// x should be promoted because it was promoted before the assert, and
@@ -75,7 +76,7 @@
var x = h.addVar('x', 'int?');
h.run((flow) {
h.declare(x, initialized: true);
- flow.conditional_thenBegin(h.notNull(x)());
+ flow.conditional_thenBegin(h.notNull(x, _Type('int?'))());
expect(flow.promotedType(x).type, 'int');
flow.conditional_elseBegin(_Expression());
expect(flow.promotedType(x), isNull);
@@ -89,7 +90,7 @@
var x = h.addVar('x', 'int?');
h.run((flow) {
h.declare(x, initialized: true);
- flow.conditional_thenBegin(h.eqNull(x)());
+ flow.conditional_thenBegin(h.eqNull(x, _Type('int?'))());
expect(flow.promotedType(x), isNull);
flow.conditional_elseBegin(_Expression());
expect(flow.promotedType(x).type, 'int');
@@ -134,8 +135,12 @@
h.declare(y, initialized: true);
h.declare(z, initialized: true);
h.if_(
- h.conditional(h.expr, h.and(h.notNull(x), h.notNull(y)),
- h.and(h.notNull(x), h.notNull(z))), () {
+ h.conditional(
+ h.expr,
+ h.and(h.notNull(x, _Type('int?')), h.notNull(y, _Type('int?'))),
+ h.and(
+ h.notNull(x, _Type('int?')), h.notNull(z, _Type('int?')))),
+ () {
expect(flow.promotedType(x).type, 'int');
expect(flow.promotedType(y), isNull);
expect(flow.promotedType(z), isNull);
@@ -157,8 +162,10 @@
h.declare(y, initialized: true);
h.declare(z, initialized: true);
h.ifElse(
- h.conditional(h.expr, h.or(h.eqNull(x), h.eqNull(y)),
- h.or(h.eqNull(x), h.eqNull(z))),
+ h.conditional(
+ h.expr,
+ h.or(h.eqNull(x, _Type('int?')), h.eqNull(y, _Type('int?'))),
+ h.or(h.eqNull(x, _Type('int?')), h.eqNull(z, _Type('int?')))),
() {}, () {
expect(flow.promotedType(x).type, 'int');
expect(flow.promotedType(y), isNull);
@@ -174,11 +181,11 @@
h.declare(x, initialized: true);
var varExpr = _Expression();
flow.variableRead(varExpr, x);
- flow.equalityOp_rightBegin(varExpr);
+ flow.equalityOp_rightBegin(varExpr, _Type('int?'));
var nullExpr = _Expression();
flow.nullLiteral(nullExpr);
var expr = _Expression();
- flow.equalityOp_end(expr, nullExpr, notEqual: true);
+ flow.equalityOp_end(expr, nullExpr, _Type('Null'), notEqual: true);
flow.ifStatement_thenBegin(expr);
expect(flow.promotedType(x).type, 'int');
flow.ifStatement_elseBegin();
@@ -187,6 +194,25 @@
});
});
+ test('equalityOp(x != <null expr>) does not promote', () {
+ var h = _Harness();
+ var x = h.addVar('x', 'int?');
+ h.run((flow) {
+ h.declare(x, initialized: true);
+ var varExpr = _Expression();
+ flow.variableRead(varExpr, x);
+ flow.equalityOp_rightBegin(varExpr, _Type('int?'));
+ var nullExpr = _Expression();
+ var expr = _Expression();
+ flow.equalityOp_end(expr, nullExpr, _Type('Null'), notEqual: true);
+ flow.ifStatement_thenBegin(expr);
+ expect(flow.promotedType(x), isNull);
+ flow.ifStatement_elseBegin();
+ expect(flow.promotedType(x), isNull);
+ flow.ifStatement_end(true);
+ });
+ });
+
test('equalityOp(x == null) promotes false branch', () {
var h = _Harness();
var x = h.addVar('x', 'int?');
@@ -194,11 +220,11 @@
h.declare(x, initialized: true);
var varExpr = _Expression();
flow.variableRead(varExpr, x);
- flow.equalityOp_rightBegin(varExpr);
+ flow.equalityOp_rightBegin(varExpr, _Type('int?'));
var nullExpr = _Expression();
flow.nullLiteral(nullExpr);
var expr = _Expression();
- flow.equalityOp_end(expr, nullExpr, notEqual: false);
+ flow.equalityOp_end(expr, nullExpr, _Type('Null'), notEqual: false);
flow.ifStatement_thenBegin(expr);
expect(flow.promotedType(x), isNull);
flow.ifStatement_elseBegin();
@@ -214,11 +240,11 @@
h.declare(x, initialized: true);
var nullExpr = _Expression();
flow.nullLiteral(nullExpr);
- flow.equalityOp_rightBegin(nullExpr);
+ flow.equalityOp_rightBegin(nullExpr, _Type('Null'));
var varExpr = _Expression();
flow.variableRead(varExpr, x);
var expr = _Expression();
- flow.equalityOp_end(expr, varExpr, notEqual: true);
+ flow.equalityOp_end(expr, varExpr, _Type('int?'), notEqual: true);
flow.ifStatement_thenBegin(expr);
expect(flow.promotedType(x).type, 'int');
flow.ifStatement_elseBegin();
@@ -227,6 +253,25 @@
});
});
+ test('equalityOp(<null expr> != x) does not promote', () {
+ var h = _Harness();
+ var x = h.addVar('x', 'int?');
+ h.run((flow) {
+ h.declare(x, initialized: true);
+ var nullExpr = _Expression();
+ flow.equalityOp_rightBegin(nullExpr, _Type('Null'));
+ var varExpr = _Expression();
+ flow.variableRead(varExpr, x);
+ var expr = _Expression();
+ flow.equalityOp_end(expr, varExpr, _Type('int?'), notEqual: true);
+ flow.ifStatement_thenBegin(expr);
+ expect(flow.promotedType(x), isNull);
+ flow.ifStatement_elseBegin();
+ expect(flow.promotedType(x), isNull);
+ flow.ifStatement_end(true);
+ });
+ });
+
test('equalityOp(null == x) promotes false branch', () {
var h = _Harness();
var x = h.addVar('x', 'int?');
@@ -234,11 +279,11 @@
h.declare(x, initialized: true);
var nullExpr = _Expression();
flow.nullLiteral(nullExpr);
- flow.equalityOp_rightBegin(nullExpr);
+ flow.equalityOp_rightBegin(nullExpr, _Type('Null'));
var varExpr = _Expression();
flow.variableRead(varExpr, x);
var expr = _Expression();
- flow.equalityOp_end(expr, varExpr, notEqual: false);
+ flow.equalityOp_end(expr, varExpr, _Type('int?'), notEqual: false);
flow.ifStatement_thenBegin(expr);
expect(flow.promotedType(x), isNull);
flow.ifStatement_elseBegin();
@@ -247,6 +292,102 @@
});
});
+ test('equalityOp(null == null) equivalent to true', () {
+ var h = _Harness();
+ h.run((flow) {
+ var null1 = _Expression();
+ flow.equalityOp_rightBegin(null1, _Type('Null'));
+ var null2 = _Expression();
+ var expr = _Expression();
+ flow.equalityOp_end(expr, null2, _Type('Null'));
+ flow.ifStatement_thenBegin(expr);
+ expect(flow.isReachable, true);
+ flow.ifStatement_elseBegin();
+ expect(flow.isReachable, false);
+ flow.ifStatement_end(true);
+ });
+ });
+
+ test('equalityOp(null != null) equivalent to false', () {
+ var h = _Harness();
+ h.run((flow) {
+ var null1 = _Expression();
+ flow.equalityOp_rightBegin(null1, _Type('Null'));
+ var null2 = _Expression();
+ var expr = _Expression();
+ flow.equalityOp_end(expr, null2, _Type('Null'), notEqual: true);
+ flow.ifStatement_thenBegin(expr);
+ expect(flow.isReachable, false);
+ flow.ifStatement_elseBegin();
+ expect(flow.isReachable, true);
+ flow.ifStatement_end(true);
+ });
+ });
+
+ test('equalityOp(null == non-null) equivalent to false', () {
+ var h = _Harness();
+ h.run((flow) {
+ var null1 = _Expression();
+ flow.equalityOp_rightBegin(null1, _Type('Null'));
+ var null2 = _Expression();
+ var expr = _Expression();
+ flow.equalityOp_end(expr, null2, _Type('int'));
+ flow.ifStatement_thenBegin(expr);
+ expect(flow.isReachable, false);
+ flow.ifStatement_elseBegin();
+ expect(flow.isReachable, true);
+ flow.ifStatement_end(true);
+ });
+ });
+
+ test('equalityOp(null != non-null) equivalent to true', () {
+ var h = _Harness();
+ h.run((flow) {
+ var null1 = _Expression();
+ flow.equalityOp_rightBegin(null1, _Type('Null'));
+ var null2 = _Expression();
+ var expr = _Expression();
+ flow.equalityOp_end(expr, null2, _Type('int'), notEqual: true);
+ flow.ifStatement_thenBegin(expr);
+ expect(flow.isReachable, true);
+ flow.ifStatement_elseBegin();
+ expect(flow.isReachable, false);
+ flow.ifStatement_end(true);
+ });
+ });
+
+ test('equalityOp(non-null == null) equivalent to false', () {
+ var h = _Harness();
+ h.run((flow) {
+ var null1 = _Expression();
+ flow.equalityOp_rightBegin(null1, _Type('int'));
+ var null2 = _Expression();
+ var expr = _Expression();
+ flow.equalityOp_end(expr, null2, _Type('Null'));
+ flow.ifStatement_thenBegin(expr);
+ expect(flow.isReachable, false);
+ flow.ifStatement_elseBegin();
+ expect(flow.isReachable, true);
+ flow.ifStatement_end(true);
+ });
+ });
+
+ test('equalityOp(non-null != null) equivalent to true', () {
+ var h = _Harness();
+ h.run((flow) {
+ var null1 = _Expression();
+ flow.equalityOp_rightBegin(null1, _Type('int'));
+ var null2 = _Expression();
+ var expr = _Expression();
+ flow.equalityOp_end(expr, null2, _Type('Null'), notEqual: true);
+ flow.ifStatement_thenBegin(expr);
+ expect(flow.isReachable, true);
+ flow.ifStatement_elseBegin();
+ expect(flow.isReachable, false);
+ flow.ifStatement_end(true);
+ });
+ });
+
test('conditionEqNull() does not promote write-captured vars', () {
var h = _Harness();
var x = h.addVar('x', 'int?');
@@ -255,13 +396,13 @@
(vars) => vars.function(functionNode, () => vars.write(x)));
h.run((flow) {
h.declare(x, initialized: true);
- h.if_(h.notNull(x), () {
+ h.if_(h.notNull(x, _Type('int?')), () {
expect(flow.promotedType(x).type, 'int');
});
h.function(functionNode, () {
flow.write(x, _Type('int?'));
});
- h.if_(h.notNull(x), () {
+ h.if_(h.notNull(x, _Type('int?')), () {
expect(flow.promotedType(x), isNull);
});
});
@@ -314,7 +455,7 @@
h.run((flow) {
h.declare(x, initialized: true);
flow.doStatement_bodyBegin(stmt);
- h.if_(h.notNull(x), () {
+ h.if_(h.notNull(x, _Type('int?')), () {
flow.handleContinue(stmt);
});
flow.handleExit();
@@ -337,7 +478,7 @@
flow.doStatement_bodyBegin(stmt);
flow.doStatement_conditionBegin();
expect(flow.promotedType(x), isNull);
- flow.doStatement_end(h.eqNull(x)());
+ flow.doStatement_end(h.eqNull(x, _Type('int?'))());
expect(flow.promotedType(x).type, 'int');
});
});
@@ -432,7 +573,7 @@
h.run((flow) {
h.declare(x, initialized: true);
flow.for_conditionBegin(stmt);
- flow.for_bodyBegin(stmt, h.notNull(x)());
+ flow.for_bodyBegin(stmt, h.notNull(x, _Type('int?'))());
expect(flow.promotedType(x).type, 'int');
flow.for_updaterBegin();
flow.for_end();
@@ -448,7 +589,7 @@
h.run((flow) {
h.declare(x, initialized: true);
flow.for_conditionBegin(node);
- flow.for_bodyBegin(null, h.notNull(x)());
+ flow.for_bodyBegin(null, h.notNull(x, _Type('int?'))());
flow.for_updaterBegin();
flow.for_end();
});
@@ -500,7 +641,8 @@
h.declare(y, initialized: true);
h.declare(z, initialized: true);
flow.for_conditionBegin(stmt);
- flow.for_bodyBegin(stmt, h.or(h.eqNull(x), h.eqNull(z))());
+ flow.for_bodyBegin(stmt,
+ h.or(h.eqNull(x, _Type('int?')), h.eqNull(z, _Type('int?')))());
h.if_(h.expr, () {
h.promote(x, 'int');
h.promote(y, 'int');
@@ -782,7 +924,7 @@
var x = h.addVar('x', 'int?');
h.run((flow) {
h.declare(x, initialized: true);
- flow.ifStatement_thenBegin(h.eqNull(x)());
+ flow.ifStatement_thenBegin(h.eqNull(x, _Type('int?'))());
flow.handleExit();
flow.ifStatement_end(false);
expect(flow.promotedType(x).type, 'int');
@@ -904,7 +1046,8 @@
var x = h.addVar('x', 'int?');
h.run((flow) {
h.declare(x, initialized: true);
- flow.logicalBinaryOp_rightBegin(h.notNull(x)(), isAnd: true);
+ flow.logicalBinaryOp_rightBegin(h.notNull(x, _Type('int?'))(),
+ isAnd: true);
expect(flow.promotedType(x).type, 'int');
flow.logicalBinaryOp_end(_Expression(), _Expression(), isAnd: true);
});
@@ -917,7 +1060,8 @@
h.declare(x, initialized: true);
flow.logicalBinaryOp_rightBegin(_Expression(), isAnd: true);
var wholeExpr = _Expression();
- flow.logicalBinaryOp_end(wholeExpr, h.notNull(x)(), isAnd: true);
+ flow.logicalBinaryOp_end(wholeExpr, h.notNull(x, _Type('int?'))(),
+ isAnd: true);
flow.ifStatement_thenBegin(wholeExpr);
expect(flow.promotedType(x).type, 'int');
flow.ifStatement_end(false);
@@ -932,7 +1076,8 @@
h.declare(x, initialized: true);
flow.logicalBinaryOp_rightBegin(_Expression(), isAnd: false);
var wholeExpr = _Expression();
- flow.logicalBinaryOp_end(wholeExpr, h.eqNull(x)(), isAnd: false);
+ flow.logicalBinaryOp_end(wholeExpr, h.eqNull(x, _Type('int?'))(),
+ isAnd: false);
flow.ifStatement_thenBegin(wholeExpr);
flow.ifStatement_elseBegin();
expect(flow.promotedType(x).type, 'int');
@@ -945,7 +1090,8 @@
var x = h.addVar('x', 'int?');
h.run((flow) {
h.declare(x, initialized: true);
- flow.logicalBinaryOp_rightBegin(h.eqNull(x)(), isAnd: false);
+ flow.logicalBinaryOp_rightBegin(h.eqNull(x, _Type('int?'))(),
+ isAnd: false);
expect(flow.promotedType(x).type, 'int');
flow.logicalBinaryOp_end(_Expression(), _Expression(), isAnd: false);
});
@@ -961,7 +1107,8 @@
h.run((flow) {
h.declare(x, initialized: true);
h.declare(y, initialized: true);
- h.if_(h.and(h.notNull(x), h.notNull(y)), () {
+ h.if_(h.and(h.notNull(x, _Type('int?')), h.notNull(y, _Type('int?'))),
+ () {
expect(flow.promotedType(x).type, 'int');
expect(flow.promotedType(y).type, 'int');
});
@@ -978,7 +1125,9 @@
h.run((flow) {
h.declare(x, initialized: true);
h.declare(y, initialized: true);
- h.ifElse(h.or(h.eqNull(x), h.eqNull(y)), () {}, () {
+ h.ifElse(
+ h.or(h.eqNull(x, _Type('int?')), h.eqNull(y, _Type('int?'))), () {},
+ () {
expect(flow.promotedType(x).type, 'int');
expect(flow.promotedType(y).type, 'int');
});
@@ -1046,8 +1195,11 @@
var x = h.addVar('x', 'int?');
h.run((flow) {
h.if_(
- h.parenthesized(h.notEqual(h.parenthesized(h.variableRead(x)),
- h.parenthesized(h.nullLiteral))), () {
+ h.parenthesized(h.notEqual(
+ h.parenthesized(h.variableRead(x)),
+ _Type('int?'),
+ h.parenthesized(h.nullLiteral),
+ _Type('Null'))), () {
expect(flow.promotedType(x).type, 'int');
});
});
@@ -1625,7 +1777,7 @@
h.run((flow) {
h.declare(x, initialized: true);
flow.whileStatement_conditionBegin(stmt);
- flow.whileStatement_bodyBegin(stmt, h.notNull(x)());
+ flow.whileStatement_bodyBegin(stmt, h.notNull(x, _Type('int?'))());
expect(flow.promotedType(x).type, 'int');
flow.whileStatement_end();
});
@@ -1646,7 +1798,8 @@
h.declare(y, initialized: true);
h.declare(z, initialized: true);
flow.whileStatement_conditionBegin(stmt);
- flow.whileStatement_bodyBegin(stmt, h.or(h.eqNull(x), h.eqNull(z))());
+ flow.whileStatement_bodyBegin(stmt,
+ h.or(h.eqNull(x, _Type('int?')), h.eqNull(z, _Type('int?')))());
h.if_(h.expr, () {
h.promote(x, 'int');
h.promote(y, 'int');
@@ -3133,6 +3286,7 @@
'int <: int?': true,
'int <: Iterable': false,
'int <: List': false,
+ 'int <: Null': false,
'int <: num': true,
'int <: num?': true,
'int <: num*': true,
@@ -3141,10 +3295,13 @@
'int <: Object?': true,
'int <: String': false,
'int? <: int': false,
+ 'int? <: Null': false,
'int? <: num': false,
'int? <: num?': true,
'int? <: Object': false,
'int? <: Object?': true,
+ 'Null <: int': false,
+ 'Null <: Object': false,
'num <: int': false,
'num <: Iterable': false,
'num <: List': false,
@@ -3175,6 +3332,7 @@
'Never? <: int?': true,
'Never? <: num?': true,
'Never? <: Object?': true,
+ 'Null <: int?': true,
'Object <: int': false,
'Object <: int?': false,
'Object <: List': false,
@@ -3204,6 +3362,7 @@
'int? - int': _Type('Never?'),
'int? - int?': _Type('Never'),
'int? - String': _Type('int?'),
+ 'Null - int': _Type('Null'),
'num - int': _Type('num'),
'num? - num': _Type('Never?'),
'num? - int': _Type('num?'),
@@ -3270,6 +3429,17 @@
callback(_AssignedVariablesHarness(_assignedVariables));
}
+ @override
+ TypeClassification classifyType(_Type type) {
+ if (isSubtypeOf(type, _Type('Object'))) {
+ return TypeClassification.nonNullable;
+ } else if (isSubtypeOf(type, _Type('Null'))) {
+ return TypeClassification.nullOrEquivalent;
+ } else {
+ return TypeClassification.potentiallyNullable;
+ }
+ }
+
/// Given three [LazyExpression]s, produces a new [LazyExpression]
/// representing the result of combining them with `?` and `:`.
LazyExpression conditional(
@@ -3293,15 +3463,15 @@
/// Creates a [LazyExpression] representing an `== null` check performed on
/// [variable].
- LazyExpression eqNull(_Var variable) {
+ LazyExpression eqNull(_Var variable, _Type type) {
return () {
var varExpr = _Expression();
_flow.variableRead(varExpr, variable);
- _flow.equalityOp_rightBegin(varExpr);
+ _flow.equalityOp_rightBegin(varExpr, type);
var nullExpr = _Expression();
_flow.nullLiteral(nullExpr);
var expr = _Expression();
- _flow.equalityOp_end(expr, nullExpr, notEqual: false);
+ _flow.equalityOp_end(expr, nullExpr, _Type('Null'), notEqual: false);
return expr;
};
}
@@ -3391,26 +3561,27 @@
/// Creates a [LazyExpression] representing an equality check between two
/// other expressions.
- LazyExpression notEqual(LazyExpression lhs, LazyExpression rhs) {
+ LazyExpression notEqual(
+ LazyExpression lhs, _Type lhsType, LazyExpression rhs, _Type rhsType) {
return () {
var expr = _Expression();
- _flow.equalityOp_rightBegin(lhs());
- _flow.equalityOp_end(expr, rhs(), notEqual: true);
+ _flow.equalityOp_rightBegin(lhs(), lhsType);
+ _flow.equalityOp_end(expr, rhs(), rhsType, notEqual: true);
return expr;
};
}
/// Creates a [LazyExpression] representing a `!= null` check performed on
/// [variable].
- LazyExpression notNull(_Var variable) {
+ LazyExpression notNull(_Var variable, _Type type) {
return () {
var varExpr = _Expression();
_flow.variableRead(varExpr, variable);
- _flow.equalityOp_rightBegin(varExpr);
+ _flow.equalityOp_rightBegin(varExpr, type);
var nullExpr = _Expression();
_flow.nullLiteral(nullExpr);
var expr = _Expression();
- _flow.equalityOp_end(expr, nullExpr, notEqual: true);
+ _flow.equalityOp_end(expr, nullExpr, _Type('Null'), notEqual: true);
return expr;
};
}
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/nullability/data/equality_operator.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/nullability/data/equality_operator.dart
new file mode 100644
index 0000000..e020657
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/nullability/data/equality_operator.dart
@@ -0,0 +1,69 @@
+// 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.
+
+Null nullExpr = null;
+
+void var_eq_null(int? x) {
+ if (x == null) {
+ x;
+ } else {
+ /*nonNullable*/ x;
+ }
+}
+
+void var_notEq_null(int? x) {
+ if (x != null) {
+ /*nonNullable*/ x;
+ } else {
+ x;
+ }
+}
+
+void null_eq_var(int? x) {
+ if (null == x) {
+ x;
+ } else {
+ /*nonNullable*/ x;
+ }
+}
+
+void null_notEq_var(int? x) {
+ if (null != x) {
+ /*nonNullable*/ x;
+ } else {
+ x;
+ }
+}
+
+void var_eq_nullExpr(int? x) {
+ if (x == nullExpr) {
+ x;
+ } else {
+ x;
+ }
+}
+
+void var_notEq_nullExpr(int? x) {
+ if (x != nullExpr) {
+ x;
+ } else {
+ x;
+ }
+}
+
+void nullExpr_eq_var(int? x) {
+ if (nullExpr == x) {
+ x;
+ } else {
+ x;
+ }
+}
+
+void nullExpr_notEq_var(int? x) {
+ if (nullExpr != x) {
+ x;
+ } else {
+ x;
+ }
+}
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/reachability/data/equality_operator.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/reachability/data/equality_operator.dart
new file mode 100644
index 0000000..195e679
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/reachability/data/equality_operator.dart
@@ -0,0 +1,83 @@
+// 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.
+
+void nullValue(Null x) {
+ if (x == null) {
+ 1;
+ } else /*unreachable*/ {
+ /*stmt: unreachable*/ 2;
+ }
+}
+
+void neverQuestionValue(Never? x) {
+ if (x == null) {
+ 1;
+ } else /*unreachable*/ {
+ /*stmt: unreachable*/ 2;
+ }
+}
+
+void dynamicValue(dynamic x) {
+ if (x == null) {
+ 1;
+ } else {
+ 2;
+ }
+}
+
+void nullableValue(int? x) {
+ if (x == null) {
+ 1;
+ } else {
+ 2;
+ }
+}
+
+void nonNullableValue(int x) {
+ if (x == null) /*unreachable*/ {
+ /*stmt: unreachable*/ 1;
+ } else {
+ 2;
+ }
+}
+
+void potentiallyNullableTypeVar_noBound<T>(T x) {
+ if (x == null) {
+ 1;
+ } else {
+ 2;
+ }
+}
+
+void potentiallyNullableTypeVar_dynamicBound<T extends dynamic>(T x) {
+ if (x == null) {
+ 1;
+ } else {
+ 2;
+ }
+}
+
+void potentiallyNullableTypeVar_nullableBound<T extends Object?>(T x) {
+ if (x == null) {
+ 1;
+ } else {
+ 2;
+ }
+}
+
+void nonNullableTypeVar<T extends Object>(T x) {
+ if (x == null) /*unreachable*/ {
+ /*stmt: unreachable*/ 1;
+ } else {
+ 2;
+ }
+}
+
+void nullTypeVar<T extends Null>(T x) {
+ if (x == null) {
+ 1;
+ } else /*unreachable*/ {
+ /*stmt: unreachable*/ 2;
+ }
+}
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/type_promotion/data/binary.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/type_promotion/data/binary.dart
index addc66a..b43c121 100644
--- a/pkg/_fe_analyzer_shared/test/flow_analysis/type_promotion/data/binary.dart
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/type_promotion/data/binary.dart
@@ -7,7 +7,7 @@
/*num*/ x;
}
-void ifNull_rightUnPromote(Object x, Object y, Object z) {
+void ifNull_rightUnPromote(Object x, Object? y, Object z) {
if (x is int) {
/*int*/ x;
y ?? (x = z);
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/type_promotion/data/null_check.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/type_promotion/data/null_check.dart
index 9dcfa11..8879f21 100644
--- a/pkg/_fe_analyzer_shared/test/flow_analysis/type_promotion/data/null_check.dart
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/type_promotion/data/null_check.dart
@@ -18,9 +18,9 @@
}
}
-promotesNullType(Null x) {
+doesNotPromoteNullType(Null x) {
if (x != null) {
- /*Never*/ x;
+ x;
} else {
x;
}
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/add_type_parameter_change.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/add_type_parameter_change.dart
index edc2312..c195e50 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/add_type_parameter_change.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/add_type_parameter_change.dart
@@ -41,8 +41,10 @@
void apply(DartFileEditBuilder builder, DataDrivenFix fix, _Data data) {
if (data is _TypeArgumentData) {
_applyToTypeArguments(builder, data);
+ } else if (data is _TypeParameterData) {
+ _applyToTypeParameters(builder, data);
} else {
- _applyToTypeParameters(builder, data as _TypeParameterData);
+ throw StateError('Unsupported class of data: ${data.runtimeType}');
}
}
@@ -165,7 +167,7 @@
/// The data returned when updating a type parameter list.
class _TypeParameterData extends _Data {
- /// The list of type parameters to which a new type paramete is being added,
+ /// The list of type parameters to which a new type parameter is being added,
/// or `null` if the first type parameter is being added.
final TypeParameterList typeParameters;
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/value_extractor.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/value_extractor.dart
index 704eae8..ad5243b 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/value_extractor.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/value_extractor.dart
@@ -6,14 +6,16 @@
import 'package:analysis_server/src/services/correction/util.dart';
import 'package:analyzer/dart/ast/ast.dart';
-/// A value extractor used to extract a specified argument of an invocation.
+/// A value extractor used to extract a specified argument from an invocation.
class ArgumentExtractor extends ValueExtractor {
- /// The parameter that defines the argument to be extracted.
+ /// The parameter corresponding to the argument from the original invocation,
+ /// or `null` if the value of the argument can't be taken from the original
+ /// invocation.
final ParameterReference parameter;
/// Initialize a newly created extractor to extract the argument that
/// corresponds to the given [parameter].
- ArgumentExtractor(this.parameter);
+ ArgumentExtractor(this.parameter) : assert(parameter != null);
@override
String from(AstNode node, CorrectionUtils utils) {
@@ -32,7 +34,7 @@
/// The code to be returned.
final String code;
- /// Initialize a newly created extractor to return the given [code].
+ /// Initialize a newly created extractor to return the given [code].
LiteralExtractor(this.code);
@override
diff --git a/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart
index 8486729..db0d9c9 100644
--- a/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart
@@ -136,13 +136,13 @@
left = node.leftOperand;
var flow = _flowAnalysis?.flow;
- flow?.equalityOp_rightBegin(left);
+ flow?.equalityOp_rightBegin(left, left.staticType);
var right = node.rightOperand;
right.accept(_resolver);
right = node.rightOperand;
- flow?.equalityOp_end(node, right, notEqual: notEqual);
+ flow?.equalityOp_end(node, right, right.staticType, notEqual: notEqual);
_resolveUserDefinableElement(
node,
diff --git a/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart b/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
index 9d22406..f6fe441 100644
--- a/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
@@ -351,6 +351,17 @@
TypeSystemTypeOperations(this.typeSystem);
@override
+ TypeClassification classifyType(DartType type) {
+ if (isSubtypeOf(type, typeSystem.typeProvider.objectType)) {
+ return TypeClassification.nonNullable;
+ } else if (isSubtypeOf(type, typeSystem.typeProvider.nullType)) {
+ return TypeClassification.nullOrEquivalent;
+ } else {
+ return TypeClassification.potentiallyNullable;
+ }
+ }
+
+ @override
DartType factor(DartType from, DartType what) {
return typeSystem.factor(from, what);
}
diff --git a/pkg/analyzer/lib/src/error/dead_code_verifier.dart b/pkg/analyzer/lib/src/error/dead_code_verifier.dart
index ad7fa1f..24eefd2 100644
--- a/pkg/analyzer/lib/src/error/dead_code_verifier.dart
+++ b/pkg/analyzer/lib/src/error/dead_code_verifier.dart
@@ -505,33 +505,40 @@
return;
}
- // We know that [node] is the first dead node, or contains it.
- // So, technically the code code interval ends at the end of [node].
- // But we trim it to the last statement for presentation purposes.
- if (node != _firstDeadNode) {
- if (node is FunctionDeclaration) {
- node = (node as FunctionDeclaration).functionExpression.body;
+ var parent = _firstDeadNode.parent;
+ if (parent is Assertion && identical(_firstDeadNode, parent.message)) {
+ // Don't report "dead code" for the message part of an assert statement,
+ // because this causes nuisance warnings for redundant `!= null`
+ // asserts.
+ } else {
+ // We know that [node] is the first dead node, or contains it.
+ // So, technically the code code interval ends at the end of [node].
+ // But we trim it to the last statement for presentation purposes.
+ if (node != _firstDeadNode) {
+ if (node is FunctionDeclaration) {
+ node = (node as FunctionDeclaration).functionExpression.body;
+ }
+ if (node is FunctionExpression) {
+ node = (node as FunctionExpression).body;
+ }
+ if (node is MethodDeclaration) {
+ node = (node as MethodDeclaration).body;
+ }
+ if (node is BlockFunctionBody) {
+ node = (node as BlockFunctionBody).block;
+ }
+ if (node is Block && node.statements.isNotEmpty) {
+ node = (node as Block).statements.last;
+ }
+ if (node is SwitchMember && node.statements.isNotEmpty) {
+ node = (node as SwitchMember).statements.last;
+ }
}
- if (node is FunctionExpression) {
- node = (node as FunctionExpression).body;
- }
- if (node is MethodDeclaration) {
- node = (node as MethodDeclaration).body;
- }
- if (node is BlockFunctionBody) {
- node = (node as BlockFunctionBody).block;
- }
- if (node is Block && node.statements.isNotEmpty) {
- node = (node as Block).statements.last;
- }
- if (node is SwitchMember && node.statements.isNotEmpty) {
- node = (node as SwitchMember).statements.last;
- }
- }
- var offset = _firstDeadNode.offset;
- var length = node.end - offset;
- _errorReporter.reportErrorForOffset(HintCode.DEAD_CODE, offset, length);
+ var offset = _firstDeadNode.offset;
+ var length = node.end - offset;
+ _errorReporter.reportErrorForOffset(HintCode.DEAD_CODE, offset, length);
+ }
_firstDeadNode = null;
}
diff --git a/pkg/analyzer/test/src/diagnostics/dead_code_test.dart b/pkg/analyzer/test/src/diagnostics/dead_code_test.dart
index 2529fea..b261479 100644
--- a/pkg/analyzer/test/src/diagnostics/dead_code_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/dead_code_test.dart
@@ -846,6 +846,20 @@
@reflectiveTest
class DeadCodeWithNullSafetyTest extends DeadCodeTest with WithNullSafetyMixin {
+ test_assert_dead_message() async {
+ // We don't warn if an assert statement is live but its message is dead,
+ // because this results in nuisance warnings for desirable assertions (e.g.
+ // a `!= null` assertion that is redundant with strong checking but still
+ // useful with weak checking).
+ await assertErrorsInCode('''
+void f(Object waldo) {
+ assert(waldo != null, "Where's Waldo?");
+}
+''', [
+ error(HintCode.UNNECESSARY_NULL_COMPARISON_TRUE, 38, 7),
+ ]);
+ }
+
test_flowEnd_tryStatement_body() async {
await assertErrorsInCode(r'''
Never foo() => throw 0;
diff --git a/pkg/front_end/lib/src/fasta/kernel/forwarding_node.dart b/pkg/front_end/lib/src/fasta/kernel/forwarding_node.dart
index ab6775c..5fa02e2 100644
--- a/pkg/front_end/lib/src/fasta/kernel/forwarding_node.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/forwarding_node.dart
@@ -418,6 +418,34 @@
(stub as Procedure).isMemberSignature &&
stub.memberSignatureOrigin == null),
"No member signature origin for member signature $stub.");
+ if (stub != interfaceMember && stub is Procedure) {
+ Procedure procedure = stub;
+ if (procedure.isForwardingStub || procedure.isForwardingSemiStub) {
+ procedure.isMemberSignature = false;
+ procedure.memberSignatureOrigin = null;
+ } else {
+ procedure.forwardingStubInterfaceTarget = null;
+ procedure.forwardingStubSuperTarget = null;
+ }
+ assert(
+ !(procedure.isMemberSignature && procedure.isForwardingStub),
+ "Procedure is both member signature and forwarding stub: "
+ "$procedure.");
+ assert(
+ !(procedure.isMemberSignature && procedure.isForwardingSemiStub),
+ "Procedure is both member signature and forwarding semi stub: "
+ "$procedure.");
+ assert(
+ !(procedure.forwardingStubInterfaceTarget is Procedure &&
+ (procedure.forwardingStubInterfaceTarget as Procedure)
+ .isMemberSignature),
+ "Forwarding stub interface target is member signature: $procedure.");
+ assert(
+ !(procedure.forwardingStubSuperTarget is Procedure &&
+ (procedure.forwardingStubSuperTarget as Procedure)
+ .isMemberSignature),
+ "Forwarding stub super target is member signature: $procedure.");
+ }
return stub;
}
@@ -438,6 +466,8 @@
if (superTarget is Procedure && superTarget.isForwardingStub) {
Procedure superProcedure = superTarget;
superTarget = superProcedure.forwardingStubSuperTarget;
+ } else {
+ superTarget = superTarget.memberSignatureOrigin ?? superTarget;
}
procedure.isAbstract = false;
if (!procedure.isForwardingStub) {
@@ -528,7 +558,7 @@
if (target is Procedure && target.isForwardingStub) {
finalTarget = target.forwardingStubInterfaceTarget;
} else {
- finalTarget = target;
+ finalTarget = target.memberSignatureOrigin ?? target;
}
Procedure referenceFrom;
if (classBuilder.referencesFromIndexed != null) {
diff --git a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
index bf4ca5e..aec4c80 100644
--- a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
@@ -3520,7 +3520,7 @@
int fileOffset, Expression left, DartType leftType, Expression right,
{bool isNot}) {
assert(isNot != null);
- inferrer.flowAnalysis.equalityOp_rightBegin(left);
+ inferrer.flowAnalysis.equalityOp_rightBegin(left, leftType);
ObjectAccessTarget equalsTarget = inferrer.findInterfaceMember(
leftType, equalsName, fileOffset,
includeExtensionMethods: true);
@@ -3557,7 +3557,9 @@
if (isNot) {
equals = new Not(equals)..fileOffset = fileOffset;
}
- inferrer.flowAnalysis.equalityOp_end(equals, right, notEqual: isNot);
+ inferrer.flowAnalysis.equalityOp_end(
+ equals, right, rightResult.inferredType,
+ notEqual: isNot);
return new ExpressionInferenceResult(
equalsTarget.isNever
? const NeverType(Nullability.nonNullable)
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inference_engine.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inference_engine.dart
index 78e2f3e..a75173f 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inference_engine.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inference_engine.dart
@@ -264,6 +264,21 @@
TypeOperationsCfe(this.typeEnvironment);
@override
+ TypeClassification classifyType(DartType type) {
+ if (type == null) {
+ // Note: this can happen during top-level inference.
+ return TypeClassification.potentiallyNullable;
+ } else if (isSubtypeOf(
+ type, typeEnvironment.coreTypes.objectNonNullableRawType)) {
+ return TypeClassification.nonNullable;
+ } else if (isSubtypeOf(type, typeEnvironment.coreTypes.nullType)) {
+ return TypeClassification.nullOrEquivalent;
+ } else {
+ return TypeClassification.potentiallyNullable;
+ }
+ }
+
+ @override
DartType factor(DartType from, DartType what) {
return factorType(typeEnvironment, from, what);
}
diff --git a/pkg/front_end/test/spell_checking_list_common.txt b/pkg/front_end/test/spell_checking_list_common.txt
index 0850a28..81e9982 100644
--- a/pkg/front_end/test/spell_checking_list_common.txt
+++ b/pkg/front_end/test/spell_checking_list_common.txt
@@ -446,6 +446,10 @@
class
classes
classic
+classification
+classifications
+classifies
+classify
clause
clauses
clean
diff --git a/pkg/front_end/test/unit_test_suites.dart b/pkg/front_end/test/unit_test_suites.dart
index 72bc4fa..768fa14 100644
--- a/pkg/front_end/test/unit_test_suites.dart
+++ b/pkg/front_end/test/unit_test_suites.dart
@@ -8,7 +8,6 @@
import 'dart:isolate' show Isolate, ReceivePort, SendPort;
import 'package:args/args.dart' show ArgParser;
-
import 'package:testing/src/chain.dart' show CreateContext, Result, Step;
import 'package:testing/src/expectation.dart' show Expectation;
import 'package:testing/src/log.dart' show Logger;
@@ -17,13 +16,14 @@
import 'package:testing/src/test_description.dart' show TestDescription;
import 'fasta/expression_suite.dart' as expression show createContext;
-import 'fasta/outline_suite.dart' as outline show createContext;
import 'fasta/fast_strong_suite.dart' as fast_strong show createContext;
import 'fasta/incremental_suite.dart' as incremental show createContext;
import 'fasta/messages_suite.dart' as messages show createContext;
+import 'fasta/outline_suite.dart' as outline show createContext;
import 'fasta/strong_tester.dart' as strong show createContext;
import 'fasta/text_serialization_tester.dart' as text_serialization
show createContext;
+import 'fasta/textual_outline_suite.dart' as textual_outline show createContext;
import 'fasta/weak_suite.dart' as weak show createContext;
import 'incremental_bulk_compiler_smoke_suite.dart' as incremental_bulk_compiler
show createContext;
@@ -271,6 +271,8 @@
const Suite(
"spelling_test_src", spelling_src.createContext, "../testing.json"),
const Suite("fasta/weak", weak.createContext, "../../testing.json"),
+ const Suite("fasta/textual_outline", textual_outline.createContext,
+ "../../testing.json"),
];
const Duration timeoutDuration = Duration(minutes: 25);
diff --git a/pkg/front_end/testcases/general/callable_type_variable.dart.textual_outline.expect b/pkg/front_end/testcases/general/callable_type_variable.dart.textual_outline.expect
new file mode 100644
index 0000000..d54964e
--- /dev/null
+++ b/pkg/front_end/testcases/general/callable_type_variable.dart.textual_outline.expect
@@ -0,0 +1,13 @@
+class Class1<T extends Function> {
+ T field;
+ Class1(this.field);
+ method() {}
+}
+
+class Class2<T extends String Function(int)> {
+ T field;
+ Class2(this.field);
+ method() {}
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/general/callable_type_variable.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/general/callable_type_variable.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..8eb5879
--- /dev/null
+++ b/pkg/front_end/testcases/general/callable_type_variable.dart.textual_outline_modelled.expect
@@ -0,0 +1,13 @@
+class Class1<T extends Function> {
+ Class1(this.field);
+ T field;
+ method() {}
+}
+
+class Class2<T extends String Function(int)> {
+ Class2(this.field);
+ T field;
+ method() {}
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/general/issue34714.dart.textual_outline.expect b/pkg/front_end/testcases/general/issue34714.dart.textual_outline.expect
new file mode 100644
index 0000000..82ac1d1
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue34714.dart.textual_outline.expect
@@ -0,0 +1,9 @@
+class A<T> {
+ factory A() = B;
+}
+
+class B<T> implements A<T> {
+ B();
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/general/issue34714.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/general/issue34714.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..82ac1d1
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue34714.dart.textual_outline_modelled.expect
@@ -0,0 +1,9 @@
+class A<T> {
+ factory A() = B;
+}
+
+class B<T> implements A<T> {
+ B();
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/general/issue42615.dart.textual_outline.expect b/pkg/front_end/testcases/general/issue42615.dart.textual_outline.expect
new file mode 100644
index 0000000..d430822
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue42615.dart.textual_outline.expect
@@ -0,0 +1,8 @@
+import 'dart:async';
+
+class Class<T> {
+ Class({FutureOr<List<T>> Function() a});
+}
+
+dynamic method() => null;
+main() {}
diff --git a/pkg/front_end/testcases/general/issue42615.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/general/issue42615.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..d430822
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue42615.dart.textual_outline_modelled.expect
@@ -0,0 +1,8 @@
+import 'dart:async';
+
+class Class<T> {
+ Class({FutureOr<List<T>> Function() a});
+}
+
+dynamic method() => null;
+main() {}
diff --git a/pkg/front_end/testcases/general_nnbd_opt_out/future_or_null_test.dart.textual_outline.expect b/pkg/front_end/testcases/general_nnbd_opt_out/future_or_null_test.dart.textual_outline.expect
new file mode 100644
index 0000000..c579627
--- /dev/null
+++ b/pkg/front_end/testcases/general_nnbd_opt_out/future_or_null_test.dart.textual_outline.expect
@@ -0,0 +1,5 @@
+// @dart = 2.6
+import 'dart:async';
+
+FutureOr<Null> get foo => null;
+main() {}
diff --git a/pkg/front_end/testcases/general_nnbd_opt_out/future_or_null_test.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/general_nnbd_opt_out/future_or_null_test.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..c579627
--- /dev/null
+++ b/pkg/front_end/testcases/general_nnbd_opt_out/future_or_null_test.dart.textual_outline_modelled.expect
@@ -0,0 +1,5 @@
+// @dart = 2.6
+import 'dart:async';
+
+FutureOr<Null> get foo => null;
+main() {}
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/flutter_widget_transform_const.yaml b/pkg/front_end/testcases/incremental_initialize_from_dill/flutter_widget_transform_const.yaml
new file mode 100644
index 0000000..07a3cda
--- /dev/null
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/flutter_widget_transform_const.yaml
@@ -0,0 +1,122 @@
+# 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.md file.
+
+type: newworld
+target: DDC
+trackWidgetCreation: true
+worlds:
+ - entry: main.dart
+ experiments: non-nullable
+ sources:
+ main.dart: |
+ import 'foo.dart';
+ Foo newFoo = new Foo();
+ Bar newBar = new Bar();
+ Bar constBar = const Bar();
+ Baz newBaz = new Baz();
+ Boz newBoz = new Boz(createNew: true);
+ Boz constBoz = new Boz(createNew: false);
+ foo.dart:
+ import 'package:flutter/src/widgets/framework.dart';
+ import 'package:flutter/src/widgets/widget_inspector.dart';
+
+ class Foo extends Widget {
+ factory Foo() => const Foo._();
+
+ const Foo._();
+ }
+
+ class Bar extends Widget {
+ const factory Bar() = Bar._;
+
+ const Bar._();
+ }
+
+ class Baz extends Widget {
+ factory Baz() => const Baz._();
+
+ const factory Baz._() = Baz.__;
+
+ const Baz.__();
+ }
+
+ class Boz extends Widget {
+ factory Boz({bool createNew}) {
+ if (createNew) {
+ return new Boz._();
+ } else {
+ return const Boz._();
+ }
+ }
+
+ const Boz._();
+ }
+ flutter/lib/src/widgets/framework.dart: |
+ abstract class Bar {
+ const Bar();
+ }
+ abstract class Widget extends Bar {
+ const Widget();
+ }
+ flutter/lib/src/widgets/widget_inspector.dart: |
+ abstract class _HasCreationLocation {
+ _Location get _location;
+ }
+ /// A tuple with file, line, and column number, for displaying human-readable
+ /// file locations.
+ class _Location {
+ const _Location({
+ required this.file,
+ required this.line,
+ required this.column,
+ required this.name,
+ required this.parameterLocations,
+ });
+ /// File path of the location.
+ final String file;
+ /// 1-based line number.
+ final int line;
+ /// 1-based column number.
+ final int column;
+ /// Optional name of the parameter or function at this location.
+ final String name;
+ /// Optional locations of the parameters of the member at this location.
+ final List<_Location> parameterLocations;
+ }
+ .dart_tool/package_config.json: |
+ {
+ "configVersion": 2,
+ "packages": [
+ {
+ "name": "flutter",
+ "rootUri": "../flutter",
+ "packageUri": "lib/"
+ }
+ ]
+ }
+ expectedLibraryCount: 4
+ - entry: main.dart
+ worldType: updated
+ invalidate:
+ - main.dart
+ expectInitializeFromDill: false
+ sources:
+ main.dart: |
+ import 'foo.dart';
+ expectedLibraryCount: 4
+ - entry: main.dart
+ worldType: updated
+ invalidate:
+ - main.dart
+ expectInitializeFromDill: false
+ sources:
+ main.dart: |
+ import 'foo.dart';
+ Foo newFoo = new Foo();
+ Bar newBar = new Bar();
+ Bar constBar = const Bar();
+ Baz newBaz = new Baz();
+ Boz newBoz = new Boz(createNew: true);
+ Boz constBoz = new Boz(createNew: false);
+ expectedLibraryCount: 4
\ No newline at end of file
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/flutter_widget_transform_const.yaml.world.1.expect b/pkg/front_end/testcases/incremental_initialize_from_dill/flutter_widget_transform_const.yaml.world.1.expect
new file mode 100644
index 0000000..3847ff7
--- /dev/null
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/flutter_widget_transform_const.yaml.world.1.expect
@@ -0,0 +1,126 @@
+main = <No Member>;
+library from "package:flutter/src/widgets/framework.dart" as fra {
+
+ abstract class Bar extends dart.core::Object /*hasConstConstructor*/ {
+ const constructor •() → fra::Bar
+ : super dart.core::Object::•()
+ ;
+ }
+ abstract class Widget extends fra::Bar implements wid::_HasCreationLocation /*hasConstConstructor*/ {
+ final field wid::_Location? _location /*isNullableByDefault, from null */;
+ const constructor •({wid::_Location? $creationLocationd_0dea112b090073317d4}) → fra::Widget
+ : super fra::Bar::•(), fra::Widget::_location = $creationLocationd_0dea112b090073317d4!
+ ;
+ }
+}
+library from "package:flutter/src/widgets/widget_inspector.dart" as wid {
+
+ abstract class _HasCreationLocation extends dart.core::Object {
+ synthetic constructor •() → wid::_HasCreationLocation
+ : super dart.core::Object::•()
+ ;
+ abstract get _location() → wid::_Location;
+ }
+ class _Location extends dart.core::Object /*hasConstConstructor*/ {
+ final field dart.core::String file;
+ final field dart.core::int line;
+ final field dart.core::int column;
+ final field dart.core::String name;
+ final field dart.core::List<wid::_Location> parameterLocations;
+ const constructor •({required dart.core::String file = #C1, required dart.core::int line = #C1, required dart.core::int column = #C1, required dart.core::String name = #C1, required dart.core::List<wid::_Location> parameterLocations = #C1}) → wid::_Location
+ : wid::_Location::file = file, wid::_Location::line = line, wid::_Location::column = column, wid::_Location::name = name, wid::_Location::parameterLocations = parameterLocations, super dart.core::Object::•()
+ ;
+ }
+}
+library from "org-dartlang-test:///foo.dart" as foo {
+
+ import "package:flutter/src/widgets/framework.dart";
+ import "package:flutter/src/widgets/widget_inspector.dart";
+
+ class Foo extends fra::Widget /*hasConstConstructor*/ {
+ const constructor _({wid::_Location? $creationLocationd_0dea112b090073317d4}) → foo::Foo
+ : super fra::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
+ ;
+ static factory •({wid::_Location? $creationLocationd_0dea112b090073317d4}) → foo::Foo
+ return #C7;
+ }
+ class Bar extends fra::Widget /*hasConstConstructor*/ {
+ static field dynamic _redirecting# = <dynamic>[foo::Bar::•]/*isNullableByDefault*/;
+ const constructor _({wid::_Location? $creationLocationd_0dea112b090073317d4}) → foo::Bar
+ : super fra::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
+ ;
+ static factory •({wid::_Location? $creationLocationd_0dea112b090073317d4}) → foo::Bar
+ let dynamic #redirecting_factory = foo::Bar::_ in invalid-expression;
+ }
+ class Baz extends fra::Widget /*hasConstConstructor*/ {
+ static field dynamic _redirecting# = <dynamic>[foo::Baz::_]/*isNullableByDefault*/;
+ const constructor __({wid::_Location? $creationLocationd_0dea112b090073317d4}) → foo::Baz
+ : super fra::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
+ ;
+ static factory •({wid::_Location? $creationLocationd_0dea112b090073317d4}) → foo::Baz
+ return #C10;
+ static factory _({wid::_Location? $creationLocationd_0dea112b090073317d4}) → foo::Baz
+ let dynamic #redirecting_factory = foo::Baz::__ in invalid-expression;
+ }
+ class Boz extends fra::Widget /*hasConstConstructor*/ {
+ const constructor _({wid::_Location? $creationLocationd_0dea112b090073317d4}) → foo::Boz
+ : super fra::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
+ ;
+ static factory •({dart.core::bool createNew = #C1, wid::_Location? $creationLocationd_0dea112b090073317d4}) → foo::Boz {
+ if(createNew) {
+ return new foo::Boz::_($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4);
+ }
+ else {
+ return #C14;
+ }
+ }
+ }
+}
+library from "org-dartlang-test:///main.dart" as main {
+
+ import "org-dartlang-test:///foo.dart";
+
+ static field foo::Foo newFoo = foo::Foo::•($creationLocationd_0dea112b090073317d4: #C17);
+ static field foo::Bar newBar = new foo::Bar::_($creationLocationd_0dea112b090073317d4: #C19);
+ static field foo::Bar constBar = #C23;
+ static field foo::Baz newBaz = foo::Baz::•($creationLocationd_0dea112b090073317d4: #C25);
+ static field foo::Boz newBoz = foo::Boz::•(createNew: true, $creationLocationd_0dea112b090073317d4: #C29);
+ static field foo::Boz constBoz = foo::Boz::•(createNew: false, $creationLocationd_0dea112b090073317d4: #C35);
+}
+constants {
+ #C1 = null
+ #C2 = "org-dartlang-test:///foo.dart"
+ #C3 = 2.0
+ #C4 = 51.0
+ #C5 = <wid::_Location*>[]
+ #C6 = wid::_Location {file:#C2, line:#C3, column:#C4, name:#C1, parameterLocations:#C5}
+ #C7 = foo::Foo {_location:#C6}
+ #C8 = 6.0
+ #C9 = wid::_Location {file:#C2, line:#C8, column:#C4, name:#C1, parameterLocations:#C5}
+ #C10 = foo::Baz {_location:#C9}
+ #C11 = 9.0
+ #C12 = 119.0
+ #C13 = wid::_Location {file:#C2, line:#C11, column:#C12, name:#C1, parameterLocations:#C5}
+ #C14 = foo::Boz {_location:#C13}
+ #C15 = "org-dartlang-test:///main.dart"
+ #C16 = 18.0
+ #C17 = wid::_Location {file:#C15, line:#C3, column:#C16, name:#C1, parameterLocations:#C5}
+ #C18 = 3.0
+ #C19 = wid::_Location {file:#C15, line:#C18, column:#C16, name:#C1, parameterLocations:#C5}
+ #C20 = 4.0
+ #C21 = 22.0
+ #C22 = wid::_Location {file:#C15, line:#C20, column:#C21, name:#C1, parameterLocations:#C5}
+ #C23 = foo::Bar {_location:#C22}
+ #C24 = 5.0
+ #C25 = wid::_Location {file:#C15, line:#C24, column:#C16, name:#C1, parameterLocations:#C5}
+ #C26 = "createNew"
+ #C27 = wid::_Location {file:#C1, line:#C8, column:#C21, name:#C26, parameterLocations:#C1}
+ #C28 = <wid::_Location*>[#C27]
+ #C29 = wid::_Location {file:#C15, line:#C8, column:#C16, name:#C1, parameterLocations:#C28}
+ #C30 = 7.0
+ #C31 = 20.0
+ #C32 = 24.0
+ #C33 = wid::_Location {file:#C1, line:#C30, column:#C32, name:#C26, parameterLocations:#C1}
+ #C34 = <wid::_Location*>[#C33]
+ #C35 = wid::_Location {file:#C15, line:#C30, column:#C31, name:#C1, parameterLocations:#C34}
+}
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/flutter_widget_transform_const.yaml.world.2.expect b/pkg/front_end/testcases/incremental_initialize_from_dill/flutter_widget_transform_const.yaml.world.2.expect
new file mode 100644
index 0000000..e48708e
--- /dev/null
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/flutter_widget_transform_const.yaml.world.2.expect
@@ -0,0 +1,99 @@
+main = <No Member>;
+library from "package:flutter/src/widgets/framework.dart" as fra {
+
+ abstract class Bar extends dart.core::Object /*hasConstConstructor*/ {
+ const constructor •() → fra::Bar
+ : super dart.core::Object::•()
+ ;
+ }
+ abstract class Widget extends fra::Bar implements wid::_HasCreationLocation /*hasConstConstructor*/ {
+ final field wid::_Location? _location /*isNullableByDefault, from null */;
+ const constructor •({wid::_Location? $creationLocationd_0dea112b090073317d4}) → fra::Widget
+ : super fra::Bar::•(), fra::Widget::_location = $creationLocationd_0dea112b090073317d4!
+ ;
+ }
+}
+library from "package:flutter/src/widgets/widget_inspector.dart" as wid {
+
+ abstract class _HasCreationLocation extends dart.core::Object {
+ synthetic constructor •() → wid::_HasCreationLocation
+ : super dart.core::Object::•()
+ ;
+ abstract get _location() → wid::_Location;
+ }
+ class _Location extends dart.core::Object /*hasConstConstructor*/ {
+ final field dart.core::String file;
+ final field dart.core::int line;
+ final field dart.core::int column;
+ final field dart.core::String name;
+ final field dart.core::List<wid::_Location> parameterLocations;
+ const constructor •({required dart.core::String file = #C1, required dart.core::int line = #C1, required dart.core::int column = #C1, required dart.core::String name = #C1, required dart.core::List<wid::_Location> parameterLocations = #C1}) → wid::_Location
+ : wid::_Location::file = file, wid::_Location::line = line, wid::_Location::column = column, wid::_Location::name = name, wid::_Location::parameterLocations = parameterLocations, super dart.core::Object::•()
+ ;
+ }
+}
+library from "org-dartlang-test:///foo.dart" as foo {
+
+ import "package:flutter/src/widgets/framework.dart";
+ import "package:flutter/src/widgets/widget_inspector.dart";
+
+ class Foo extends fra::Widget /*hasConstConstructor*/ {
+ const constructor _({wid::_Location? $creationLocationd_0dea112b090073317d4}) → foo::Foo
+ : super fra::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
+ ;
+ static factory •({wid::_Location? $creationLocationd_0dea112b090073317d4}) → foo::Foo
+ return #C7;
+ }
+ class Bar extends fra::Widget /*hasConstConstructor*/ {
+ static field dynamic _redirecting# = <dynamic>[foo::Bar::•]/*isNullableByDefault*/;
+ const constructor _({wid::_Location? $creationLocationd_0dea112b090073317d4}) → foo::Bar
+ : super fra::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
+ ;
+ static factory •({wid::_Location? $creationLocationd_0dea112b090073317d4}) → foo::Bar
+ let dynamic #redirecting_factory = foo::Bar::_ in invalid-expression;
+ }
+ class Baz extends fra::Widget /*hasConstConstructor*/ {
+ static field dynamic _redirecting# = <dynamic>[foo::Baz::_]/*isNullableByDefault*/;
+ const constructor __({wid::_Location? $creationLocationd_0dea112b090073317d4}) → foo::Baz
+ : super fra::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
+ ;
+ static factory •({wid::_Location? $creationLocationd_0dea112b090073317d4}) → foo::Baz
+ return #C10;
+ static factory _({wid::_Location? $creationLocationd_0dea112b090073317d4}) → foo::Baz
+ let dynamic #redirecting_factory = foo::Baz::__ in invalid-expression;
+ }
+ class Boz extends fra::Widget /*hasConstConstructor*/ {
+ const constructor _({wid::_Location? $creationLocationd_0dea112b090073317d4}) → foo::Boz
+ : super fra::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
+ ;
+ static factory •({dart.core::bool createNew = #C1, wid::_Location? $creationLocationd_0dea112b090073317d4}) → foo::Boz {
+ if(createNew) {
+ return new foo::Boz::_($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4);
+ }
+ else {
+ return #C14;
+ }
+ }
+ }
+}
+library from "org-dartlang-test:///main.dart" as main {
+
+ import "org-dartlang-test:///foo.dart";
+
+}
+constants {
+ #C1 = null
+ #C2 = "org-dartlang-test:///foo.dart"
+ #C3 = 2.0
+ #C4 = 51.0
+ #C5 = <wid::_Location*>[]
+ #C6 = wid::_Location {file:#C2, line:#C3, column:#C4, name:#C1, parameterLocations:#C5}
+ #C7 = foo::Foo {_location:#C6}
+ #C8 = 6.0
+ #C9 = wid::_Location {file:#C2, line:#C8, column:#C4, name:#C1, parameterLocations:#C5}
+ #C10 = foo::Baz {_location:#C9}
+ #C11 = 9.0
+ #C12 = 119.0
+ #C13 = wid::_Location {file:#C2, line:#C11, column:#C12, name:#C1, parameterLocations:#C5}
+ #C14 = foo::Boz {_location:#C13}
+}
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/flutter_widget_transform_const.yaml.world.3.expect b/pkg/front_end/testcases/incremental_initialize_from_dill/flutter_widget_transform_const.yaml.world.3.expect
new file mode 100644
index 0000000..3847ff7
--- /dev/null
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/flutter_widget_transform_const.yaml.world.3.expect
@@ -0,0 +1,126 @@
+main = <No Member>;
+library from "package:flutter/src/widgets/framework.dart" as fra {
+
+ abstract class Bar extends dart.core::Object /*hasConstConstructor*/ {
+ const constructor •() → fra::Bar
+ : super dart.core::Object::•()
+ ;
+ }
+ abstract class Widget extends fra::Bar implements wid::_HasCreationLocation /*hasConstConstructor*/ {
+ final field wid::_Location? _location /*isNullableByDefault, from null */;
+ const constructor •({wid::_Location? $creationLocationd_0dea112b090073317d4}) → fra::Widget
+ : super fra::Bar::•(), fra::Widget::_location = $creationLocationd_0dea112b090073317d4!
+ ;
+ }
+}
+library from "package:flutter/src/widgets/widget_inspector.dart" as wid {
+
+ abstract class _HasCreationLocation extends dart.core::Object {
+ synthetic constructor •() → wid::_HasCreationLocation
+ : super dart.core::Object::•()
+ ;
+ abstract get _location() → wid::_Location;
+ }
+ class _Location extends dart.core::Object /*hasConstConstructor*/ {
+ final field dart.core::String file;
+ final field dart.core::int line;
+ final field dart.core::int column;
+ final field dart.core::String name;
+ final field dart.core::List<wid::_Location> parameterLocations;
+ const constructor •({required dart.core::String file = #C1, required dart.core::int line = #C1, required dart.core::int column = #C1, required dart.core::String name = #C1, required dart.core::List<wid::_Location> parameterLocations = #C1}) → wid::_Location
+ : wid::_Location::file = file, wid::_Location::line = line, wid::_Location::column = column, wid::_Location::name = name, wid::_Location::parameterLocations = parameterLocations, super dart.core::Object::•()
+ ;
+ }
+}
+library from "org-dartlang-test:///foo.dart" as foo {
+
+ import "package:flutter/src/widgets/framework.dart";
+ import "package:flutter/src/widgets/widget_inspector.dart";
+
+ class Foo extends fra::Widget /*hasConstConstructor*/ {
+ const constructor _({wid::_Location? $creationLocationd_0dea112b090073317d4}) → foo::Foo
+ : super fra::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
+ ;
+ static factory •({wid::_Location? $creationLocationd_0dea112b090073317d4}) → foo::Foo
+ return #C7;
+ }
+ class Bar extends fra::Widget /*hasConstConstructor*/ {
+ static field dynamic _redirecting# = <dynamic>[foo::Bar::•]/*isNullableByDefault*/;
+ const constructor _({wid::_Location? $creationLocationd_0dea112b090073317d4}) → foo::Bar
+ : super fra::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
+ ;
+ static factory •({wid::_Location? $creationLocationd_0dea112b090073317d4}) → foo::Bar
+ let dynamic #redirecting_factory = foo::Bar::_ in invalid-expression;
+ }
+ class Baz extends fra::Widget /*hasConstConstructor*/ {
+ static field dynamic _redirecting# = <dynamic>[foo::Baz::_]/*isNullableByDefault*/;
+ const constructor __({wid::_Location? $creationLocationd_0dea112b090073317d4}) → foo::Baz
+ : super fra::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
+ ;
+ static factory •({wid::_Location? $creationLocationd_0dea112b090073317d4}) → foo::Baz
+ return #C10;
+ static factory _({wid::_Location? $creationLocationd_0dea112b090073317d4}) → foo::Baz
+ let dynamic #redirecting_factory = foo::Baz::__ in invalid-expression;
+ }
+ class Boz extends fra::Widget /*hasConstConstructor*/ {
+ const constructor _({wid::_Location? $creationLocationd_0dea112b090073317d4}) → foo::Boz
+ : super fra::Widget::•($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4)
+ ;
+ static factory •({dart.core::bool createNew = #C1, wid::_Location? $creationLocationd_0dea112b090073317d4}) → foo::Boz {
+ if(createNew) {
+ return new foo::Boz::_($creationLocationd_0dea112b090073317d4: $creationLocationd_0dea112b090073317d4);
+ }
+ else {
+ return #C14;
+ }
+ }
+ }
+}
+library from "org-dartlang-test:///main.dart" as main {
+
+ import "org-dartlang-test:///foo.dart";
+
+ static field foo::Foo newFoo = foo::Foo::•($creationLocationd_0dea112b090073317d4: #C17);
+ static field foo::Bar newBar = new foo::Bar::_($creationLocationd_0dea112b090073317d4: #C19);
+ static field foo::Bar constBar = #C23;
+ static field foo::Baz newBaz = foo::Baz::•($creationLocationd_0dea112b090073317d4: #C25);
+ static field foo::Boz newBoz = foo::Boz::•(createNew: true, $creationLocationd_0dea112b090073317d4: #C29);
+ static field foo::Boz constBoz = foo::Boz::•(createNew: false, $creationLocationd_0dea112b090073317d4: #C35);
+}
+constants {
+ #C1 = null
+ #C2 = "org-dartlang-test:///foo.dart"
+ #C3 = 2.0
+ #C4 = 51.0
+ #C5 = <wid::_Location*>[]
+ #C6 = wid::_Location {file:#C2, line:#C3, column:#C4, name:#C1, parameterLocations:#C5}
+ #C7 = foo::Foo {_location:#C6}
+ #C8 = 6.0
+ #C9 = wid::_Location {file:#C2, line:#C8, column:#C4, name:#C1, parameterLocations:#C5}
+ #C10 = foo::Baz {_location:#C9}
+ #C11 = 9.0
+ #C12 = 119.0
+ #C13 = wid::_Location {file:#C2, line:#C11, column:#C12, name:#C1, parameterLocations:#C5}
+ #C14 = foo::Boz {_location:#C13}
+ #C15 = "org-dartlang-test:///main.dart"
+ #C16 = 18.0
+ #C17 = wid::_Location {file:#C15, line:#C3, column:#C16, name:#C1, parameterLocations:#C5}
+ #C18 = 3.0
+ #C19 = wid::_Location {file:#C15, line:#C18, column:#C16, name:#C1, parameterLocations:#C5}
+ #C20 = 4.0
+ #C21 = 22.0
+ #C22 = wid::_Location {file:#C15, line:#C20, column:#C21, name:#C1, parameterLocations:#C5}
+ #C23 = foo::Bar {_location:#C22}
+ #C24 = 5.0
+ #C25 = wid::_Location {file:#C15, line:#C24, column:#C16, name:#C1, parameterLocations:#C5}
+ #C26 = "createNew"
+ #C27 = wid::_Location {file:#C1, line:#C8, column:#C21, name:#C26, parameterLocations:#C1}
+ #C28 = <wid::_Location*>[#C27]
+ #C29 = wid::_Location {file:#C15, line:#C8, column:#C16, name:#C1, parameterLocations:#C28}
+ #C30 = 7.0
+ #C31 = 20.0
+ #C32 = 24.0
+ #C33 = wid::_Location {file:#C1, line:#C30, column:#C32, name:#C26, parameterLocations:#C1}
+ #C34 = <wid::_Location*>[#C33]
+ #C35 = wid::_Location {file:#C15, line:#C30, column:#C31, name:#C1, parameterLocations:#C34}
+}
diff --git a/pkg/front_end/testcases/nnbd/infer_method_types.dart.outline.expect b/pkg/front_end/testcases/nnbd/infer_method_types.dart.outline.expect
index f0f39bc4..fa99f33 100644
--- a/pkg/front_end/testcases/nnbd/infer_method_types.dart.outline.expect
+++ b/pkg/front_end/testcases/nnbd/infer_method_types.dart.outline.expect
@@ -40,7 +40,7 @@
abstract class H extends core::Object implements self::D, self::E, self::F, self::C {
synthetic constructor •() → self::H
;
- abstract forwarding-stub member-signature method m(covariant core::num a) → core::Object?;
+ abstract forwarding-stub method m(covariant core::num a) → core::Object?;
}
abstract class I extends core::Object implements self::D {
synthetic constructor •() → self::I
@@ -55,7 +55,7 @@
abstract class K extends core::Object implements self::I, self::E, self::G {
synthetic constructor •() → self::K
;
- abstract forwarding-stub member-signature method m(covariant core::num a) → core::Object?;
+ abstract forwarding-stub method m(covariant core::num a) → core::Object?;
}
abstract class L extends core::Object implements self::K {
synthetic constructor •() → self::L
diff --git a/pkg/front_end/testcases/nnbd/infer_method_types.dart.strong.expect b/pkg/front_end/testcases/nnbd/infer_method_types.dart.strong.expect
index 85e72a0..5bd5e36 100644
--- a/pkg/front_end/testcases/nnbd/infer_method_types.dart.strong.expect
+++ b/pkg/front_end/testcases/nnbd/infer_method_types.dart.strong.expect
@@ -48,7 +48,7 @@
synthetic constructor •() → self::H
: super core::Object::•()
;
- abstract forwarding-stub member-signature method m(covariant core::num a) → core::Object?;
+ abstract forwarding-stub method m(covariant core::num a) → core::Object?;
}
abstract class I extends core::Object implements self::D {
synthetic constructor •() → self::I
@@ -66,7 +66,7 @@
synthetic constructor •() → self::K
: super core::Object::•()
;
- abstract forwarding-stub member-signature method m(covariant core::num a) → core::Object?;
+ abstract forwarding-stub method m(covariant core::num a) → core::Object?;
}
abstract class L extends core::Object implements self::K {
synthetic constructor •() → self::L
diff --git a/pkg/front_end/testcases/nnbd/infer_method_types.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/infer_method_types.dart.strong.transformed.expect
index 85e72a0..5bd5e36 100644
--- a/pkg/front_end/testcases/nnbd/infer_method_types.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/infer_method_types.dart.strong.transformed.expect
@@ -48,7 +48,7 @@
synthetic constructor •() → self::H
: super core::Object::•()
;
- abstract forwarding-stub member-signature method m(covariant core::num a) → core::Object?;
+ abstract forwarding-stub method m(covariant core::num a) → core::Object?;
}
abstract class I extends core::Object implements self::D {
synthetic constructor •() → self::I
@@ -66,7 +66,7 @@
synthetic constructor •() → self::K
: super core::Object::•()
;
- abstract forwarding-stub member-signature method m(covariant core::num a) → core::Object?;
+ abstract forwarding-stub method m(covariant core::num a) → core::Object?;
}
abstract class L extends core::Object implements self::K {
synthetic constructor •() → self::L
diff --git a/pkg/front_end/testcases/nnbd/infer_method_types.dart.weak.expect b/pkg/front_end/testcases/nnbd/infer_method_types.dart.weak.expect
index 85e72a0..5bd5e36 100644
--- a/pkg/front_end/testcases/nnbd/infer_method_types.dart.weak.expect
+++ b/pkg/front_end/testcases/nnbd/infer_method_types.dart.weak.expect
@@ -48,7 +48,7 @@
synthetic constructor •() → self::H
: super core::Object::•()
;
- abstract forwarding-stub member-signature method m(covariant core::num a) → core::Object?;
+ abstract forwarding-stub method m(covariant core::num a) → core::Object?;
}
abstract class I extends core::Object implements self::D {
synthetic constructor •() → self::I
@@ -66,7 +66,7 @@
synthetic constructor •() → self::K
: super core::Object::•()
;
- abstract forwarding-stub member-signature method m(covariant core::num a) → core::Object?;
+ abstract forwarding-stub method m(covariant core::num a) → core::Object?;
}
abstract class L extends core::Object implements self::K {
synthetic constructor •() → self::L
diff --git a/pkg/front_end/testcases/nnbd/infer_method_types.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/infer_method_types.dart.weak.transformed.expect
index 85e72a0..5bd5e36 100644
--- a/pkg/front_end/testcases/nnbd/infer_method_types.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/infer_method_types.dart.weak.transformed.expect
@@ -48,7 +48,7 @@
synthetic constructor •() → self::H
: super core::Object::•()
;
- abstract forwarding-stub member-signature method m(covariant core::num a) → core::Object?;
+ abstract forwarding-stub method m(covariant core::num a) → core::Object?;
}
abstract class I extends core::Object implements self::D {
synthetic constructor •() → self::I
@@ -66,7 +66,7 @@
synthetic constructor •() → self::K
: super core::Object::•()
;
- abstract forwarding-stub member-signature method m(covariant core::num a) → core::Object?;
+ abstract forwarding-stub method m(covariant core::num a) → core::Object?;
}
abstract class L extends core::Object implements self::K {
synthetic constructor •() → self::L
diff --git a/pkg/front_end/testcases/nnbd/issue42579.dart.textual_outline.expect b/pkg/front_end/testcases/nnbd/issue42579.dart.textual_outline.expect
new file mode 100644
index 0000000..8c9bcff
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue42579.dart.textual_outline.expect
@@ -0,0 +1,6 @@
+class A<T> {}
+
+S foo<S>() => throw "foo";
+bar<R>(A<R> a) {}
+baz() => bar(foo());
+main() {}
diff --git a/pkg/front_end/testcases/nnbd/issue42579.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/nnbd/issue42579.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..3e6dfbe
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue42579.dart.textual_outline_modelled.expect
@@ -0,0 +1,7 @@
+S foo<S>() => throw "foo";
+bar<R>(A<R> a) {}
+baz() => bar(foo());
+
+class A<T> {}
+
+main() {}
diff --git a/pkg/front_end/testcases/nnbd/issue42579_2.dart.textual_outline.expect b/pkg/front_end/testcases/nnbd/issue42579_2.dart.textual_outline.expect
new file mode 100644
index 0000000..60af131
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue42579_2.dart.textual_outline.expect
@@ -0,0 +1,9 @@
+class A<T> {
+ T get property => throw "A.property";
+}
+
+S foo<S>() => throw "foo";
+wrap<R>(R Function() f) {}
+wrap2<R>(A<R> Function() f) {}
+bar() {}
+main() {}
diff --git a/pkg/front_end/testcases/nnbd/issue42579_2.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/nnbd/issue42579_2.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..5cdcfec
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue42579_2.dart.textual_outline_modelled.expect
@@ -0,0 +1,10 @@
+S foo<S>() => throw "foo";
+bar() {}
+
+class A<T> {
+ T get property => throw "A.property";
+}
+
+main() {}
+wrap2<R>(A<R> Function() f) {}
+wrap<R>(R Function() f) {}
diff --git a/pkg/front_end/testcases/nnbd/issue42579_3.dart.textual_outline.expect b/pkg/front_end/testcases/nnbd/issue42579_3.dart.textual_outline.expect
new file mode 100644
index 0000000..743ca12
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue42579_3.dart.textual_outline.expect
@@ -0,0 +1,11 @@
+class A<X> {}
+
+class B<Y1, Y2 extends List<Y3>, Y3> extends A<Y1> {
+ Y1 get y1 => throw "B.y1";
+ Y2 get y2 => throw "B.y2";
+ Y3 get y3 => throw "B.y3";
+}
+
+foo<Z>(A<List<Z>> Function() f) {}
+bar() {}
+main() {}
diff --git a/pkg/front_end/testcases/nnbd/issue42579_3.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/nnbd/issue42579_3.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..0a7a426
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue42579_3.dart.textual_outline_modelled.expect
@@ -0,0 +1,12 @@
+bar() {}
+
+class A<X> {}
+
+class B<Y1, Y2 extends List<Y3>, Y3> extends A<Y1> {
+ Y1 get y1 => throw "B.y1";
+ Y2 get y2 => throw "B.y2";
+ Y3 get y3 => throw "B.y3";
+}
+
+foo<Z>(A<List<Z>> Function() f) {}
+main() {}
diff --git a/pkg/front_end/testcases/nnbd/upper_bound_on_promoted_type.dart.textual_outline.expect b/pkg/front_end/testcases/nnbd/upper_bound_on_promoted_type.dart.textual_outline.expect
new file mode 100644
index 0000000..39f0511
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/upper_bound_on_promoted_type.dart.textual_outline.expect
@@ -0,0 +1,7 @@
+class Foo<T> {
+ dynamic bar;
+ dynamic baz;
+ dynamic qux() {}
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/nnbd/upper_bound_on_promoted_type.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/nnbd/upper_bound_on_promoted_type.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..39f0511
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/upper_bound_on_promoted_type.dart.textual_outline_modelled.expect
@@ -0,0 +1,7 @@
+class Foo<T> {
+ dynamic bar;
+ dynamic baz;
+ dynamic qux() {}
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/regress/issue_41265.crash_dart.textual_outline.expect b/pkg/front_end/testcases/regress/issue_41265.crash_dart.textual_outline.expect
index 0ebe3c9..49f2d7d 100644
--- a/pkg/front_end/testcases/regress/issue_41265.crash_dart.textual_outline.expect
+++ b/pkg/front_end/testcases/regress/issue_41265.crash_dart.textual_outline.expect
@@ -1 +1,13 @@
-class A<T> {} mixin M<T> {} class DND1 extends Object with M<dynamic> Function()> { } class DND2 extends Object with M<dynamic> Function() { } class DND3 extends M<dynamic> Function() { } class DND4 implements M<dynamic> Function() { } main() { }
+class A<T> {
+}
+mixin M<T> {
+}
+class DND1 extends Object with M<dynamic> Function()> {
+}
+class DND2 extends Object with M<dynamic> Function() {
+}
+class DND3 extends M<dynamic> Function() {
+}
+class DND4 implements M<dynamic> Function() {
+}
+main() { }
diff --git a/pkg/front_end/testcases/textual_outline.status b/pkg/front_end/testcases/textual_outline.status
index 6603dc9..138700b 100644
--- a/pkg/front_end/testcases/textual_outline.status
+++ b/pkg/front_end/testcases/textual_outline.status
@@ -20,9 +20,6 @@
regress/issue_39091_2: EmptyOutput
regress/utf_16_le_content.crash: EmptyOutput
-rasta/issue_000032: FormatterCrash
-regress/ambiguous_builder_01: FormatterCrash
-regress/issue_34614: FormatterCrash
extensions/ambiguous: FormatterCrash
extensions/annotations: FormatterCrash
extensions/builtin_identifiers: FormatterCrash
@@ -99,7 +96,6 @@
general/bug31124: FormatterCrash
general/clone_function_type: FormatterCrash
general/constructor_initializer_invalid: FormatterCrash
-general/covariant_field: FormatterCrash
general/duplicated_declarations: FormatterCrash
general/error_recovery/constructor_recovery_bad_name_general.crash: FormatterCrash
general/error_recovery/constructor_recovery_bad_name_get.crash: FormatterCrash
@@ -120,7 +116,9 @@
general/invalid_operator: FormatterCrash
general/invalid_operator2: FormatterCrash
general/issue40242: FormatterCrash
+general/issue42997: FormatterCrash
general/many_errors: FormatterCrash
+general/null_safety_invalid_experiment_and_language_version: FormatterCrash
general/type_parameter_usage_in_static_method_in_extension: FormatterCrash
general/type_parameters_on_void: FormatterCrash
general/var_as_type_name: FormatterCrash
@@ -157,8 +155,21 @@
late_lowering/override_getter_setter: FormatterCrash
late_lowering/override: FormatterCrash
late_lowering/uninitialized_non_nullable_late_fields: FormatterCrash
+nnbd_mixed/inheritance_from_opt_in: FormatterCrash
+nnbd_mixed/issue41597: FormatterCrash
+nnbd_mixed/no_null_shorting_explicit_extension: FormatterCrash
+nnbd_mixed/no_null_shorting_extension: FormatterCrash
+nnbd_mixed/null_safety_invalid_language_version: FormatterCrash
+nnbd_mixed/nullable_extension_on_opt_out: FormatterCrash
+nnbd_mixed/opt_out: FormatterCrash
+nnbd/abstract_field_errors: FormatterCrash
+nnbd/abstract_fields_spec: FormatterCrash
+nnbd/abstract_fields: FormatterCrash
nnbd/covariant_late_field: FormatterCrash
nnbd/extension_never: FormatterCrash
+nnbd/external_field_errors: FormatterCrash
+nnbd/external_fields_spec: FormatterCrash
+nnbd/external_fields: FormatterCrash
nnbd/forbidden_supers: FormatterCrash
nnbd/infer_if_null: FormatterCrash
nnbd/inheritance_from_opt_in: FormatterCrash
@@ -174,6 +185,8 @@
nnbd/null_shorting_explicit_extension: FormatterCrash
nnbd/null_shorting_extension: FormatterCrash
nnbd/null_shorting_index: FormatterCrash
+nnbd/nullable_extension: FormatterCrash
+nnbd/nullable_setter: FormatterCrash
nnbd/opt_out: FormatterCrash
nnbd/potentially_non_nullable_field: FormatterCrash
nnbd/potentially_nullable_access: FormatterCrash
@@ -181,6 +194,7 @@
nnbd/uninitialized_non_nullable_late_fields: FormatterCrash
nonfunction_type_aliases/issue41501: FormatterCrash
rasta/bad_redirection: FormatterCrash
+rasta/issue_000032: FormatterCrash
rasta/issue_000034: FormatterCrash
rasta/issue_000036: FormatterCrash
rasta/issue_000044: FormatterCrash
@@ -188,6 +202,7 @@
rasta/issue_000047: FormatterCrash
rasta/malformed_const_constructor: FormatterCrash
rasta/mandatory_parameter_initializer: FormatterCrash
+regress/ambiguous_builder_01: FormatterCrash
regress/issue_29942: FormatterCrash
regress/issue_29944: FormatterCrash
regress/issue_29983: FormatterCrash
@@ -204,12 +219,14 @@
regress/issue_34225: FormatterCrash
regress/issue_34563: FormatterCrash
regress/issue_34610: FormatterCrash
+regress/issue_34614: FormatterCrash
regress/issue_34850: FormatterCrash
regress/issue_35151: FormatterCrash
regress/issue_36400: FormatterCrash
regress/issue_37285: FormatterCrash
regress/issue_39091_1: FormatterCrash
regress/issue_41265.crash: Crash
+regress/issue_41265.crash: FormatterCrash
triple_shift/invalid_operator: FormatterCrash
variance/class_type_parameter_modifier: FormatterCrash
variance/generic_covariance_sound_variance: FormatterCrash
diff --git a/pkg/front_end/testcases/value_class/empty.dart.textual_outline.expect b/pkg/front_end/testcases/value_class/empty.dart.textual_outline.expect
new file mode 100644
index 0000000..5716b5d
--- /dev/null
+++ b/pkg/front_end/testcases/value_class/empty.dart.textual_outline.expect
@@ -0,0 +1,6 @@
+const String valueClass = "valueClass";
+
+@valueClass
+class EmptyClass {}
+
+main() {}
diff --git a/pkg/front_end/testcases/value_class/empty.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/value_class/empty.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..3137333
--- /dev/null
+++ b/pkg/front_end/testcases/value_class/empty.dart.textual_outline_modelled.expect
@@ -0,0 +1,5 @@
+@valueClass
+class EmptyClass {}
+
+const String valueClass = "valueClass";
+main() {}
diff --git a/pkg/front_end/testcases/value_class/explicit_mixin.dart.textual_outline.expect b/pkg/front_end/testcases/value_class/explicit_mixin.dart.textual_outline.expect
new file mode 100644
index 0000000..2b99804
--- /dev/null
+++ b/pkg/front_end/testcases/value_class/explicit_mixin.dart.textual_outline.expect
@@ -0,0 +1,14 @@
+const String valueClass = "valueClass";
+
+@valueClass
+class A {}
+
+class B {}
+
+class C {}
+
+class D = A with B;
+class E = B with A;
+@valueClass
+class F = B with C;
+main() {}
diff --git a/pkg/front_end/testcases/value_class/explicit_mixin.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/value_class/explicit_mixin.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..afefd47
--- /dev/null
+++ b/pkg/front_end/testcases/value_class/explicit_mixin.dart.textual_outline_modelled.expect
@@ -0,0 +1,13 @@
+@valueClass
+class A {}
+
+class B {}
+
+class C {}
+
+const String valueClass = "valueClass";
+class D = A with B;
+class E = B with A;
+@valueClass
+class F = B with C;
+main() {}
diff --git a/pkg/front_end/testcases/value_class/non_final_field_error.dart.textual_outline.expect b/pkg/front_end/testcases/value_class/non_final_field_error.dart.textual_outline.expect
new file mode 100644
index 0000000..2130873
--- /dev/null
+++ b/pkg/front_end/testcases/value_class/non_final_field_error.dart.textual_outline.expect
@@ -0,0 +1,8 @@
+const String valueClass = "valueClass";
+
+@valueClass
+class Animal {
+ int numLegs;
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/value_class/non_final_field_error.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/value_class/non_final_field_error.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..e9bb0a6
--- /dev/null
+++ b/pkg/front_end/testcases/value_class/non_final_field_error.dart.textual_outline_modelled.expect
@@ -0,0 +1,7 @@
+@valueClass
+class Animal {
+ int numLegs;
+}
+
+const String valueClass = "valueClass";
+main() {}
diff --git a/pkg/front_end/testcases/value_class/non_value_extends_value_error.dart.textual_outline.expect b/pkg/front_end/testcases/value_class/non_value_extends_value_error.dart.textual_outline.expect
new file mode 100644
index 0000000..91337d4
--- /dev/null
+++ b/pkg/front_end/testcases/value_class/non_value_extends_value_error.dart.textual_outline.expect
@@ -0,0 +1,10 @@
+const String valueClass = "valueClass";
+
+@valueClass
+class Animal {
+ final int numLegs;
+}
+
+class Cat extends Animal {}
+
+main() {}
diff --git a/pkg/front_end/testcases/value_class/non_value_extends_value_error.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/value_class/non_value_extends_value_error.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..693a088
--- /dev/null
+++ b/pkg/front_end/testcases/value_class/non_value_extends_value_error.dart.textual_outline_modelled.expect
@@ -0,0 +1,9 @@
+@valueClass
+class Animal {
+ final int numLegs;
+}
+
+class Cat extends Animal {}
+
+const String valueClass = "valueClass";
+main() {}
diff --git a/pkg/front_end/testcases/value_class/non_value_implements_value_error.dart.textual_outline.expect b/pkg/front_end/testcases/value_class/non_value_implements_value_error.dart.textual_outline.expect
new file mode 100644
index 0000000..a28b098
--- /dev/null
+++ b/pkg/front_end/testcases/value_class/non_value_implements_value_error.dart.textual_outline.expect
@@ -0,0 +1,12 @@
+const String valueClass = "valueClass";
+
+@valueClass
+class Animal {
+ final int numLegs;
+}
+
+class Cat implements Animal {
+ final int numLegs;
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/value_class/non_value_implements_value_error.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/value_class/non_value_implements_value_error.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..021adc2
--- /dev/null
+++ b/pkg/front_end/testcases/value_class/non_value_implements_value_error.dart.textual_outline_modelled.expect
@@ -0,0 +1,11 @@
+@valueClass
+class Animal {
+ final int numLegs;
+}
+
+class Cat implements Animal {
+ final int numLegs;
+}
+
+const String valueClass = "valueClass";
+main() {}
diff --git a/pkg/front_end/testcases/value_class/simple.dart.textual_outline.expect b/pkg/front_end/testcases/value_class/simple.dart.textual_outline.expect
new file mode 100644
index 0000000..5408535
--- /dev/null
+++ b/pkg/front_end/testcases/value_class/simple.dart.textual_outline.expect
@@ -0,0 +1,9 @@
+const String valueClass = "valueClass";
+
+@valueClass
+class Animal {
+ final int numLegs;
+}
+
+main() {}
+expect(expected, actual, [expectNull = false]) {}
diff --git a/pkg/front_end/testcases/value_class/simple.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/value_class/simple.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..dabb2ae
--- /dev/null
+++ b/pkg/front_end/testcases/value_class/simple.dart.textual_outline_modelled.expect
@@ -0,0 +1,8 @@
+@valueClass
+class Animal {
+ final int numLegs;
+}
+
+const String valueClass = "valueClass";
+expect(expected, actual, [expectNull = false]) {}
+main() {}
diff --git a/pkg/front_end/testcases/value_class/super_type.dart.textual_outline.expect b/pkg/front_end/testcases/value_class/super_type.dart.textual_outline.expect
new file mode 100644
index 0000000..08894f3
--- /dev/null
+++ b/pkg/front_end/testcases/value_class/super_type.dart.textual_outline.expect
@@ -0,0 +1,15 @@
+const String valueClass = "valueClass";
+
+@valueClass
+class Animal {
+ final int numLegs;
+}
+
+@valueClass
+class Cat implements Animal {
+ final int numLegs;
+ final int numWhiskers;
+}
+
+main() {}
+expect(expected, actual, [expectNull = false]) {}
diff --git a/pkg/front_end/testcases/value_class/super_type.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/value_class/super_type.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..33c1c4f
--- /dev/null
+++ b/pkg/front_end/testcases/value_class/super_type.dart.textual_outline_modelled.expect
@@ -0,0 +1,14 @@
+@valueClass
+class Animal {
+ final int numLegs;
+}
+
+@valueClass
+class Cat implements Animal {
+ final int numLegs;
+ final int numWhiskers;
+}
+
+const String valueClass = "valueClass";
+expect(expected, actual, [expectNull = false]) {}
+main() {}
diff --git a/pkg/front_end/testcases/value_class/value_extends_non_value.dart.textual_outline.expect b/pkg/front_end/testcases/value_class/value_extends_non_value.dart.textual_outline.expect
new file mode 100644
index 0000000..ec2d5db
--- /dev/null
+++ b/pkg/front_end/testcases/value_class/value_extends_non_value.dart.textual_outline.expect
@@ -0,0 +1,10 @@
+const String valueClass = "valueClass";
+
+class Animal {
+ final int numLegs;
+}
+
+@valueClass
+class Cat extends Animal {}
+
+main() {}
diff --git a/pkg/front_end/testcases/value_class/value_extends_non_value.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/value_class/value_extends_non_value.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..89b6414
--- /dev/null
+++ b/pkg/front_end/testcases/value_class/value_extends_non_value.dart.textual_outline_modelled.expect
@@ -0,0 +1,9 @@
+@valueClass
+class Cat extends Animal {}
+
+class Animal {
+ final int numLegs;
+}
+
+const String valueClass = "valueClass";
+main() {}
diff --git a/pkg/front_end/testcases/value_class/value_extends_non_value_error.dart.textual_outline.expect b/pkg/front_end/testcases/value_class/value_extends_non_value_error.dart.textual_outline.expect
new file mode 100644
index 0000000..5d5b409
--- /dev/null
+++ b/pkg/front_end/testcases/value_class/value_extends_non_value_error.dart.textual_outline.expect
@@ -0,0 +1,10 @@
+const String valueClass = "valueClass";
+
+class Animal {
+ int numLegs;
+}
+
+@valueClass
+class Cat extends Animal {}
+
+main() {}
diff --git a/pkg/front_end/testcases/value_class/value_extends_non_value_error.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/value_class/value_extends_non_value_error.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..9ee23d7
--- /dev/null
+++ b/pkg/front_end/testcases/value_class/value_extends_non_value_error.dart.textual_outline_modelled.expect
@@ -0,0 +1,9 @@
+@valueClass
+class Cat extends Animal {}
+
+class Animal {
+ int numLegs;
+}
+
+const String valueClass = "valueClass";
+main() {}
diff --git a/pkg/front_end/testcases/value_class/value_implements_non_value.dart.textual_outline.expect b/pkg/front_end/testcases/value_class/value_implements_non_value.dart.textual_outline.expect
new file mode 100644
index 0000000..c0ada4f
--- /dev/null
+++ b/pkg/front_end/testcases/value_class/value_implements_non_value.dart.textual_outline.expect
@@ -0,0 +1,12 @@
+const String valueClass = "valueClass";
+
+class Animal {
+ final int numLegs;
+}
+
+@valueClass
+class Cat implements Animal {
+ final int numLegs;
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/value_class/value_implements_non_value.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/value_class/value_implements_non_value.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..ebcb028
--- /dev/null
+++ b/pkg/front_end/testcases/value_class/value_implements_non_value.dart.textual_outline_modelled.expect
@@ -0,0 +1,11 @@
+@valueClass
+class Cat implements Animal {
+ final int numLegs;
+}
+
+class Animal {
+ final int numLegs;
+}
+
+const String valueClass = "valueClass";
+main() {}
diff --git a/pkg/front_end/testcases/value_class/value_mixin_error.dart.textual_outline.expect b/pkg/front_end/testcases/value_class/value_mixin_error.dart.textual_outline.expect
new file mode 100644
index 0000000..c195f72
--- /dev/null
+++ b/pkg/front_end/testcases/value_class/value_mixin_error.dart.textual_outline.expect
@@ -0,0 +1,12 @@
+const String valueClass = "valueClass";
+
+@valueClass
+class A {}
+
+class B {}
+
+class C extends B with A {}
+
+class C extends A with B {}
+
+main() {}
diff --git a/pkg/front_end/testcases/value_class/value_mixin_error.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/value_class/value_mixin_error.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..17460dc
--- /dev/null
+++ b/pkg/front_end/testcases/value_class/value_mixin_error.dart.textual_outline_modelled.expect
@@ -0,0 +1,11 @@
+@valueClass
+class A {}
+
+class B {}
+
+class C extends A with B {}
+
+class C extends B with A {}
+
+const String valueClass = "valueClass";
+main() {}
diff --git a/pkg/kernel/lib/binary/ast_to_binary.dart b/pkg/kernel/lib/binary/ast_to_binary.dart
index 7654469..481479e 100644
--- a/pkg/kernel/lib/binary/ast_to_binary.dart
+++ b/pkg/kernel/lib/binary/ast_to_binary.dart
@@ -1191,6 +1191,19 @@
!(node.isMemberSignature &&
node.memberSignatureOriginReference == null),
"No member signature origin for member signature $node.");
+ assert(!(node.isMemberSignature && node.isForwardingStub),
+ "Procedure is both member signature and forwarding stub: $node.");
+ assert(!(node.isMemberSignature && node.isForwardingSemiStub),
+ "Procedure is both member signature and forwarding semi stub: $node.");
+ assert(
+ !(node.forwardingStubInterfaceTarget is Procedure &&
+ (node.forwardingStubInterfaceTarget as Procedure)
+ .isMemberSignature),
+ "Forwarding stub interface target is member signature: $node.");
+ assert(
+ !(node.forwardingStubSuperTarget is Procedure &&
+ (node.forwardingStubSuperTarget as Procedure).isMemberSignature),
+ "Forwarding stub super target is member signature: $node.");
procedureOffsets.add(getBufferOffset());
diff --git a/pkg/kernel/lib/transformations/track_widget_constructor_locations.dart b/pkg/kernel/lib/transformations/track_widget_constructor_locations.dart
index ec8c8c8..5e18e6f 100644
--- a/pkg/kernel/lib/transformations/track_widget_constructor_locations.dart
+++ b/pkg/kernel/lib/transformations/track_widget_constructor_locations.dart
@@ -209,16 +209,18 @@
return node;
}
- _addLocationArgument(node, target.function, constructedClass);
+ _addLocationArgument(node, target.function, constructedClass,
+ isConst: node.isConst);
return node;
}
- void _addLocationArgument(InvocationExpression node, FunctionNode function,
- Class constructedClass) {
+ void _addLocationArgument(
+ InvocationExpression node, FunctionNode function, Class constructedClass,
+ {bool isConst: false}) {
_maybeAddCreationLocationArgument(
node.arguments,
function,
- _computeLocation(node, function, constructedClass),
+ _computeLocation(node, function, constructedClass, isConst: isConst),
_locationClass,
);
}
@@ -233,17 +235,23 @@
return node;
}
- _addLocationArgument(node, constructor.function, constructedClass);
+ _addLocationArgument(node, constructor.function, constructedClass,
+ isConst: node.isConst);
return node;
}
- Expression _computeLocation(InvocationExpression node, FunctionNode function,
- Class constructedClass) {
+ Expression _computeLocation(
+ InvocationExpression node, FunctionNode function, Class constructedClass,
+ {bool isConst: false}) {
// For factory constructors we need to use the location specified as an
// argument to the factory constructor rather than the location
if (_currentFactory != null &&
_tracker._isSubclassOf(
- constructedClass, _currentFactory.enclosingClass)) {
+ constructedClass, _currentFactory.enclosingClass) &&
+ // If the constructor invocation is constant we cannot refer to the
+ // location parameter of the surrounding factory since it isn't a
+ // constant expression.
+ !isConst) {
final VariableDeclaration creationLocationParameter = _getNamedParameter(
_currentFactory.function,
_creationLocationParameterName,
diff --git a/pkg/nnbd_migration/lib/src/decorated_type_operations.dart b/pkg/nnbd_migration/lib/src/decorated_type_operations.dart
index 1ff9b47..05b2e5f 100644
--- a/pkg/nnbd_migration/lib/src/decorated_type_operations.dart
+++ b/pkg/nnbd_migration/lib/src/decorated_type_operations.dart
@@ -21,6 +21,15 @@
this._typeSystem, this._variableRepository, this._graph);
@override
+ TypeClassification classifyType(DecoratedType type) {
+ if (type.type.isDartCoreNull) {
+ return TypeClassification.nullOrEquivalent;
+ } else {
+ return TypeClassification.potentiallyNullable;
+ }
+ }
+
+ @override
DecoratedType factor(DecoratedType from, DecoratedType what) {
// TODO(scheglov): https://github.com/dart-lang/sdk/issues/41672
return from;
diff --git a/pkg/nnbd_migration/lib/src/edge_builder.dart b/pkg/nnbd_migration/lib/src/edge_builder.dart
index 8011686..af3f31d 100644
--- a/pkg/nnbd_migration/lib/src/edge_builder.dart
+++ b/pkg/nnbd_migration/lib/src/edge_builder.dart
@@ -414,11 +414,12 @@
if (operatorType == TokenType.EQ_EQ || operatorType == TokenType.BANG_EQ) {
var leftType = _dispatch(leftOperand);
_graph.connectDummy(leftType.node, DummyOrigin(source, node));
- _flowAnalysis.equalityOp_rightBegin(leftOperand);
+ _flowAnalysis.equalityOp_rightBegin(leftOperand, leftType);
var rightType = _dispatch(rightOperand);
_graph.connectDummy(rightType.node, DummyOrigin(source, node));
bool notEqual = operatorType == TokenType.BANG_EQ;
- _flowAnalysis.equalityOp_end(node, rightOperand, notEqual: notEqual);
+ _flowAnalysis.equalityOp_end(node, rightOperand, rightType,
+ notEqual: notEqual);
void buildNullConditionInfo(NullLiteral nullLiteral,
Expression otherOperand, NullabilityNode otherNode) {
diff --git a/pkg/vm_service/test/async_single_step_exception_test.dart b/pkg/vm_service/test/async_single_step_exception_test.dart
index 59c4a209..189196e 100644
--- a/pkg/vm_service/test/async_single_step_exception_test.dart
+++ b/pkg/vm_service/test/async_single_step_exception_test.dart
@@ -45,12 +45,6 @@
stoppedAtLine(LINE_D), // await helper
stepInto,
- ...ifLazyAsyncStacks(<IsolateTest>[
- hasStoppedAtBreakpoint,
- stoppedAtLine(19), // helper() async { ... }
- stepInto,
- ]),
-
hasStoppedAtBreakpoint,
stoppedAtLine(LINE_A), // print helper
smartNext,
diff --git a/pkg/vm_service/test/async_single_step_into_test.dart b/pkg/vm_service/test/async_single_step_into_test.dart
index 8f7373f..90be6df 100644
--- a/pkg/vm_service/test/async_single_step_into_test.dart
+++ b/pkg/vm_service/test/async_single_step_into_test.dart
@@ -34,12 +34,6 @@
stoppedAtLine(LINE_D),
stepInto,
- ...ifLazyAsyncStacks(<IsolateTest>[
- hasStoppedAtBreakpoint,
- stoppedAtLine(16), // helper() async { ... }
- stepInto,
- ]),
-
hasStoppedAtBreakpoint,
stoppedAtLine(LINE_A),
stepOver, // print.
diff --git a/pkg/vm_service/test/async_single_step_out_test.dart b/pkg/vm_service/test/async_single_step_out_test.dart
index 69ed577..fe8baed 100644
--- a/pkg/vm_service/test/async_single_step_out_test.dart
+++ b/pkg/vm_service/test/async_single_step_out_test.dart
@@ -35,12 +35,6 @@
stoppedAtLine(LINE_D), // await helper
stepInto,
- ...ifLazyAsyncStacks(<IsolateTest>[
- hasStoppedAtBreakpoint,
- stoppedAtLine(17), // helper() async { ... }
- stepInto,
- ]),
-
hasStoppedAtBreakpoint,
stoppedAtLine(LINE_A), // print.
stepOver,
diff --git a/pkg/vm_service/test/async_star_single_step_into_test.dart b/pkg/vm_service/test/async_star_single_step_into_test.dart
index 179b03d..061c2fd 100644
--- a/pkg/vm_service/test/async_star_single_step_into_test.dart
+++ b/pkg/vm_service/test/async_star_single_step_into_test.dart
@@ -45,12 +45,6 @@
stoppedAtLine(LINE_F),
stepInto,
- ...ifLazyAsyncStacks(<IsolateTest>[
- hasStoppedAtBreakpoint,
- stoppedAtLine(23), // helper() async { ... }
- stepInto,
- ]),
-
hasStoppedAtBreakpoint,
stoppedAtLine(LINE_C),
stepOver, // print.
@@ -58,12 +52,6 @@
hasStoppedAtBreakpoint,
stepInto,
- ...ifLazyAsyncStacks(<IsolateTest>[
- hasStoppedAtBreakpoint,
- stoppedAtLine(26), // await for (...) { ... }
- stepInto,
- ]),
-
hasStoppedAtBreakpoint,
stoppedAtLine(LINE_A),
// Resume here to exit the generator function.
diff --git a/pkg/vm_service/test/async_star_step_out_test.dart b/pkg/vm_service/test/async_star_step_out_test.dart
index 76e8e86..6e4cde9 100644
--- a/pkg/vm_service/test/async_star_step_out_test.dart
+++ b/pkg/vm_service/test/async_star_step_out_test.dart
@@ -49,12 +49,6 @@
stoppedAtLine(LINE_F),
stepInto,
- ...ifLazyAsyncStacks(<IsolateTest>[
- hasStoppedAtBreakpoint,
- stoppedAtLine(26), // helper() async { ... }
- stepInto,
- ]),
-
hasStoppedAtBreakpoint,
stoppedAtLine(LINE_C),
stepOver, // print.
@@ -62,12 +56,6 @@
hasStoppedAtBreakpoint,
stepInto,
- ...ifLazyAsyncStacks(<IsolateTest>[
- hasStoppedAtBreakpoint,
- stoppedAtLine(29), // await for (...) {}
- stepInto,
- ]),
-
hasStoppedAtBreakpoint,
stoppedAtLine(LINE_A),
stepOut, // step out of generator.
diff --git a/pkg/vm_service/test/async_step_out_test.dart b/pkg/vm_service/test/async_step_out_test.dart
index 8bbc4c8..3ccfb1d 100644
--- a/pkg/vm_service/test/async_step_out_test.dart
+++ b/pkg/vm_service/test/async_step_out_test.dart
@@ -37,12 +37,6 @@
stoppedAtLine(LINE_E),
stepInto,
- ...ifLazyAsyncStacks(<IsolateTest>[
- hasStoppedAtBreakpoint,
- stoppedAtLine(18), // helper() async { ... }
- stepInto,
- ]),
-
hasStoppedAtBreakpoint,
stoppedAtLine(LINE_A),
asyncNext,
diff --git a/pkg/vm_service/test/common/test_helper.dart b/pkg/vm_service/test/common/test_helper.dart
index f226cc0..1b19ba1 100644
--- a/pkg/vm_service/test/common/test_helper.dart
+++ b/pkg/vm_service/test/common/test_helper.dart
@@ -22,11 +22,6 @@
? ['--causal-async-stacks', '--no-lazy-async-stacks']
: ['--no-causal-async-stacks', '--lazy-async-stacks'];
-List<IsolateTest> ifLazyAsyncStacks(List<IsolateTest> lazyTests) {
- if (useCausalAsyncStacks) return const <IsolateTest>[];
- return lazyTests;
-}
-
/// Will be set to the http address of the VM's service protocol before
/// any tests are invoked.
String serviceHttpAddress;
@@ -222,13 +217,13 @@
first = false;
print('** Signaled to run test queries on $uri');
}
- print('>testee>out> $line');
+ stdout.write('>testee>out> ${line}\n');
});
process.stderr
.transform(utf8.decoder)
.transform(LineSplitter())
.listen((line) {
- print('>testee>err> $line');
+ stdout.write('>testee>err> ${line}\n');
});
process.exitCode.then((exitCode) {
if ((exitCode != 0) && !killedByTester) {
diff --git a/runtime/bin/ffi_test/ffi_test_functions.cc b/runtime/bin/ffi_test/ffi_test_functions.cc
index 1d7082e..3b915c0 100644
--- a/runtime/bin/ffi_test/ffi_test_functions.cc
+++ b/runtime/bin/ffi_test/ffi_test_functions.cc
@@ -1038,4 +1038,8 @@
p2[0] = 42;
}
+DART_EXPORT int32_t PassStruct(void*) {
+ return 42;
+}
+
} // namespace dart
diff --git a/runtime/observatory/tests/service/async_single_step_exception_test.dart b/runtime/observatory/tests/service/async_single_step_exception_test.dart
index 6393d32..fdc490e 100644
--- a/runtime/observatory/tests/service/async_single_step_exception_test.dart
+++ b/runtime/observatory/tests/service/async_single_step_exception_test.dart
@@ -46,12 +46,6 @@
stoppedAtLine(LINE_D), // await helper
stepInto,
- ...ifLazyAsyncStacks(<IsolateTest>[
- hasStoppedAtBreakpoint,
- stoppedAtLine(19), // helper() async { ... }
- stepInto,
- ]),
-
hasStoppedAtBreakpoint,
stoppedAtLine(LINE_A), // print helper
smartNext,
diff --git a/runtime/observatory/tests/service/async_single_step_into_test.dart b/runtime/observatory/tests/service/async_single_step_into_test.dart
index 2f67565..8d8417f 100644
--- a/runtime/observatory/tests/service/async_single_step_into_test.dart
+++ b/runtime/observatory/tests/service/async_single_step_into_test.dart
@@ -34,12 +34,6 @@
stoppedAtLine(LINE_D),
stepInto,
- ...ifLazyAsyncStacks(<IsolateTest>[
- hasStoppedAtBreakpoint,
- stoppedAtLine(16),
- stepInto, // helper() async { ... }
- ]),
-
hasStoppedAtBreakpoint,
stoppedAtLine(LINE_A),
stepOver, // print.
diff --git a/runtime/observatory/tests/service/async_single_step_out_test.dart b/runtime/observatory/tests/service/async_single_step_out_test.dart
index f5ba1d6..2b51e24 100644
--- a/runtime/observatory/tests/service/async_single_step_out_test.dart
+++ b/runtime/observatory/tests/service/async_single_step_out_test.dart
@@ -35,12 +35,6 @@
stoppedAtLine(LINE_D), // await helper
stepInto,
- ...ifLazyAsyncStacks(<IsolateTest>[
- hasStoppedAtBreakpoint,
- stoppedAtLine(17), // helper() async {}
- stepInto,
- ]),
-
hasStoppedAtBreakpoint,
stoppedAtLine(LINE_A), // print.
stepOver,
@@ -57,7 +51,7 @@
stoppedAtLine(25), // await helper (weird dispatching)
smartNext,
- hasStoppedAtBreakpoint, //19
+ hasStoppedAtBreakpoint,
stoppedAtLine(LINE_E), // arrive after the await.
resumeIsolate
];
diff --git a/runtime/observatory/tests/service/async_star_single_step_into_test.dart b/runtime/observatory/tests/service/async_star_single_step_into_test.dart
index 3a55fa0..65a784d 100644
--- a/runtime/observatory/tests/service/async_star_single_step_into_test.dart
+++ b/runtime/observatory/tests/service/async_star_single_step_into_test.dart
@@ -44,22 +44,10 @@
stoppedAtLine(LINE_F),
stepInto,
- ...ifLazyAsyncStacks(<IsolateTest>[
- hasStoppedAtBreakpoint,
- stoppedAtLine(23), // helper() async { ... }
- stepInto,
- ]),
-
hasStoppedAtBreakpoint,
stoppedAtLine(LINE_C),
stepOver, // print.
- ...ifLazyAsyncStacks(<IsolateTest>[
- hasStoppedAtBreakpoint,
- stoppedAtLine(25), // await for ...
- stepInto,
- ]),
-
hasStoppedAtBreakpoint,
stepInto,
diff --git a/runtime/observatory/tests/service/async_star_step_out_test.dart b/runtime/observatory/tests/service/async_star_step_out_test.dart
index 4f44917..fca9d16 100644
--- a/runtime/observatory/tests/service/async_star_step_out_test.dart
+++ b/runtime/observatory/tests/service/async_star_step_out_test.dart
@@ -48,12 +48,6 @@
stoppedAtLine(LINE_F),
stepInto,
- ...ifLazyAsyncStacks(<IsolateTest>[
- hasStoppedAtBreakpoint,
- stoppedAtLine(26), // helper() async {}
- stepInto,
- ]),
-
hasStoppedAtBreakpoint,
stoppedAtLine(LINE_C),
stepOver, // print.
@@ -61,12 +55,6 @@
hasStoppedAtBreakpoint,
stepInto,
- ...ifLazyAsyncStacks(<IsolateTest>[
- hasStoppedAtBreakpoint,
- stoppedAtLine(28), // await for ...
- stepInto,
- ]),
-
hasStoppedAtBreakpoint,
stoppedAtLine(LINE_A),
stepOut, // step out of generator.
diff --git a/runtime/observatory/tests/service/async_step_out_test.dart b/runtime/observatory/tests/service/async_step_out_test.dart
index f581cca..f22f8d8 100644
--- a/runtime/observatory/tests/service/async_step_out_test.dart
+++ b/runtime/observatory/tests/service/async_step_out_test.dart
@@ -37,12 +37,6 @@
stoppedAtLine(LINE_E),
stepInto,
- ...ifLazyAsyncStacks(<IsolateTest>[
- hasStoppedAtBreakpoint,
- stoppedAtLine(18), // helper() async { ... }
- stepInto,
- ]),
-
hasStoppedAtBreakpoint,
stoppedAtLine(LINE_A),
asyncNext,
diff --git a/runtime/observatory/tests/service/test_helper.dart b/runtime/observatory/tests/service/test_helper.dart
index 6b9bb3b..ecf4736 100644
--- a/runtime/observatory/tests/service/test_helper.dart
+++ b/runtime/observatory/tests/service/test_helper.dart
@@ -25,11 +25,6 @@
? const ['--causal-async-stacks', '--no-lazy-async-stacks']
: const ['--no-causal-async-stacks', '--lazy-async-stacks'];
-List<IsolateTest> ifLazyAsyncStacks(List<IsolateTest> lazyTests) {
- if (useCausalAsyncStacks) return const <IsolateTest>[];
- return lazyTests;
-}
-
/// Will be set to the http address of the VM's service protocol before
/// any tests are invoked.
String serviceHttpAddress;
@@ -286,7 +281,7 @@
.transform(utf8.decoder)
.transform(new LineSplitter())
.listen((line) {
- print('>testee>err> $line');
+ print('>testee>err> ${line.trim()}');
});
process.exitCode.then((exitCode) async {
await serviceInfoDir.delete(recursive: true);
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index 01bade0..2c505b8 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -4182,7 +4182,7 @@
Fragment otherwise_fragment(otherwise);
otherwise_fragment += IntConstant(condition_start_offset.Pos());
otherwise_fragment += IntConstant(condition_end_offset.Pos());
- Tag tag = ReadTag(); // read (first part of) message.
+ Tag tag = ReadTag(); // read (first part of) message.
if (tag == kSomething) {
otherwise_fragment += BuildExpression(); // read (rest of) message.
} else {
@@ -4748,8 +4748,8 @@
needs_stacktrace, is_synthetic);
// Fill in the body of the catch.
for (intptr_t i = 0; i < catch_count; ++i) {
- intptr_t catch_offset = ReaderOffset(); // Catch has no tag.
- TokenPosition position = ReadPosition(); // read position.
+ intptr_t catch_offset = ReaderOffset(); // Catch has no tag.
+ TokenPosition position = ReadPosition(); // read position.
const AbstractType& type_guard = T.BuildType(); // read guard.
handler_types.SetAt(i, type_guard);
@@ -5269,6 +5269,15 @@
Function::ZoneHandle(Z, compiler::ffi::NativeCallbackFunction(
native_sig, target, exceptional_return));
code += Constant(result);
+
+ auto& ffi_callback_functions = GrowableObjectArray::Handle(Z);
+ ffi_callback_functions ^= I->object_store()->ffi_callback_functions();
+ if (ffi_callback_functions.IsNull()) {
+ ffi_callback_functions ^= GrowableObjectArray::New();
+ I->object_store()->set_ffi_callback_functions(ffi_callback_functions);
+ }
+ ffi_callback_functions.Add(result);
+
return code;
}
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
index eaa2fbd..3669dc2 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -2207,6 +2207,8 @@
class StackTrace& async_stack_trace = StackTrace::Handle(zone);
Array& async_code_array = Array::Handle(zone);
Array& async_pc_offset_array = Array::Handle(zone);
+
+ // Extract the eagerly recorded async stack from the current thread.
StackTraceUtils::ExtractAsyncStackTraceInfo(
thread, &async_function, &async_stack_trace, &async_code_array,
&async_pc_offset_array);
@@ -3724,6 +3726,16 @@
return bpt_location->AddPerClosure(this, closure, for_over_await);
}
+Breakpoint* Debugger::SetBreakpointAtAsyncOp(const Function& async_op) {
+ const Script& script = Script::Handle(async_op.script());
+ BreakpointLocation* bpt_location =
+ SetBreakpoint(script, async_op.token_pos(), async_op.end_token_pos(), -1,
+ -1 /* no line/col */, async_op);
+ auto bpt = bpt_location->AddSingleShot(this);
+ bpt->set_is_synthetic_async(true);
+ return bpt;
+}
+
Breakpoint* Debugger::BreakpointAtActivation(const Instance& closure) {
if (!closure.IsClosure()) {
return NULL;
@@ -4458,6 +4470,37 @@
ActivationFrame* frame = TopDartFrame();
ASSERT(frame != NULL);
+ // Since lazy async stacks doesn't use the _asyncStackTraceHelper runtime
+ // entry, we need to manually set a synthetic breakpoint for async_op before
+ // we enter it.
+ if (FLAG_lazy_async_stacks) {
+ // async and async* functions always contain synthetic async_ops.
+ if ((frame->function().IsAsyncFunction() ||
+ frame->function().IsAsyncGenerator())) {
+ ASSERT(!frame->GetSavedCurrentContext().IsNull());
+ ASSERT(frame->GetSavedCurrentContext().num_variables() >
+ Context::kAsyncCompleterIndex);
+
+ const Object& jump_var = Object::Handle(
+ frame->GetSavedCurrentContext().At(Context::kAsyncCompleterIndex));
+
+ // Only set breakpoint when entering async_op the first time.
+ // :async_completer_var should be uninitialised at this point:
+ if (jump_var.IsNull()) {
+ const Function& async_op =
+ Function::Handle(frame->function().GetGeneratedClosure());
+ if (!async_op.IsNull()) {
+ SetBreakpointAtAsyncOp(async_op);
+ // After setting the breakpoint we stop stepping and continue the
+ // debugger until the next breakpoint, to step over all the
+ // synthetic code.
+ Continue();
+ return Error::null();
+ }
+ }
+ }
+ }
+
if (FLAG_async_debugger) {
if ((async_stepping_fp_ != 0) && (top_frame_awaiter_ != Object::null())) {
// Check if the user has single stepped out of an async function with
diff --git a/runtime/vm/debugger.h b/runtime/vm/debugger.h
index 881e492..ed3760d 100644
--- a/runtime/vm/debugger.h
+++ b/runtime/vm/debugger.h
@@ -519,6 +519,10 @@
intptr_t line_number,
intptr_t column_number);
+ // Sets synthetic breakpoint at async_op to step over the synthetic part of
+ // the stack trace.
+ Breakpoint* SetBreakpointAtAsyncOp(const Function& async_op);
+
BreakpointLocation* BreakpointLocationAtLineCol(const String& script_url,
intptr_t line_number,
intptr_t column_number);
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index c413708..20c9883 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -6969,6 +6969,26 @@
}
}
+FunctionPtr Function::GetGeneratedClosure() const {
+ const auto& closure_functions = GrowableObjectArray::Handle(
+ Isolate::Current()->object_store()->closure_functions());
+ auto& entry = Object::Handle();
+
+ for (auto i = (closure_functions.Length() - 1); i >= 0; i--) {
+ entry = closure_functions.At(i);
+
+ ASSERT(entry.IsFunction());
+
+ const auto& closure_function = Function::Cast(entry);
+ if (closure_function.parent_function() == raw() &&
+ closure_function.is_generated_body()) {
+ return closure_function.raw();
+ }
+ }
+
+ return Function::null();
+}
+
// Enclosing outermost function of this local function.
FunctionPtr Function::GetOutermostFunction() const {
FunctionPtr parent = parent_function();
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index cc26637..533eb68 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -2746,6 +2746,11 @@
// Enclosing function of this local function.
FunctionPtr parent_function() const;
+ // Enclosed generated closure function of this local function.
+ // This will only work after the closure function has been allocated in the
+ // isolate's object_store.
+ FunctionPtr GetGeneratedClosure() const;
+
// Enclosing outermost function of this local function.
FunctionPtr GetOutermostFunction() const;
diff --git a/runtime/vm/object_store.h b/runtime/vm/object_store.h
index 7f3c93e..52c75ed 100644
--- a/runtime/vm/object_store.h
+++ b/runtime/vm/object_store.h
@@ -216,6 +216,7 @@
RW(Array, dispatch_table_code_entries) \
RW(GrowableObjectArray, code_order_tables) \
RW(Array, obfuscation_map) \
+ RW(GrowableObjectArray, ffi_callback_functions) \
RW(Class, ffi_pointer_class) \
RW(Class, ffi_native_type_class) \
RW(Class, ffi_struct_class) \
diff --git a/runtime/vm/program_visitor.cc b/runtime/vm/program_visitor.cc
index b5f73c3..fe04547 100644
--- a/runtime/vm/program_visitor.cc
+++ b/runtime/vm/program_visitor.cc
@@ -261,6 +261,16 @@
walker.AddToWorklist(function);
ASSERT(!function.HasImplicitClosureFunction());
}
+ // TODO(dartbug.com/43049): Use a more general solution and remove manual
+ // tracking through object_store->ffi_callback_functions.
+ const auto& ffi_callback_entries = GrowableObjectArray::Handle(
+ zone, object_store->ffi_callback_functions());
+ if (!ffi_callback_entries.IsNull()) {
+ for (intptr_t i = 0; i < ffi_callback_entries.Length(); i++) {
+ function ^= ffi_callback_entries.At(i);
+ walker.AddToWorklist(function);
+ }
+ }
}
if (visitor->IsCodeVisitor()) {
@@ -382,7 +392,7 @@
// directly.
//
// In precompiled mode, the binder runs after tree shaking, during which
- // all targets have been compiled, and so the binder replace all static
+ // all targets have been compiled, and so the binder replaces all static
// calls with direct calls to the target.
//
// Cf. runtime entry PatchStaticCall called from CallStaticFunction
diff --git a/runtime/vm/stack_trace.cc b/runtime/vm/stack_trace.cc
index 468edfc..2ea19b7 100644
--- a/runtime/vm/stack_trace.cc
+++ b/runtime/vm/stack_trace.cc
@@ -395,6 +395,7 @@
CallerClosureFinder caller_closure_finder(zone);
auto& pc_descs = PcDescriptors::Handle();
+ // Start by traversing the sync. part of the stack.
for (; frame != nullptr; frame = frames.NextFrame()) {
if (skip_frames > 0) {
skip_frames--;
@@ -461,6 +462,7 @@
// Skip: Already handled this frame's function above.
closure = caller_closure_finder.FindCaller(closure);
+ // Traverse the trail of async futures all the way up.
for (; !closure.IsNull();
closure = caller_closure_finder.FindCaller(closure)) {
function = closure.function();
diff --git a/tests/co19/co19-co19.status b/tests/co19/co19-co19.status
index f4ff221..3934f21 100644
--- a/tests/co19/co19-co19.status
+++ b/tests/co19/co19-co19.status
@@ -114,11 +114,6 @@
LibTest/isolate/RawReceivePort/*: Skip # Not migrated to NNBD
LibTest/isolate/ReceivePort/*: Skip # Not migrated to NNBD
LibTest/isolate/SendPort/*: Skip # Not migrated to NNBD
-LibTest/math/*: Skip # Not migrated to NNBD
-LibTest/math/MutableRectangle/*: Skip # Not migrated to NNBD
-LibTest/math/Point/*: Skip # Not migrated to NNBD
-LibTest/math/Random/*: Skip # Not migrated to NNBD
-LibTest/math/Rectangle/*: Skip # Not migrated to NNBD
LibTest/mirrors/*: Skip # Not migrated to NNBD
LibTest/typed_data/*: Skip # Not migrated to NNBD
LibTest/typed_data/ByteBuffer/*: Skip # Not migrated to NNBD
diff --git a/tests/ffi/function_callbacks_test.dart b/tests/ffi/function_callbacks_test.dart
index 24ca064..2b12a98 100644
--- a/tests/ffi/function_callbacks_test.dart
+++ b/tests/ffi/function_callbacks_test.dart
@@ -12,6 +12,7 @@
// VMOptions=--use-slow-path --enable-testing-pragmas --stacktrace-every=100
// VMOptions=--use-slow-path --enable-testing-pragmas --write-protect-code --no-dual-map-code
// VMOptions=--use-slow-path --enable-testing-pragmas --write-protect-code --no-dual-map-code --stacktrace-every=100
+// VMOptions=--use-bare-instructions=false
// SharedObjects=ffi_test_functions
import 'dart:ffi';
diff --git a/tests/ffi/regress_43016_test.dart b/tests/ffi/regress_43016_test.dart
new file mode 100644
index 0000000..bb134bf
--- /dev/null
+++ b/tests/ffi/regress_43016_test.dart
@@ -0,0 +1,32 @@
+// 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.
+
+// VMOptions=--optimization-counter-threshold=5
+
+import 'dart:ffi';
+
+import 'dylib_utils.dart';
+
+class MyStruct extends Struct {}
+
+typedef _c_pass_struct = Int32 Function(Pointer<MyStruct>);
+
+typedef _dart_pass_struct = int Function(Pointer<MyStruct>);
+
+int pass_struct(Pointer<MyStruct> arg0) {
+ _pass_struct ??= ffiTestFunctions
+ .lookupFunction<_c_pass_struct, _dart_pass_struct>('PassStruct');
+ return _pass_struct!.call(arg0);
+}
+
+_dart_pass_struct? _pass_struct;
+
+final ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions");
+
+void main() {
+ // Trigger both the runtime entry and the IL in bytecode.
+ for (int i = 0; i < 10000; i++) {
+ pass_struct(nullptr);
+ }
+}
diff --git a/tests/ffi_2/function_callbacks_test.dart b/tests/ffi_2/function_callbacks_test.dart
index 24ca064..2b12a98 100644
--- a/tests/ffi_2/function_callbacks_test.dart
+++ b/tests/ffi_2/function_callbacks_test.dart
@@ -12,6 +12,7 @@
// VMOptions=--use-slow-path --enable-testing-pragmas --stacktrace-every=100
// VMOptions=--use-slow-path --enable-testing-pragmas --write-protect-code --no-dual-map-code
// VMOptions=--use-slow-path --enable-testing-pragmas --write-protect-code --no-dual-map-code --stacktrace-every=100
+// VMOptions=--use-bare-instructions=false
// SharedObjects=ffi_test_functions
import 'dart:ffi';
diff --git a/tests/ffi_2/regress_43016_test.dart b/tests/ffi_2/regress_43016_test.dart
new file mode 100644
index 0000000..005b1df
--- /dev/null
+++ b/tests/ffi_2/regress_43016_test.dart
@@ -0,0 +1,32 @@
+// 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.
+
+// VMOptions=--optimization-counter-threshold=5
+
+import 'dart:ffi';
+
+import 'dylib_utils.dart';
+
+class MyStruct extends Struct {}
+
+typedef _c_pass_struct = Int32 Function(Pointer<MyStruct> arg0);
+
+typedef _dart_pass_struct = int Function(Pointer<MyStruct> arg0);
+
+int pass_struct(Pointer<MyStruct> arg0) {
+ _pass_struct ??= ffiTestFunctions
+ .lookupFunction<_c_pass_struct, _dart_pass_struct>('PassStruct');
+ return _pass_struct(arg0);
+}
+
+_dart_pass_struct _pass_struct;
+
+final ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions");
+
+void main() {
+ // Trigger both the runtime entry and the IL in bytecode.
+ for (int i = 0; i < 10000; i++) {
+ pass_struct(nullptr);
+ }
+}
diff --git a/tests/language/nnbd/type_promotion/assignment_inference_after_null_check_test.dart b/tests/language/nnbd/type_promotion/assignment_inference_after_null_check_test.dart
new file mode 100644
index 0000000..220bfbe
--- /dev/null
+++ b/tests/language/nnbd/type_promotion/assignment_inference_after_null_check_test.dart
@@ -0,0 +1,25 @@
+// 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.
+
+// The reason that `if (x == null)` doesn't promote x's type to `Null` is
+// because we lose type argument information that would be necessary for
+// inference. This test makes sure that the type argument information is
+// appropriately preserved.
+
+// SharedOptions=--enable-experiment=non-nullable
+
+void f(List<int>? x) {
+ if (x == null) {
+ // If x were promoted to `Null`, inference would not know that `[]` should
+ // be considered to mean `<int>[]`, so either the line below would be a
+ // compile-time error, or a runtime error would occur later when we try to
+ // add an int to the list.
+ x = [];
+ }
+ x.add(0);
+}
+
+main() {
+ f(null);
+}
diff --git a/tests/language/nnbd/type_promotion/null_type_insufficient_for_equals_null_check_error_test.dart b/tests/language/nnbd/type_promotion/null_type_insufficient_for_equals_null_check_error_test.dart
new file mode 100644
index 0000000..30e882f
--- /dev/null
+++ b/tests/language/nnbd/type_promotion/null_type_insufficient_for_equals_null_check_error_test.dart
@@ -0,0 +1,61 @@
+// 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.
+
+// Given an expression E having type `Null`, the reason that `if (x != E)`
+// doesn't promote x's type to non-nullable is because evaluation of the
+// expression may change the value of `x`. (Consider, for example, if E is the
+// expression `(x = null)`). This test demonstrates the problem with `(x =
+// null)` and checks a few other cases.
+
+// SharedOptions=--enable-experiment=non-nullable
+
+void assignNullRhs(int? x) {
+ if (x != (x = null)) {
+ x.isEven;
+// ^
+// [analyzer] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
+ //^
+ // [cfe] Property 'isEven' cannot be accessed on 'int?' because it is potentially null.
+ }
+}
+
+void assignNullLhs(int? x) {
+ // In theory it would be sound to promote x in this case, because the
+ // assignment happens before the RHS is evaluated, but we prefer not to
+ // promote in order to be consistent with the `assignNullRhs` case.
+ if ((x = null) != x) {
+ x.isEven;
+// ^
+// [analyzer] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
+ //^
+ // [cfe] Property 'isEven' cannot be accessed on 'int?' because it is potentially null.
+ }
+}
+
+void unrelatedVarRhs(int? x, Null n) {
+ if (x != n) {
+ x.isEven;
+// ^
+// [analyzer] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
+ //^
+ // [cfe] Property 'isEven' cannot be accessed on 'int?' because it is potentially null.
+ }
+}
+
+void unrelatedVarLhs(int? x, Null n) {
+ if (n != x) {
+ x.isEven;
+// ^
+// [analyzer] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
+ //^
+ // [cfe] Property 'isEven' cannot be accessed on 'int?' because it is potentially null.
+ }
+}
+
+main() {
+ assignNullRhs(0);
+ assignNullLhs(0);
+ unrelatedVarLhs(0, null);
+ unrelatedVarRhs(0, null);
+}
diff --git a/tools/VERSION b/tools/VERSION
index 956d8fc..46dd6f8 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 10
PATCH 0
-PRERELEASE 28
+PRERELEASE 29
PRERELEASE_PATCH 0
\ No newline at end of file