Report NON_CONSTANT_XYZ in not taken branches of IfElement.
R=brianwilkerson@google.com
Change-Id: I27e8a3b99140046399cdcc8daaa7ecc28ffa62df
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/97010
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/lib/src/dart/constant/constant_verifier.dart b/pkg/analyzer/lib/src/dart/constant/constant_verifier.dart
index 8f20c32..a8459c1 100644
--- a/pkg/analyzer/lib/src/dart/constant/constant_verifier.dart
+++ b/pkg/analyzer/lib/src/dart/constant/constant_verifier.dart
@@ -13,6 +13,7 @@
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:analyzer/src/dart/constant/evaluation.dart';
+import 'package:analyzer/src/dart/constant/potentially_constant.dart';
import 'package:analyzer/src/dart/constant/value.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/error/codes.dart';
@@ -707,13 +708,19 @@
// The errors have already been reported.
if (conditionBool == null) return false;
- var thenValid = !conditionBool || verify(element.thenElement);
-
- var elseValid = conditionBool ||
- element.elseElement == null ||
- verify(element.elseElement);
-
- // TODO(scheglov) Check that not taken branches are constants
+ var thenValid = true;
+ var elseValid = true;
+ if (conditionBool) {
+ thenValid = verify(element.thenElement);
+ if (element.elseElement != null) {
+ elseValid = _reportNotPotentialConstants(element.elseElement);
+ }
+ } else {
+ thenValid = _reportNotPotentialConstants(element.thenElement);
+ if (element.elseElement != null) {
+ elseValid = verify(element.elseElement);
+ }
+ }
return thenValid && elseValid;
} else if (element is MapLiteralEntry) {
@@ -739,6 +746,36 @@
);
}
+ /// Return `true` if the [node] is a potential constant.
+ bool _reportNotPotentialConstants(AstNode node) {
+ var notPotentiallyConstants = getNotPotentiallyConstants(node);
+ if (notPotentiallyConstants.isEmpty) return true;
+
+ for (var notConst in notPotentiallyConstants) {
+ CompileTimeErrorCode errorCode;
+ if (forList) {
+ errorCode = CompileTimeErrorCode.NON_CONSTANT_LIST_ELEMENT;
+ } else if (forMap) {
+ errorCode = CompileTimeErrorCode.NON_CONSTANT_MAP_ELEMENT;
+ for (var parent = notConst; parent != null; parent = parent.parent) {
+ if (parent is MapLiteralEntry) {
+ if (parent.key == notConst) {
+ errorCode = CompileTimeErrorCode.NON_CONSTANT_MAP_KEY;
+ } else {
+ errorCode = CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE;
+ }
+ break;
+ }
+ }
+ } else if (forSet) {
+ errorCode = CompileTimeErrorCode.NON_CONSTANT_SET_ELEMENT;
+ }
+ verifier._errorReporter.reportErrorForNode(errorCode, notConst);
+ }
+
+ return false;
+ }
+
bool _validateListExpression(Expression expression, DartObjectImpl value) {
if (!verifier._evaluationEngine.runtimeTypeMatch(value, listElementType)) {
verifier._errorReporter.reportErrorForNode(
diff --git a/pkg/analyzer/test/src/diagnostics/map_key_type_not_assignable_test.dart b/pkg/analyzer/test/src/diagnostics/map_key_type_not_assignable_test.dart
index 9167018..30eea8f 100644
--- a/pkg/analyzer/test/src/diagnostics/map_key_type_not_assignable_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/map_key_type_not_assignable_test.dart
@@ -94,14 +94,6 @@
''', [StaticWarningCode.MAP_KEY_TYPE_NOT_ASSIGNABLE]);
}
- @failingTest
- test_const_ifElement_thenFalse_notConst() async {
- await assertErrorsInCode('''
-final a = 0;
-var v = const <int, bool>{if (1 > 2) a: true};
-''', [CompileTimeErrorCode.NON_CONSTANT_MAP_KEY]);
- }
-
test_const_ifElement_thenTrue_intInt_dynamic() async {
await assertNoErrorsInCode('''
const dynamic a = 0;
diff --git a/pkg/analyzer/test/src/diagnostics/map_value_type_not_assignable_test.dart b/pkg/analyzer/test/src/diagnostics/map_value_type_not_assignable_test.dart
index 62ae81e..35e036a 100644
--- a/pkg/analyzer/test/src/diagnostics/map_value_type_not_assignable_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/map_value_type_not_assignable_test.dart
@@ -94,14 +94,6 @@
''', [StaticWarningCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE]);
}
- @failingTest
- test_const_ifElement_thenFalse_notConst() async {
- await assertErrorsInCode('''
-final a = 0;
-var v = const <bool, int>{if (1 > 2) true: a};
-''', [CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE]);
- }
-
test_const_ifElement_thenTrue_intInt_dynamic() async {
await assertNoErrorsInCode('''
const dynamic a = 0;
diff --git a/pkg/analyzer/test/src/diagnostics/non_constant_list_element_test.dart b/pkg/analyzer/test/src/diagnostics/non_constant_list_element_test.dart
new file mode 100644
index 0000000..f90a4c4
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/non_constant_list_element_test.dart
@@ -0,0 +1,101 @@
+// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analyzer/src/dart/analysis/experiments.dart';
+import 'package:analyzer/src/error/codes.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/driver_resolution.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(NonConstantListElementTest);
+ defineReflectiveTests(NonConstantListElementWithUiAsCodeTest);
+ });
+}
+
+@reflectiveTest
+class NonConstantListElementTest extends DriverResolutionTest {
+ test_const_topVar() async {
+ await assertErrorsInCode('''
+final dynamic a = 0;
+var v = const [a];
+''', [CompileTimeErrorCode.NON_CONSTANT_LIST_ELEMENT]);
+ }
+
+ test_nonConst_topVar() async {
+ await assertNoErrorsInCode('''
+final dynamic a = 0;
+var v = [a];
+''');
+ }
+}
+
+@reflectiveTest
+class NonConstantListElementWithUiAsCodeTest
+ extends NonConstantListElementTest {
+ @override
+ AnalysisOptionsImpl get analysisOptions => AnalysisOptionsImpl()
+ ..enabledExperiments = [
+ EnableString.control_flow_collections,
+ EnableString.spread_collections,
+ ];
+
+ test_const_ifElement_thenElseFalse_finalElse() async {
+ await assertErrorsInCode('''
+final dynamic a = 0;
+var v = const [if (1 < 0) 0 else a];
+''', [CompileTimeErrorCode.NON_CONSTANT_LIST_ELEMENT]);
+ }
+
+ test_const_ifElement_thenElseFalse_finalThen() async {
+ await assertErrorsInCode('''
+final dynamic a = 0;
+var v = const [if (1 < 0) a else 0];
+''', [CompileTimeErrorCode.NON_CONSTANT_LIST_ELEMENT]);
+ }
+
+ test_const_ifElement_thenElseTrue_finalElse() async {
+ await assertErrorsInCode('''
+final dynamic a = 0;
+var v = const [if (1 > 0) 0 else a];
+''', [CompileTimeErrorCode.NON_CONSTANT_LIST_ELEMENT]);
+ }
+
+ test_const_ifElement_thenElseTrue_finalThen() async {
+ await assertErrorsInCode('''
+final dynamic a = 0;
+var v = const [if (1 > 0) a else 0];
+''', [CompileTimeErrorCode.NON_CONSTANT_LIST_ELEMENT]);
+ }
+
+ test_const_ifElement_thenFalse_constThen() async {
+ await assertNoErrorsInCode('''
+const dynamic a = 0;
+var v = const [if (1 < 0) a];
+''');
+ }
+
+ test_const_ifElement_thenFalse_finalThen() async {
+ await assertErrorsInCode('''
+final dynamic a = 0;
+var v = const [if (1 < 0) a];
+''', [CompileTimeErrorCode.NON_CONSTANT_LIST_ELEMENT]);
+ }
+
+ test_const_ifElement_thenTrue_constThen() async {
+ await assertNoErrorsInCode('''
+const dynamic a = 0;
+var v = const [if (1 > 0) a];
+''');
+ }
+
+ test_const_ifElement_thenTrue_finalThen() async {
+ await assertErrorsInCode('''
+final dynamic a = 0;
+var v = const [if (1 > 0) a];
+''', [CompileTimeErrorCode.NON_CONSTANT_LIST_ELEMENT]);
+ }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/non_constant_map_element_test.dart b/pkg/analyzer/test/src/diagnostics/non_constant_map_element_test.dart
index eb39184..ddff55a 100644
--- a/pkg/analyzer/test/src/diagnostics/non_constant_map_element_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/non_constant_map_element_test.dart
@@ -12,6 +12,10 @@
main() {
defineReflectiveSuite(() {
defineReflectiveTests(NonConstantMapElementWithUiAsCodeTest);
+ defineReflectiveTests(NonConstantMapKeyTest);
+ defineReflectiveTests(NonConstantMapKeyWithUiAsCodeTest);
+ defineReflectiveTests(NonConstantMapValueTest);
+ defineReflectiveTests(NonConstantMapValueWithUiAsCodeTest);
});
}
@@ -100,3 +104,169 @@
''', [CompileTimeErrorCode.NON_CONSTANT_MAP_ELEMENT]);
}
}
+
+@reflectiveTest
+class NonConstantMapKeyTest extends DriverResolutionTest {
+ test_const_topVar() async {
+ await assertErrorsInCode('''
+final dynamic a = 0;
+var v = const <int, int>{a: 0};
+''', [CompileTimeErrorCode.NON_CONSTANT_MAP_KEY]);
+ }
+
+ test_nonConst_topVar() async {
+ await assertNoErrorsInCode('''
+final dynamic a = 0;
+var v = <int, int>{a: 0};
+''');
+ }
+}
+
+@reflectiveTest
+class NonConstantMapKeyWithUiAsCodeTest extends NonConstantMapKeyTest {
+ @override
+ AnalysisOptionsImpl get analysisOptions => AnalysisOptionsImpl()
+ ..enabledExperiments = [
+ EnableString.control_flow_collections,
+ EnableString.spread_collections,
+ ];
+
+ test_const_ifElement_thenElseFalse_finalElse() async {
+ await assertErrorsInCode('''
+final dynamic a = 0;
+var v = const <int, int>{if (1 < 0) 0: 0 else a: 0};
+''', [CompileTimeErrorCode.NON_CONSTANT_MAP_KEY]);
+ }
+
+ test_const_ifElement_thenElseFalse_finalThen() async {
+ await assertErrorsInCode('''
+final dynamic a = 0;
+var v = const <int, int>{if (1 < 0) a: 0 else 0: 0};
+''', [CompileTimeErrorCode.NON_CONSTANT_MAP_KEY]);
+ }
+
+ test_const_ifElement_thenElseTrue_finalElse() async {
+ await assertErrorsInCode('''
+final dynamic a = 0;
+var v = const <int, int>{if (1 > 0) 0: 0 else a: 0};
+''', [CompileTimeErrorCode.NON_CONSTANT_MAP_KEY]);
+ }
+
+ test_const_ifElement_thenElseTrue_finalThen() async {
+ await assertErrorsInCode('''
+final dynamic a = 0;
+var v = const <int, int>{if (1 > 0) a: 0 else 0: 0};
+''', [CompileTimeErrorCode.NON_CONSTANT_MAP_KEY]);
+ }
+
+ test_const_ifElement_thenFalse_constThen() async {
+ await assertNoErrorsInCode('''
+const dynamic a = 0;
+var v = const <int, int>{if (1 < 0) a: 0};
+''');
+ }
+
+ test_const_ifElement_thenFalse_finalThen() async {
+ await assertErrorsInCode('''
+final dynamic a = 0;
+var v = const <int, int>{if (1 < 0) a: 0};
+''', [CompileTimeErrorCode.NON_CONSTANT_MAP_KEY]);
+ }
+
+ test_const_ifElement_thenTrue_constThen() async {
+ await assertNoErrorsInCode('''
+const dynamic a = 0;
+var v = const <int, int>{if (1 > 0) a: 0};
+''');
+ }
+
+ test_const_ifElement_thenTrue_finalThen() async {
+ await assertErrorsInCode('''
+final dynamic a = 0;
+var v = const <int, int>{if (1 > 0) a: 0};
+''', [CompileTimeErrorCode.NON_CONSTANT_MAP_KEY]);
+ }
+}
+
+@reflectiveTest
+class NonConstantMapValueTest extends DriverResolutionTest {
+ test_const_topVar() async {
+ await assertErrorsInCode('''
+final dynamic a = 0;
+var v = const <int, int>{0: a};
+''', [CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE]);
+ }
+
+ test_nonConst_topVar() async {
+ await assertNoErrorsInCode('''
+final dynamic a = 0;
+var v = <int, int>{0: a};
+''');
+ }
+}
+
+@reflectiveTest
+class NonConstantMapValueWithUiAsCodeTest extends NonConstantMapValueTest {
+ @override
+ AnalysisOptionsImpl get analysisOptions => AnalysisOptionsImpl()
+ ..enabledExperiments = [
+ EnableString.control_flow_collections,
+ EnableString.spread_collections,
+ ];
+
+ test_const_ifElement_thenElseFalse_finalElse() async {
+ await assertErrorsInCode('''
+final dynamic a = 0;
+var v = const <int, int>{if (1 < 0) 0: 0 else 0: a};
+''', [CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE]);
+ }
+
+ test_const_ifElement_thenElseFalse_finalThen() async {
+ await assertErrorsInCode('''
+final dynamic a = 0;
+var v = const <int, int>{if (1 < 0) 0: a else 0: 0};
+''', [CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE]);
+ }
+
+ test_const_ifElement_thenElseTrue_finalElse() async {
+ await assertErrorsInCode('''
+final dynamic a = 0;
+var v = const <int, int>{if (1 > 0) 0: 0 else 0: a};
+''', [CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE]);
+ }
+
+ test_const_ifElement_thenElseTrue_finalThen() async {
+ await assertErrorsInCode('''
+final dynamic a = 0;
+var v = const <int, int>{if (1 > 0) 0: a else 0: 0};
+''', [CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE]);
+ }
+
+ test_const_ifElement_thenFalse_constThen() async {
+ await assertNoErrorsInCode('''
+const dynamic a = 0;
+var v = const <int, int>{if (1 < 0) 0: a};
+''');
+ }
+
+ test_const_ifElement_thenFalse_finalThen() async {
+ await assertErrorsInCode('''
+final dynamic a = 0;
+var v = const <int, int>{if (1 < 0) 0: a};
+''', [CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE]);
+ }
+
+ test_const_ifElement_thenTrue_constThen() async {
+ await assertNoErrorsInCode('''
+const dynamic a = 0;
+var v = const <int, int>{if (1 > 0) 0: a};
+''');
+ }
+
+ test_const_ifElement_thenTrue_finalThen() async {
+ await assertErrorsInCode('''
+final dynamic a = 0;
+var v = const <int, int>{if (1 > 0) 0: a};
+''', [CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE]);
+ }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/non_constant_set_element_test.dart b/pkg/analyzer/test/src/diagnostics/non_constant_set_element_test.dart
new file mode 100644
index 0000000..1d8c475
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/non_constant_set_element_test.dart
@@ -0,0 +1,100 @@
+// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analyzer/src/dart/analysis/experiments.dart';
+import 'package:analyzer/src/error/codes.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/driver_resolution.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(NonConstantSetElementTest);
+ defineReflectiveTests(NonConstantSetElementWithUiAsCodeTest);
+ });
+}
+
+@reflectiveTest
+class NonConstantSetElementTest extends DriverResolutionTest {
+ test_const_topVar() async {
+ await assertErrorsInCode('''
+final dynamic a = 0;
+var v = const <int>{a};
+''', [CompileTimeErrorCode.NON_CONSTANT_SET_ELEMENT]);
+ }
+
+ test_nonConst_topVar() async {
+ await assertNoErrorsInCode('''
+final dynamic a = 0;
+var v = <int>{a};
+''');
+ }
+}
+
+@reflectiveTest
+class NonConstantSetElementWithUiAsCodeTest extends NonConstantSetElementTest {
+ @override
+ AnalysisOptionsImpl get analysisOptions => AnalysisOptionsImpl()
+ ..enabledExperiments = [
+ EnableString.control_flow_collections,
+ EnableString.spread_collections,
+ ];
+
+ test_const_ifElement_thenElseFalse_finalElse() async {
+ await assertErrorsInCode('''
+final dynamic a = 0;
+var v = const <int>{if (1 < 0) 0 else a};
+''', [CompileTimeErrorCode.NON_CONSTANT_SET_ELEMENT]);
+ }
+
+ test_const_ifElement_thenElseFalse_finalThen() async {
+ await assertErrorsInCode('''
+final dynamic a = 0;
+var v = const <int>{if (1 < 0) a else 0};
+''', [CompileTimeErrorCode.NON_CONSTANT_SET_ELEMENT]);
+ }
+
+ test_const_ifElement_thenElseTrue_finalElse() async {
+ await assertErrorsInCode('''
+final dynamic a = 0;
+var v = const <int>{if (1 > 0) 0 else a};
+''', [CompileTimeErrorCode.NON_CONSTANT_SET_ELEMENT]);
+ }
+
+ test_const_ifElement_thenElseTrue_finalThen() async {
+ await assertErrorsInCode('''
+final dynamic a = 0;
+var v = const <int>{if (1 > 0) a else 0};
+''', [CompileTimeErrorCode.NON_CONSTANT_SET_ELEMENT]);
+ }
+
+ test_const_ifElement_thenFalse_constThen() async {
+ await assertNoErrorsInCode('''
+const dynamic a = 0;
+var v = const <int>{if (1 < 0) a};
+''');
+ }
+
+ test_const_ifElement_thenFalse_finalThen() async {
+ await assertErrorsInCode('''
+final dynamic a = 0;
+var v = const <int>{if (1 < 0) a};
+''', [CompileTimeErrorCode.NON_CONSTANT_SET_ELEMENT]);
+ }
+
+ test_const_ifElement_thenTrue_constThen() async {
+ await assertNoErrorsInCode('''
+const dynamic a = 0;
+var v = const <int>{if (1 > 0) a};
+''');
+ }
+
+ test_const_ifElement_thenTrue_finalThen() async {
+ await assertErrorsInCode('''
+final dynamic a = 0;
+var v = const <int>{if (1 > 0) a};
+''', [CompileTimeErrorCode.NON_CONSTANT_SET_ELEMENT]);
+ }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/test_all.dart b/pkg/analyzer/test/src/diagnostics/test_all.dart
index 7f6fdae..27a10b6 100644
--- a/pkg/analyzer/test/src/diagnostics/test_all.dart
+++ b/pkg/analyzer/test/src/diagnostics/test_all.dart
@@ -43,7 +43,9 @@
import 'must_be_immutable_test.dart' as must_be_immutable;
import 'must_call_super_test.dart' as must_call_super;
import 'non_bool_condition_test.dart' as non_bool_condition;
+import 'non_constant_list_element_test.dart' as non_constant_list_element;
import 'non_constant_map_element_test.dart' as non_constant_map_element;
+import 'non_constant_set_element_test.dart' as non_constant_set_element;
import 'set_element_type_not_assignable_test.dart'
as set_element_type_not_assignable;
import 'subtype_of_sealed_class_test.dart' as subtype_of_sealed_class;
@@ -106,7 +108,9 @@
must_be_immutable.main();
must_call_super.main();
non_bool_condition.main();
+ non_constant_list_element.main();
non_constant_map_element.main();
+ non_constant_set_element.main();
set_element_type_not_assignable.main();
subtype_of_sealed_class.main();
top_level_instance_getter.main();