Version 2.13.0-35.0.dev
Merge commit 'c2eb847697a34d7be5882385d27bdbd7a3e8a367' into 'dev'
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/nullable_error_scenarios.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/nullable_error_scenarios.dart
new file mode 100644
index 0000000..5797fa4
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/nullable_error_scenarios.dart
@@ -0,0 +1,82 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This test contains a test case for each kind of error that can arise from an
+// expression being nullable, for which we wish to report "why not promoted"
+// errors.
+
+class C {
+ int? i;
+ void Function()? f;
+}
+
+extension on int {
+ get propertyOnNonNullInt => null;
+ void methodOnNonNullInt() {}
+}
+
+extension on int? {
+ get propertyOnNullableInt => null;
+ void methodOnNullableInt() {}
+}
+
+property_get_of_variable(int? i, int? j) {
+ if (i == null) return;
+ /*cfe.update: explicitWrite*/ /*analyzer.explicitWrite*/ i = j;
+ i. /*notPromoted(explicitWrite)*/ isEven;
+}
+
+extension_property_get_of_variable(int? i, int? j) {
+ if (i == null) return;
+ /*cfe.update: explicitWrite*/ /*analyzer.explicitWrite*/ i = j;
+ i.propertyOnNullableInt;
+ i.
+ /*cfe.invoke: notPromoted(explicitWrite)*/
+ /*analyzer.notPromoted(explicitWrite)*/
+ propertyOnNonNullInt;
+}
+
+property_get_of_expression(C c) {
+ if (c.i == null) return;
+ c.i. /*notPromoted(propertyNotPromoted(member:C.i))*/ isEven;
+}
+
+extension_property_get_of_expression(C c) {
+ if (c.i == null) return;
+ c.i.propertyOnNullableInt;
+ c
+ .i
+ .
+ /*cfe.invoke: notPromoted(propertyNotPromoted(member:C.i))*/
+ /*analyzer.notPromoted(propertyNotPromoted(member:C.i))*/
+ propertyOnNonNullInt;
+}
+
+method_invocation(C c) {
+ if (c.i == null) return;
+ c.i
+ .
+ /*cfe.invoke: notPromoted(propertyNotPromoted(member:C.i))*/
+ /*analyzer.notPromoted(propertyNotPromoted(member:C.i))*/
+ abs();
+}
+
+extension_method_invocation(C c) {
+ if (c.i == null) return;
+ c.i.methodOnNullableInt();
+ c.i
+ .
+ /*cfe.invoke: notPromoted(propertyNotPromoted(member:C.i))*/
+ /*analyzer.notPromoted(propertyNotPromoted(member:C.i))*/
+ methodOnNonNullInt();
+}
+
+call_invocation(C c) {
+ if (c.f == null) return;
+ c.f
+ .
+ /*cfe.invoke: notPromoted(propertyNotPromoted(member:C.f))*/
+ /*analyzer.notPromoted(propertyNotPromoted(member:C.f))*/
+ call();
+}
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 81efd86..4ad0b97 100644
--- a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
@@ -5,13 +5,9 @@
// @dart = 2.9
import 'dart:core' hide MapEntry;
-import 'dart:core' as core;
-import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart';
-import 'package:_fe_analyzer_shared/src/testing/id.dart';
import 'package:_fe_analyzer_shared/src/util/link.dart';
import 'package:front_end/src/api_prototype/lowering_predicates.dart';
-import 'package:front_end/src/testing/id_extractor.dart';
import 'package:kernel/ast.dart'
hide Reference; // Work around https://github.com/dart-lang/sdk/issues/44667
import 'package:kernel/src/legacy_erasure.dart';
@@ -4739,36 +4735,13 @@
readResult ??= new ExpressionInferenceResult(readType, read);
if (!inferrer.isTopLevel && readTarget.isNullable) {
- Map<DartType, NonPromotionReason> whyNotPromoted =
- inferrer.flowAnalysis?.whyNotPromoted(receiver);
- List<LocatedMessage> context;
- if (whyNotPromoted != null && whyNotPromoted.isNotEmpty) {
- _WhyNotPromotedVisitor whyNotPromotedVisitor =
- new _WhyNotPromotedVisitor(inferrer, receiver);
- for (core.MapEntry<DartType, NonPromotionReason> entry
- in whyNotPromoted.entries) {
- if (entry.key.isPotentiallyNullable) continue;
- LocatedMessage message = entry.value.accept(whyNotPromotedVisitor);
- if (inferrer.dataForTesting != null) {
- String nonPromotionReasonText = entry.value.shortName;
- if (whyNotPromotedVisitor.propertyReference != null) {
- Id id = computeMemberId(whyNotPromotedVisitor.propertyReference);
- nonPromotionReasonText += '($id)';
- }
- inferrer.dataForTesting.flowAnalysisResult
- .nonPromotionReasons[read] = nonPromotionReasonText;
- }
- context = [message];
- break;
- }
- }
readResult = inferrer.wrapExpressionInferenceResultInProblem(
readResult,
templateNullablePropertyAccessError.withArguments(
propertyName.text, receiverType, inferrer.isNonNullableByDefault),
read.fileOffset,
propertyName.text.length,
- context: context);
+ context: inferrer.getWhyNotPromotedContext(receiver, read));
}
return readResult;
}
@@ -6988,66 +6961,6 @@
}
}
-class _WhyNotPromotedVisitor
- implements
- NonPromotionReasonVisitor<LocatedMessage, Node, Expression,
- VariableDeclaration> {
- final TypeInferrerImpl inferrer;
-
- final Expression receiver;
-
- Member propertyReference;
-
- _WhyNotPromotedVisitor(this.inferrer, this.receiver);
-
- @override
- LocatedMessage visitDemoteViaExplicitWrite(
- DemoteViaExplicitWrite<VariableDeclaration, Expression> reason) {
- if (inferrer.dataForTesting != null) {
- inferrer.dataForTesting.flowAnalysisResult
- .nonPromotionReasonTargets[reason.writeExpression] = reason.shortName;
- }
- int offset = reason.writeExpression.fileOffset;
- return templateVariableCouldBeNullDueToWrite
- .withArguments(reason.variable.name)
- .withLocation(inferrer.helper.uri, offset, noLength);
- }
-
- @override
- LocatedMessage visitDemoteViaForEachVariableWrite(
- DemoteViaForEachVariableWrite<VariableDeclaration, Node> reason) {
- int offset = (reason.node as TreeNode).fileOffset;
- return templateVariableCouldBeNullDueToWrite
- .withArguments(reason.variable.name)
- .withLocation(inferrer.helper.uri, offset, noLength);
- }
-
- @override
- LocatedMessage visitPropertyNotPromoted(PropertyNotPromoted reason) {
- Member member;
- Expression receiver = this.receiver;
- if (receiver is InstanceGet) {
- member = receiver.interfaceTarget;
- } else if (receiver is SuperPropertyGet) {
- member = receiver.interfaceTarget;
- } else if (receiver is StaticInvocation) {
- member = receiver.target;
- } else if (receiver is PropertyGet) {
- member = receiver.interfaceTarget;
- } else {
- assert(false, 'Unrecognized receiver: ${receiver.runtimeType}');
- }
- if (member != null) {
- propertyReference = member;
- return templateFieldNotPromoted
- .withArguments(reason.propertyName)
- .withLocation(member.fileUri, member.fileOffset, noLength);
- } else {
- return null;
- }
- }
-}
-
class ForInResult {
final VariableDeclaration variable;
final Expression iterable;
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
index b98da53..3521770 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
@@ -5,14 +5,17 @@
// @dart = 2.9
import 'dart:core' hide MapEntry;
+import 'dart:core' as core;
import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart'
hide Reference; // Work around https://github.com/dart-lang/sdk/issues/44667
+import 'package:_fe_analyzer_shared/src/testing/id.dart';
import 'package:_fe_analyzer_shared/src/util/link.dart';
import 'package:front_end/src/fasta/kernel/internal_ast.dart';
import 'package:front_end/src/fasta/type_inference/type_demotion.dart';
+import 'package:front_end/src/testing/id_extractor.dart';
import 'package:kernel/ast.dart'
hide Reference; // Work around https://github.com/dart-lang/sdk/issues/44667
@@ -301,6 +304,37 @@
..fileOffset = fileOffset;
}
+ /// Computes a list of context messages explaining why [receiver] was not
+ /// promoted, to be used when reporting an error for a larger expression
+ /// containing [receiver]. [expression] is the containing expression.
+ List<LocatedMessage> getWhyNotPromotedContext(
+ Expression receiver, Expression expression) {
+ Map<DartType, NonPromotionReason> whyNotPromoted =
+ flowAnalysis?.whyNotPromoted(receiver);
+ List<LocatedMessage> context;
+ if (whyNotPromoted != null && whyNotPromoted.isNotEmpty) {
+ _WhyNotPromotedVisitor whyNotPromotedVisitor =
+ new _WhyNotPromotedVisitor(this, receiver);
+ for (core.MapEntry<DartType, NonPromotionReason> entry
+ in whyNotPromoted.entries) {
+ if (entry.key.isPotentiallyNullable) continue;
+ LocatedMessage message = entry.value.accept(whyNotPromotedVisitor);
+ if (dataForTesting != null) {
+ String nonPromotionReasonText = entry.value.shortName;
+ if (whyNotPromotedVisitor.propertyReference != null) {
+ Id id = computeMemberId(whyNotPromotedVisitor.propertyReference);
+ nonPromotionReasonText += '($id)';
+ }
+ dataForTesting.flowAnalysisResult.nonPromotionReasons[expression] =
+ nonPromotionReasonText;
+ }
+ context = [message];
+ break;
+ }
+ }
+ return context;
+ }
+
/// Returns `true` if exceptions should be thrown in paths reachable only due
/// to unsoundness in flow analysis in mixed mode.
bool get shouldThrowUnsoundnessException =>
@@ -2754,7 +2788,8 @@
templateNullableMethodCallError.withArguments(
name.text, receiverType, isNonNullableByDefault),
fileOffset,
- name.text.length);
+ name.text.length,
+ context: getWhyNotPromotedContext(receiver, staticInvocation));
}
}
return createNullAwareExpressionInferenceResult(
@@ -2831,7 +2866,8 @@
templateNullableMethodCallError.withArguments(
callName.text, receiverType, isNonNullableByDefault),
fileOffset,
- callName.text.length);
+ callName.text.length,
+ context: getWhyNotPromotedContext(receiver, expression));
}
}
// TODO(johnniwinther): Check that type arguments against the bounds.
@@ -2993,7 +3029,8 @@
templateNullableMethodCallError.withArguments(
methodName.text, receiverType, isNonNullableByDefault),
fileOffset,
- methodName.text.length);
+ methodName.text.length,
+ context: getWhyNotPromotedContext(receiver, expression));
}
}
@@ -4836,3 +4873,63 @@
InferredFunctionBody(this.body, this.futureValueType);
}
+
+class _WhyNotPromotedVisitor
+ implements
+ NonPromotionReasonVisitor<LocatedMessage, Node, Expression,
+ VariableDeclaration> {
+ final TypeInferrerImpl inferrer;
+
+ final Expression receiver;
+
+ Member propertyReference;
+
+ _WhyNotPromotedVisitor(this.inferrer, this.receiver);
+
+ @override
+ LocatedMessage visitDemoteViaExplicitWrite(
+ DemoteViaExplicitWrite<VariableDeclaration, Expression> reason) {
+ if (inferrer.dataForTesting != null) {
+ inferrer.dataForTesting.flowAnalysisResult
+ .nonPromotionReasonTargets[reason.writeExpression] = reason.shortName;
+ }
+ int offset = reason.writeExpression.fileOffset;
+ return templateVariableCouldBeNullDueToWrite
+ .withArguments(reason.variable.name)
+ .withLocation(inferrer.helper.uri, offset, noLength);
+ }
+
+ @override
+ LocatedMessage visitDemoteViaForEachVariableWrite(
+ DemoteViaForEachVariableWrite<VariableDeclaration, Node> reason) {
+ int offset = (reason.node as TreeNode).fileOffset;
+ return templateVariableCouldBeNullDueToWrite
+ .withArguments(reason.variable.name)
+ .withLocation(inferrer.helper.uri, offset, noLength);
+ }
+
+ @override
+ LocatedMessage visitPropertyNotPromoted(PropertyNotPromoted reason) {
+ Member member;
+ Expression receiver = this.receiver;
+ if (receiver is InstanceGet) {
+ member = receiver.interfaceTarget;
+ } else if (receiver is SuperPropertyGet) {
+ member = receiver.interfaceTarget;
+ } else if (receiver is StaticInvocation) {
+ member = receiver.target;
+ } else if (receiver is PropertyGet) {
+ member = receiver.interfaceTarget;
+ } else {
+ assert(false, 'Unrecognized receiver: ${receiver.runtimeType}');
+ }
+ if (member != null) {
+ propertyReference = member;
+ return templateFieldNotPromoted
+ .withArguments(reason.propertyName)
+ .withLocation(member.fileUri, member.fileOffset, noLength);
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/pkg/front_end/test/lint_test.status b/pkg/front_end/test/lint_test.status
index aaa6e98..2728954 100644
--- a/pkg/front_end/test/lint_test.status
+++ b/pkg/front_end/test/lint_test.status
@@ -20,7 +20,6 @@
front_end/lib/src/fasta/kernel/body_builder/ImportsTwice: Fail
front_end/lib/src/fasta/kernel/constant_evaluator/ExplicitType: Pass
front_end/lib/src/fasta/kernel/expression_generator_helper/ImportsTwice: Fail
-front_end/lib/src/fasta/kernel/inference_visitor/ImportsTwice: Fail
front_end/lib/src/fasta/kernel/kernel_api/Exports: Fail
front_end/lib/src/fasta/kernel/kernel_ast_api/Exports: Fail
front_end/lib/src/fasta/kernel/kernel_builder/Exports: Fail
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index 7900556..5d9da02 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -390,6 +390,7 @@
exp
expando
expense
+explaining
exportable
exportee
exportees
diff --git a/tools/VERSION b/tools/VERSION
index 5505a1f..b7fdb1d 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 13
PATCH 0
-PRERELEASE 34
+PRERELEASE 35
PRERELEASE_PATCH 0
\ No newline at end of file