Add getNotPotentiallyConstants() and isConstantTypeExpression().
R=brianwilkerson@google.com, paulberry@google.com
Change-Id: I1db7d336f71d307274917d49f8f414217e62dd16
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/97005
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/dart/constant/potentially_constant.dart b/pkg/analyzer/lib/src/dart/constant/potentially_constant.dart
new file mode 100644
index 0000000..5a48513
--- /dev/null
+++ b/pkg/analyzer/lib/src/dart/constant/potentially_constant.dart
@@ -0,0 +1,330 @@
+// 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/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/token.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/src/dart/element/type.dart';
+
+/// Check if the [node] and all its sub-nodes are potentially constant.
+///
+/// Return the list of nodes that are not potentially constant.
+List<AstNode> getNotPotentiallyConstants(AstNode node) {
+ var collector = _Collector();
+ collector.collect(node);
+ return collector.nodes;
+}
+
+/// Return `true` if the [node] is a constant type expression.
+bool isConstantTypeExpression(TypeAnnotation node) {
+ if (node is TypeName) {
+ if (_isConstantTypeName(node.name)) {
+ var arguments = node.typeArguments?.arguments;
+ if (arguments != null) {
+ for (var argument in arguments) {
+ if (!isConstantTypeExpression(argument)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ if (node.type is DynamicTypeImpl) {
+ return true;
+ }
+ if (node.type is VoidType) {
+ return true;
+ }
+ return false;
+ }
+
+ if (node is GenericFunctionType) {
+ var returnType = node.returnType;
+ if (returnType != null) {
+ if (!isConstantTypeExpression(returnType)) {
+ return false;
+ }
+ }
+
+ var typeParameters = node.typeParameters?.typeParameters;
+ if (typeParameters != null) {
+ for (var parameter in typeParameters) {
+ var bound = parameter.bound;
+ if (bound != null && !isConstantTypeExpression(bound)) {
+ return false;
+ }
+ }
+ }
+
+ var formalParameters = node.parameters?.parameters;
+ if (formalParameters != null) {
+ for (var parameter in formalParameters) {
+ if (parameter is SimpleFormalParameter) {
+ if (!isConstantTypeExpression(parameter.type)) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+bool _isConstantTypeName(Identifier name) {
+ var element = name.staticElement;
+ if (element is ClassElement || element is GenericTypeAliasElement) {
+ if (name is PrefixedIdentifier) {
+ if (name.isDeferred) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+class _Collector {
+ final List<AstNode> nodes = [];
+
+ void collect(AstNode node) {
+ if (node is BooleanLiteral ||
+ node is DoubleLiteral ||
+ node is IntegerLiteral ||
+ node is NullLiteral ||
+ node is SimpleStringLiteral ||
+ node is SymbolLiteral) {
+ return;
+ }
+
+ if (node is StringInterpolation) {
+ for (var component in node.elements) {
+ if (component is InterpolationExpression) {
+ collect(component.expression);
+ }
+ }
+ return;
+ }
+
+ if (node is Identifier) {
+ return _identifier(node);
+ }
+
+ if (node is InstanceCreationExpression) {
+ if (!node.isConst) {
+ nodes.add(node);
+ }
+ return;
+ }
+
+ if (node is TypedLiteral) {
+ return _typeLiteral(node);
+ }
+
+ if (node is ParenthesizedExpression) {
+ collect(node.expression);
+ return;
+ }
+
+ if (node is MethodInvocation) {
+ return _methodInvocation(node);
+ }
+
+ if (node is BinaryExpression) {
+ collect(node.leftOperand);
+ collect(node.rightOperand);
+ return;
+ }
+
+ if (node is PrefixExpression) {
+ var operator = node.operator.type;
+ if (operator == TokenType.BANG ||
+ operator == TokenType.MINUS ||
+ operator == TokenType.TILDE) {
+ collect(node.operand);
+ return;
+ }
+ nodes.add(node);
+ return;
+ }
+
+ if (node is ConditionalExpression) {
+ collect(node.condition);
+ collect(node.thenExpression);
+ collect(node.elseExpression);
+ return;
+ }
+
+ if (node is PropertyAccess) {
+ return _propertyAccess(node);
+ }
+
+ if (node is AsExpression) {
+ if (!isConstantTypeExpression(node.type)) {
+ nodes.add(node.type);
+ }
+ collect(node.expression);
+ return;
+ }
+
+ if (node is IsExpression) {
+ if (!isConstantTypeExpression(node.type)) {
+ nodes.add(node.type);
+ }
+ collect(node.expression);
+ return;
+ }
+
+ if (node is MapLiteralEntry) {
+ collect(node.key);
+ collect(node.value);
+ return;
+ }
+
+ if (node is SpreadElement) {
+ collect(node.expression);
+ return;
+ }
+
+ if (node is IfElement) {
+ collect(node.condition);
+ collect(node.thenElement);
+ if (node.elseElement != null) {
+ collect(node.elseElement);
+ }
+ return;
+ }
+
+ nodes.add(node);
+ }
+
+ void _identifier(Identifier node) {
+ var element = node.staticElement;
+
+ if (node is PrefixedIdentifier) {
+ if (node.isDeferred) {
+ nodes.add(node);
+ return;
+ }
+ if (node.identifier.name == 'length') {
+ collect(node.prefix);
+ return;
+ }
+ if (element is MethodElement && element.isStatic) {
+ if (_isConstantTypeName(node.prefix)) {
+ return;
+ }
+ }
+ }
+
+ if (element is VariableElement) {
+ if (!element.isConst) {
+ nodes.add(node);
+ }
+ return;
+ }
+ if (element is PropertyAccessorElement && element.isGetter) {
+ var variable = element.variable;
+ if (!variable.isConst) {
+ nodes.add(node);
+ }
+ return;
+ }
+ if (_isConstantTypeName(node)) {
+ return;
+ }
+ if (element is FunctionElement) {
+ return;
+ }
+ nodes.add(node);
+ }
+
+ void _methodInvocation(MethodInvocation node) {
+ var arguments = node.argumentList?.arguments;
+ if (arguments?.length == 2 && node.methodName.name == 'identical') {
+ var library = node.methodName?.staticElement?.library;
+ if (library?.isDartCore == true) {
+ collect(arguments[0]);
+ collect(arguments[1]);
+ return;
+ }
+ }
+ nodes.add(node);
+ }
+
+ void _propertyAccess(PropertyAccess node) {
+ if (node.propertyName.name == 'length') {
+ collect(node.target);
+ return;
+ }
+
+ var target = node.target;
+ if (target is PrefixedIdentifier) {
+ if (target.isDeferred) {
+ nodes.add(node);
+ return;
+ }
+
+ var element = node.propertyName.staticElement;
+ if (element is PropertyAccessorElement && element.isGetter) {
+ var variable = element.variable;
+ if (!variable.isConst) {
+ nodes.add(node.propertyName);
+ }
+ return;
+ }
+ }
+
+ nodes.add(node);
+ }
+
+ void _typeLiteral(TypedLiteral node) {
+ if (!node.isConst) {
+ nodes.add(node);
+ return;
+ }
+
+ if (node is ListLiteral) {
+ var typeArguments = node.typeArguments?.arguments;
+ if (typeArguments?.length == 1) {
+ var elementType = typeArguments[0];
+ if (!isConstantTypeExpression(elementType)) {
+ nodes.add(elementType);
+ }
+ }
+
+ for (var element in node.elements2) {
+ collect(element);
+ }
+ return;
+ }
+
+ if (node is SetOrMapLiteral) {
+ var typeArguments = node.typeArguments?.arguments;
+ if (typeArguments?.length == 1) {
+ var elementType = typeArguments[0];
+ if (!isConstantTypeExpression(elementType)) {
+ nodes.add(elementType);
+ }
+ }
+
+ if (typeArguments?.length == 2) {
+ var keyType = typeArguments[0];
+ var valueType = typeArguments[1];
+ if (!isConstantTypeExpression(keyType)) {
+ nodes.add(keyType);
+ }
+ if (!isConstantTypeExpression(valueType)) {
+ nodes.add(valueType);
+ }
+ }
+
+ for (var element in node.elements2) {
+ collect(element);
+ }
+ }
+ }
+}
diff --git a/pkg/analyzer/lib/src/test_utilities/find_node.dart b/pkg/analyzer/lib/src/test_utilities/find_node.dart
index 90dc42a..11b92be 100644
--- a/pkg/analyzer/lib/src/test_utilities/find_node.dart
+++ b/pkg/analyzer/lib/src/test_utilities/find_node.dart
@@ -252,6 +252,10 @@
return _node(search, (n) => n is VariableDeclaration);
}
+ VariableDeclarationList variableDeclarationList(String search) {
+ return _node(search, (n) => n is VariableDeclarationList);
+ }
+
WhileStatement whileStatement(String search) {
return _node(search, (n) => n is WhileStatement);
}
diff --git a/pkg/analyzer/test/src/dart/constant/potentially_constant_test.dart b/pkg/analyzer/test/src/dart/constant/potentially_constant_test.dart
new file mode 100644
index 0000000..b9ca532
--- /dev/null
+++ b/pkg/analyzer/test/src/dart/constant/potentially_constant_test.dart
@@ -0,0 +1,804 @@
+// Copyright (c) 2016, 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/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/constant/potentially_constant.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../resolution/driver_resolution.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(IsConstantTypeExpressionTest);
+ defineReflectiveTests(PotentiallyConstantTest);
+ defineReflectiveTests(PotentiallyConstantWithUIAsCodeTest);
+ });
+}
+
+@reflectiveTest
+class IsConstantTypeExpressionTest extends DriverResolutionTest {
+ test_class() async {
+ await _assertConst(r'''
+int x;
+''');
+ }
+
+ test_class_prefix() async {
+ newFile('/test/lib/a.dart', content: r'''
+class A {}
+''');
+ await _assertConst(r'''
+import 'a.dart' as p;
+p.A x;
+''');
+ }
+
+ test_class_prefix_deferred() async {
+ newFile('/test/lib/a.dart', content: r'''
+class A {}
+''');
+ await _assertNotConst(r'''
+import 'a.dart' deferred as p;
+p.A x;
+''');
+ }
+
+ test_class_typeArguments() async {
+ await _assertConst(r'''
+List<int> x;
+''');
+ }
+
+ test_class_typeArguments_notConst() async {
+ await _assertNotConst(r'''
+class A<T> {
+ m() {
+ List<T> x;
+ }
+}
+''');
+ }
+
+ test_dynamic() async {
+ await _assertConst(r'''
+dynamic x;
+''');
+ }
+
+ test_genericFunctionType() async {
+ await _assertConst(r'''
+int Function<T extends num, U>(int, bool) x;
+''');
+ }
+
+ test_genericFunctionType_formalParameterType() async {
+ await _assertNotConst(r'''
+class A<T> {
+ m() {
+ Function(T) x;
+ }
+}
+''');
+ }
+
+ test_genericFunctionType_returnType() async {
+ await _assertNotConst(r'''
+class A<T> {
+ m() {
+ T Function() x;
+ }
+}
+''');
+ }
+
+ test_genericFunctionType_typeParameterBound() async {
+ await _assertNotConst(r'''
+class A<T> {
+ m() {
+ Function<U extends T>() x;
+ }
+}
+''');
+ }
+
+ test_typeParameter() async {
+ await _assertNotConst(r'''
+class A<T> {
+ m() {
+ T x;
+ }
+}
+''');
+ }
+
+ test_void() async {
+ await _assertConst(r'''
+void x;
+''');
+ }
+
+ Future<void> _assertConst(String code) async {
+ addTestFile(code);
+ await resolveTestFile();
+
+ var type = findNode.variableDeclarationList('x;').type;
+ expect(isConstantTypeExpression(type), isTrue);
+ }
+
+ Future<void> _assertNotConst(String code) async {
+ addTestFile(code);
+ await resolveTestFile();
+
+ var type = findNode.variableDeclarationList('x;').type;
+ expect(isConstantTypeExpression(type), isFalse);
+ }
+}
+
+@reflectiveTest
+class PotentiallyConstantTest extends DriverResolutionTest {
+ test_asExpression() async {
+ await _assertConst(r'''
+const a = 0;
+var x = a as int;
+''', () => _xInitializer());
+ }
+
+ test_asExpression_final() async {
+ await _assertNotConst(r'''
+final a = 0;
+var x = a as int;
+''', () => _xInitializer(), () => [findNode.simple('a as')]);
+ }
+
+ test_asExpression_notConstType() async {
+ await _assertNotConst(r'''
+const a = 0;
+class A<T> {
+ m() {
+ var x = a as T;
+ }
+}
+''', () => _xInitializer(), () => [findNode.typeName('T;')]);
+ }
+
+ test_conditional() async {
+ await _assertConst(r'''
+const a = 0;
+const b = 0;
+const c = 0;
+var x = a ? b : c;
+''', () => _xInitializer());
+ }
+
+ test_conditional_final() async {
+ await _assertNotConst(
+ r'''
+final a = 0;
+final b = 0;
+final c = 0;
+var x = a ? b : c;
+''',
+ () => _xInitializer(),
+ () => [
+ findNode.simple('a ?'),
+ findNode.simple('b :'),
+ findNode.simple('c;')
+ ]);
+ }
+
+ test_instanceCreation() async {
+ await _assertNotConst(r'''
+class A {
+ const A();
+}
+
+var x = new A(); // x
+''', () => _xInitializer(), () => [findNode.instanceCreation('A(); // x')]);
+ }
+
+ test_instanceCreation_const() async {
+ await _assertConst(r'''
+class A {
+ const A();
+}
+
+var x = const A();
+''', () => _xInitializer());
+ }
+
+ test_isExpression() async {
+ await _assertConst(r'''
+const a = 0;
+var x = a is int;
+''', () => _xInitializer());
+ }
+
+ test_isExpression_final() async {
+ await _assertNotConst(r'''
+final a = 0;
+var x = a is int;
+''', () => _xInitializer(), () => [findNode.simple('a is')]);
+ }
+
+ test_isExpression_notConstType() async {
+ await _assertNotConst(r'''
+const a = 0;
+class A<T> {
+ m() {
+ var x = a is T;
+ }
+}
+''', () => _xInitializer(), () => [findNode.typeName('T;')]);
+ }
+
+ test_listLiteral() async {
+ await _assertConst(r'''
+var x = const [0, 1, 2];
+''', () => _xInitializer());
+ }
+
+ test_listLiteral_notConst() async {
+ await _assertNotConst(r'''
+var x = [0, 1, 2];
+''', () => _xInitializer(), () => [findNode.listLiteral('0,')]);
+ }
+
+ test_listLiteral_notConst_element() async {
+ await _assertNotConst(r'''
+final a = 0;
+final b = 1;
+var x = const [a, b, 2];
+''', () => _xInitializer(),
+ () => [findNode.simple('a,'), findNode.simple('b,')]);
+ }
+
+ test_listLiteral_typeArgument() async {
+ await _assertConst(r'''
+var x = const <int>[0, 1, 2];
+''', () => _xInitializer());
+ }
+
+ test_listLiteral_typeArgument_notConstType() async {
+ await _assertNotConst(r'''
+class A<T> {
+ m() {
+ var x = const <T>[0, 1, 2];
+ }
+}
+''', () => _xInitializer(), () => [findNode.typeName('T>[0')]);
+ }
+
+ test_literal_bool() async {
+ await _assertConst(r'''
+var x = true;
+''', () => _xInitializer());
+ }
+
+ test_literal_double() async {
+ await _assertConst(r'''
+var x = 1.2;
+''', () => _xInitializer());
+ }
+
+ test_literal_int() async {
+ await _assertConst(r'''
+var x = 0;
+''', () => _xInitializer());
+ }
+
+ test_literal_null() async {
+ await _assertConst(r'''
+var x = null;
+''', () => _xInitializer());
+ }
+
+ test_literal_simpleString() async {
+ await _assertConst(r'''
+var x = '123';
+''', () => _xInitializer());
+ }
+
+ test_literal_symbol() async {
+ await _assertConst(r'''
+var x = #a.b.c;
+''', () => _xInitializer());
+ }
+
+ test_mapLiteral() async {
+ await _assertConst(r'''
+var x = const {0: 1};
+''', () => _xInitializer());
+ }
+
+ test_mapLiteral_notConst() async {
+ await _assertNotConst(r'''
+var x = {0: 1};
+''', () => _xInitializer(), () => [findNode.setOrMapLiteral('0: 1')]);
+ }
+
+ test_mapLiteral_notConst_key() async {
+ await _assertNotConst(r'''
+final a = 1;
+final b = 2;
+var x = const {0: 0, a: 1, b: 2};
+''', () => _xInitializer(),
+ () => [findNode.simple('a:'), findNode.simple('b:')]);
+ }
+
+ test_mapLiteral_notConst_value() async {
+ await _assertNotConst(r'''
+final a = 1;
+final b = 2;
+var x = const {0: 0, 1: a, 2: b};
+''', () => _xInitializer(),
+ () => [findNode.simple('a,'), findNode.simple('b}')]);
+ }
+
+ test_mapLiteral_typeArgument() async {
+ await _assertConst(r'''
+var x = const <int, int>{0: 0};
+''', () => _xInitializer());
+ }
+
+ test_mapLiteral_typeArgument_notConstType() async {
+ await _assertNotConst(r'''
+class A<T> {
+ m() {
+ var x = const <T, T>{};
+ }
+}
+''', () => _xInitializer(),
+ () => [findNode.typeName('T,'), findNode.typeName('T>{')]);
+ }
+
+ test_methodInvocation_identical() async {
+ await _assertConst(r'''
+const a = 0;
+const b = 0;
+var x = identical(a, b);
+''', () => _xInitializer());
+ }
+
+ test_methodInvocation_identical_final() async {
+ await _assertNotConst(r'''
+final a = 0;
+final b = 0;
+var x = identical(a, b);
+''', () => _xInitializer(),
+ () => [findNode.simple('a,'), findNode.simple('b)')]);
+ }
+
+ test_methodInvocation_name() async {
+ await _assertNotConst(r'''
+const a = 0;
+const b = 0;
+var x = foo(a, b);
+''', () => _xInitializer(), () => [findNode.methodInvocation('foo')]);
+ }
+
+ test_methodInvocation_target() async {
+ await _assertNotConst(r'''
+var x = a.foo();
+''', () => _xInitializer(), () => [findNode.methodInvocation('a.foo()')]);
+ }
+
+ test_parenthesizedExpression_const() async {
+ await _assertConst(r'''
+const a = 0;
+var x = (a);
+''', () => _xInitializer());
+ }
+
+ test_parenthesizedExpression_final() async {
+ await _assertNotConst(r'''
+final a = 0;
+var x = (a);
+''', () => _xInitializer(), () => [findNode.simple('a);')]);
+ }
+
+ test_postfixExpression() async {
+ await _assertNotConst(r'''
+const a = 0;
+var x = a++;
+''', () => _xInitializer(), () => [findNode.postfix('a++')]);
+ }
+
+ test_prefixedIdentifier_importPrefix() async {
+ newFile('/test/lib/a.dart', content: r'''
+const a = 0;
+''');
+ await _assertConst(r'''
+import 'a.dart' as p;
+var x = p.a + 1;
+''', () => _xInitializer());
+ }
+
+ test_prefixedIdentifier_importPrefix_deferred() async {
+ newFile('/test/lib/a.dart', content: r'''
+const a = 0;
+''');
+ await _assertNotConst(r'''
+import 'a.dart' deferred as p;
+var x = p.a + 1;
+''', () => _xInitializer(), () => [findNode.prefixed('p.a')]);
+ }
+
+ test_prefixedIdentifier_length_const() async {
+ await _assertConst(r'''
+const a = 'abc';
+var x = a.length;
+''', () => _xInitializer());
+ }
+
+ test_prefixedIdentifier_length_final() async {
+ await _assertNotConst(r'''
+final a = 'abc';
+var x = a.length;
+''', () => _xInitializer(), () => [findNode.simple('a.')]);
+ }
+
+ test_prefixedIdentifier_method_instance() async {
+ await _assertNotConst(r'''
+class A {
+ const A();
+ m() {};
+}
+
+const a = const A();
+
+var x = a.m;
+''', () => _xInitializer(), () => [findNode.prefixed('a.m')]);
+ }
+
+ test_prefixedIdentifier_method_static() async {
+ await _assertConst(r'''
+class A {
+ static m() {};
+}
+
+var x = A.m;
+''', () => _xInitializer());
+ }
+
+ test_prefixedIdentifier_method_static_viaInstance() async {
+ await _assertNotConst(r'''
+class A {
+ const A();
+ static m() {};
+}
+
+const a = const A();
+
+var x = a.m;
+''', () => _xInitializer(), () => [findNode.prefixed('a.m')]);
+ }
+
+ test_prefixedIdentifier_prefix_variable() async {
+ await _assertNotConst(r'''
+class A {
+ final a = 0;
+ const A();
+}
+
+const a = const A();
+
+var x = a.b + 1;
+''', () => _xInitializer(), () => [findNode.prefixed('a.b + 1')]);
+ }
+
+ test_prefixedIdentifier_staticField_const() async {
+ await _assertConst(r'''
+class A {
+ static const a = 0;
+}
+var x = A.a + 1;
+''', () => _xInitializer());
+ }
+
+ test_prefixedIdentifier_staticField_final() async {
+ await _assertNotConst(
+ r'''
+class A {
+ static final a = 0;
+}
+var x = A.a + 1;
+''',
+ () => _xInitializer(),
+ () => [findNode.prefixed('A.a')],
+ );
+ }
+
+ test_prefixExpression_bang() async {
+ await _assertConst(r'''
+const a = 0;
+var x = !a;
+''', () => _xInitializer());
+ }
+
+ test_prefixExpression_minus() async {
+ await _assertConst(r'''
+const a = 0;
+var x = -a;
+''', () => _xInitializer());
+ }
+
+ test_prefixExpression_minus_final() async {
+ await _assertNotConst(r'''
+final a = 0;
+var x = -a;
+''', () => _xInitializer(), () => [findNode.simple('a;')]);
+ }
+
+ test_prefixExpression_plusPlus() async {
+ await _assertNotConst(r'''
+const a = 0;
+var x = ++a;
+''', () => _xInitializer(), () => [findNode.prefix('++a')]);
+ }
+
+ test_prefixExpression_tilde() async {
+ await _assertConst(r'''
+const a = 0;
+var x = ~a;
+''', () => _xInitializer());
+ }
+
+ test_propertyAccess_length_final() async {
+ await _assertNotConst(r'''
+final a = 'abc';
+var x = (a).length;
+''', () => _xInitializer(), () => [findNode.simple('a).')]);
+ }
+
+ test_propertyAccess_length_stringLiteral() async {
+ await _assertConst(r'''
+var x = 'abc'.length;
+''', () => _xInitializer());
+ }
+
+ test_propertyAccess_staticField_withPrefix_const() async {
+ newFile('/test/lib/a.dart', content: r'''
+class A {
+ static const a = 0;
+}
+''');
+ await _assertConst(r'''
+import 'a.dart' as p;
+var x = p.A.a + 1;
+''', () => _xInitializer());
+ }
+
+ test_propertyAccess_staticField_withPrefix_deferred() async {
+ newFile('/test/lib/a.dart', content: r'''
+class A {
+ static const a = 0;
+}
+''');
+ await _assertNotConst(r'''
+import 'a.dart' deferred as p;
+var x = p.A.a + 1;
+''', () => _xInitializer(), () => [findNode.propertyAccess('p.A.a')]);
+ }
+
+ test_propertyAccess_staticField_withPrefix_final() async {
+ newFile('/test/lib/a.dart', content: r'''
+class A {
+ static final a = 0;
+}
+''');
+ await _assertNotConst(r'''
+import 'a.dart' as p;
+var x = p.A.a + 1;
+''', () => _xInitializer(), () => [findNode.simple('a + 1')]);
+ }
+
+ test_propertyAccess_target_instanceCreation() async {
+ await _assertNotConst(r'''
+class A {
+ final a = 0;
+}
+
+var x = A().a + 1;
+''', () => _xInitializer(), () => [findNode.propertyAccess('A().a')]);
+ }
+
+ test_propertyAccess_target_variable() async {
+ newFile('/test/lib/a.dart', content: r'''
+class A {
+ final a = 0;
+ const A();
+}
+
+const a = const A();
+''');
+ await _assertNotConst(r'''
+import 'a.dart' as p;
+
+var x = p.a.b + 1;
+''', () => _xInitializer(), () => [findNode.propertyAccess('p.a.b + 1')]);
+ }
+
+ test_setLiteral() async {
+ await _assertConst(r'''
+var x = const {0, 1, 2};
+''', () => _xInitializer());
+ }
+
+ test_setLiteral_notConst() async {
+ await _assertNotConst(r'''
+var x = {0, 1, 2};
+''', () => _xInitializer(), () => [findNode.setOrMapLiteral('0,')]);
+ }
+
+ test_setLiteral_notConst_element() async {
+ await _assertNotConst(r'''
+final a = 0;
+final b = 1;
+var x = const {a, b, 2};
+''', () => _xInitializer(),
+ () => [findNode.simple('a,'), findNode.simple('b,')]);
+ }
+
+ test_setLiteral_typeArgument() async {
+ await _assertConst(r'''
+var x = const <int>{0, 1, 2};
+''', () => _xInitializer());
+ }
+
+ test_setLiteral_typeArgument_notConstType() async {
+ await _assertNotConst(r'''
+class A<T> {
+ m() {
+ var x = const <T>{0, 1, 2};
+ }
+}
+''', () => _xInitializer(), () => [findNode.typeName('T>{0')]);
+ }
+
+ test_simpleIdentifier_function() async {
+ await _assertConst(r'''
+var x = f;
+
+void f() {}
+''', () => _xInitializer());
+ }
+
+ test_simpleIdentifier_localVar_const() async {
+ await _assertConst(r'''
+main() {
+ const a = 0;
+ var x = a + 1;
+}
+''', () => _xInitializer());
+ }
+
+ test_simpleIdentifier_localVar_final() async {
+ await _assertNotConst(
+ r'''
+main() {
+ final a = 0;
+ var x = a + 1;
+}
+''',
+ () => _xInitializer(),
+ () => [findNode.simple('a +')],
+ );
+ }
+
+ test_simpleIdentifier_topVar_const() async {
+ await _assertConst(r'''
+const a = 0;
+var x = a + 1;
+''', () => _xInitializer());
+ }
+
+ test_simpleIdentifier_topVar_final() async {
+ await _assertNotConst(
+ r'''
+final a = 0;
+var x = a + 1;
+''',
+ () => _xInitializer(),
+ () => [findNode.simple('a +')],
+ );
+ }
+
+ test_simpleIdentifier_type_class() async {
+ await _assertConst(r'''
+var x = int;
+''', () => _xInitializer());
+ }
+
+ test_stringInterpolation_topVar_const() async {
+ await _assertConst(r'''
+const a = 0;
+var x = 'a $a b';
+''', () => _xInitializer());
+ }
+
+ test_stringInterpolation_topVar_final() async {
+ await _assertNotConst(
+ r'''
+final a = 0;
+var x = 'a $a b';
+''',
+ () => _xInitializer(),
+ () => [findNode.simple('a b')],
+ );
+ }
+
+ _assertConst(String code, AstNode Function() getNode) async {
+ addTestFile(code);
+ await resolveTestFile();
+
+ var node = getNode();
+ var notConstList = getNotPotentiallyConstants(node);
+ expect(notConstList, isEmpty);
+ }
+
+ _assertNotConst(String code, AstNode Function() getNode,
+ List<AstNode> Function() getNotConstList) async {
+ addTestFile(code);
+ await resolveTestFile();
+
+ var node = getNode();
+ var notConstList = getNotPotentiallyConstants(node);
+
+ var expectedNotConst = getNotConstList();
+ expect(notConstList, unorderedEquals(expectedNotConst));
+ }
+
+ Expression _xInitializer() {
+ return findNode.variableDeclaration('x = ').initializer;
+ }
+}
+
+@reflectiveTest
+class PotentiallyConstantWithUIAsCodeTest extends PotentiallyConstantTest {
+ @override
+ AnalysisOptionsImpl get analysisOptions => AnalysisOptionsImpl()
+ ..enabledExperiments = ['control-flow-collections', 'spread-collections'];
+
+ test_ifElement_then() async {
+ await _assertConst(r'''
+const a = 0;
+const b = 0;
+var x = const [if (a) b];
+''', () => _xInitializer());
+ }
+
+ test_ifElement_then_final() async {
+ await _assertNotConst(r'''
+final a = 0;
+final b = 0;
+var x = const [if (a) b];
+''', () => _xInitializer(),
+ () => [findNode.simple('a)'), findNode.simple('b]')]);
+ }
+
+ test_ifElement_thenElse() async {
+ await _assertConst(r'''
+const a = 0;
+const b = 0;
+const c = 0;
+var x = const [if (a) b else c];
+''', () => _xInitializer());
+ }
+
+ test_spreadElement() async {
+ await _assertConst(r'''
+const a = [0, 1, 2];
+var x = const [...a];
+''', () => _xInitializer());
+ }
+
+ test_spreadElement_final() async {
+ await _assertNotConst(r'''
+final a = [0, 1, 2];
+var x = const [...a];
+''', () => _xInitializer(), () => [findNode.simple('a];')]);
+ }
+}