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];')]);
+  }
+}