Fix a bug in the ConstantVerifier so that conditional elements are lazily verified

Change-Id: Ib8b8d5af3dc3a27280116d275616bf26a482bb90
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/96360
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Paul Berry <paulberry@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 3373f64..ddffed2 100644
--- a/pkg/analyzer/lib/src/dart/constant/constant_verifier.dart
+++ b/pkg/analyzer/lib/src/dart/constant/constant_verifier.dart
@@ -467,21 +467,32 @@
       }
       return true;
     } else if (element is IfElement) {
-      bool validCondition =
-          !isConst || _validate(element.condition, errorCode) != null;
-      return validCondition &&
-          _validateCollectionElement(
+      if (isConst) {
+        DartObject conditionResult = _validate(element.condition, errorCode);
+        bool conditionValue = conditionResult?.toBoolValue();
+        if (conditionValue == null) {
+          // The errors have already been reported.
+          return false;
+        } else if (conditionValue) {
+          return _validateCollectionElement(
                   element.thenElement, isConst, keys, invalidKeys, errorCode) !=
-              null &&
-          _validateCollectionElement(
+              null;
+        } else if (element.elseElement != null) {
+          return _validateCollectionElement(
                   element.elseElement, isConst, keys, invalidKeys, errorCode) !=
               null;
+        } else {
+          return true;
+        }
+      }
+      return true;
     } else if (element is MapLiteralEntry) {
       return _validateMapLiteralEntry(element, isConst, keys, invalidKeys);
     } else if (element is SpreadElement) {
       return !isConst || _validate(element.expression, errorCode) != null;
     }
-    return null;
+    throw new UnsupportedError(
+        'Unhandled type of collection element: ${element.runtimeType}');
   }
 
   /// Validate that if the passed arguments are constant expressions.
diff --git a/pkg/analyzer/test/src/dart/constant/evaluation_test.dart b/pkg/analyzer/test/src/dart/constant/evaluation_test.dart
index f0e2140..7b9330f 100644
--- a/pkg/analyzer/test/src/dart/constant/evaluation_test.dart
+++ b/pkg/analyzer/test/src/dart/constant/evaluation_test.dart
@@ -802,6 +802,46 @@
       EnableString.spread_collections
     ];
 
+  test_listLiteral_ifElement_false_withElse() async {
+    await _resolveTestCode('''
+const c = [1, if (1 < 0) 2 else 3, 4];
+''');
+    DartObjectImpl result = _evaluateConstant('c');
+    expect(
+        result.type, typeProvider.listType.instantiate([typeProvider.intType]));
+    expect(result.toListValue().map((e) => e.toIntValue()), [1, 3, 4]);
+  }
+
+  test_listLiteral_ifElement_false_withoutElse() async {
+    await _resolveTestCode('''
+const c = [1, if (1 < 0) 2, 3];
+''');
+    DartObjectImpl result = _evaluateConstant('c');
+    expect(
+        result.type, typeProvider.listType.instantiate([typeProvider.intType]));
+    expect(result.toListValue().map((e) => e.toIntValue()), [1, 3]);
+  }
+
+  test_listLiteral_ifElement_true_withElse() async {
+    await _resolveTestCode('''
+const c = [1, if (1 > 0) 2 else 3, 4];
+''');
+    DartObjectImpl result = _evaluateConstant('c');
+    expect(
+        result.type, typeProvider.listType.instantiate([typeProvider.intType]));
+    expect(result.toListValue().map((e) => e.toIntValue()), [1, 2, 4]);
+  }
+
+  test_listLiteral_ifElement_true_withoutElse() async {
+    await _resolveTestCode('''
+const c = [1, if (1 > 0) 2, 3];
+''');
+    DartObjectImpl result = _evaluateConstant('c');
+    expect(
+        result.type, typeProvider.listType.instantiate([typeProvider.intType]));
+    expect(result.toListValue().map((e) => e.toIntValue()), [1, 2, 3]);
+  }
+
   test_listLiteral_nested() async {
     await _resolveTestCode('''
 const c = [1, if (1 > 0) if (2 > 1) 2, 3];
@@ -814,47 +854,7 @@
     expect(result.toListValue().map((e) => e.toIntValue()), [1, 2, 3]);
   }
 
-  test_listLiteral_withIf_false_withElse() async {
-    await _resolveTestCode('''
-const c = [1, if (1 < 0) 2 else 3, 4];
-''');
-    DartObjectImpl result = _evaluateConstant('c');
-    expect(
-        result.type, typeProvider.listType.instantiate([typeProvider.intType]));
-    expect(result.toListValue().map((e) => e.toIntValue()), [1, 3, 4]);
-  }
-
-  test_listLiteral_withIf_false_withoutElse() async {
-    await _resolveTestCode('''
-const c = [1, if (1 < 0) 2, 3];
-''');
-    DartObjectImpl result = _evaluateConstant('c');
-    expect(
-        result.type, typeProvider.listType.instantiate([typeProvider.intType]));
-    expect(result.toListValue().map((e) => e.toIntValue()), [1, 3]);
-  }
-
-  test_listLiteral_withIf_true_withElse() async {
-    await _resolveTestCode('''
-const c = [1, if (1 > 0) 2 else 3, 4];
-''');
-    DartObjectImpl result = _evaluateConstant('c');
-    expect(
-        result.type, typeProvider.listType.instantiate([typeProvider.intType]));
-    expect(result.toListValue().map((e) => e.toIntValue()), [1, 2, 4]);
-  }
-
-  test_listLiteral_withIf_true_withoutElse() async {
-    await _resolveTestCode('''
-const c = [1, if (1 > 0) 2, 3];
-''');
-    DartObjectImpl result = _evaluateConstant('c');
-    expect(
-        result.type, typeProvider.listType.instantiate([typeProvider.intType]));
-    expect(result.toListValue().map((e) => e.toIntValue()), [1, 2, 3]);
-  }
-
-  test_listLiteral_withSpread() async {
+  test_listLiteral_spreadElement() async {
     await _resolveTestCode('''
 const c = [1, ...[2, 3], 4];
 ''');
@@ -864,6 +864,66 @@
     expect(result.toListValue().map((e) => e.toIntValue()), [1, 2, 3, 4]);
   }
 
+  test_mapLiteral_ifElement_false_withElse() async {
+    await _resolveTestCode('''
+const c = {'a' : 1, if (1 < 0) 'b' : 2 else 'c' : 3, 'd' : 4};
+''');
+    DartObjectImpl result = _evaluateConstant('c');
+    expect(
+        result.type,
+        typeProvider.mapType
+            .instantiate([typeProvider.stringType, typeProvider.intType]));
+    Map<DartObject, DartObject> value = result.toMapValue();
+    expect(value.keys.map((e) => e.toStringValue()),
+        unorderedEquals(['a', 'c', 'd']));
+    expect(value.values.map((e) => e.toIntValue()), unorderedEquals([1, 3, 4]));
+  }
+
+  test_mapLiteral_ifElement_false_withoutElse() async {
+    await _resolveTestCode('''
+const c = {'a' : 1, if (1 < 0) 'b' : 2, 'c' : 3};
+''');
+    DartObjectImpl result = _evaluateConstant('c');
+    expect(
+        result.type,
+        typeProvider.mapType
+            .instantiate([typeProvider.stringType, typeProvider.intType]));
+    Map<DartObject, DartObject> value = result.toMapValue();
+    expect(
+        value.keys.map((e) => e.toStringValue()), unorderedEquals(['a', 'c']));
+    expect(value.values.map((e) => e.toIntValue()), unorderedEquals([1, 3]));
+  }
+
+  test_mapLiteral_ifElement_true_withElse() async {
+    await _resolveTestCode('''
+const c = {'a' : 1, if (1 > 0) 'b' : 2 else 'c' : 3, 'd' : 4};
+''');
+    DartObjectImpl result = _evaluateConstant('c');
+    expect(
+        result.type,
+        typeProvider.mapType
+            .instantiate([typeProvider.stringType, typeProvider.intType]));
+    Map<DartObject, DartObject> value = result.toMapValue();
+    expect(value.keys.map((e) => e.toStringValue()),
+        unorderedEquals(['a', 'b', 'd']));
+    expect(value.values.map((e) => e.toIntValue()), unorderedEquals([1, 2, 4]));
+  }
+
+  test_mapLiteral_ifElement_true_withoutElse() async {
+    await _resolveTestCode('''
+const c = {'a' : 1, if (1 > 0) 'b' : 2, 'c' : 3};
+''');
+    DartObjectImpl result = _evaluateConstant('c');
+    expect(
+        result.type,
+        typeProvider.mapType
+            .instantiate([typeProvider.stringType, typeProvider.intType]));
+    Map<DartObject, DartObject> value = result.toMapValue();
+    expect(value.keys.map((e) => e.toStringValue()),
+        unorderedEquals(['a', 'b', 'c']));
+    expect(value.values.map((e) => e.toIntValue()), unorderedEquals([1, 2, 3]));
+  }
+
   @failingTest
   test_mapLiteral_nested() async {
     // Fails because we're not yet parsing nested elements.
@@ -881,67 +941,7 @@
     expect(value.values.map((e) => e.toIntValue()), unorderedEquals([1, 2, 3]));
   }
 
-  test_mapLiteral_withIf_false_withElse() async {
-    await _resolveTestCode('''
-const c = {'a' : 1, if (1 < 0) 'b' : 2 else 'c' : 3, 'd' : 4};
-''');
-    DartObjectImpl result = _evaluateConstant('c');
-    expect(
-        result.type,
-        typeProvider.mapType
-            .instantiate([typeProvider.stringType, typeProvider.intType]));
-    Map<DartObject, DartObject> value = result.toMapValue();
-    expect(value.keys.map((e) => e.toStringValue()),
-        unorderedEquals(['a', 'c', 'd']));
-    expect(value.values.map((e) => e.toIntValue()), unorderedEquals([1, 3, 4]));
-  }
-
-  test_mapLiteral_withIf_false_withoutElse() async {
-    await _resolveTestCode('''
-const c = {'a' : 1, if (1 < 0) 'b' : 2, 'c' : 3};
-''');
-    DartObjectImpl result = _evaluateConstant('c');
-    expect(
-        result.type,
-        typeProvider.mapType
-            .instantiate([typeProvider.stringType, typeProvider.intType]));
-    Map<DartObject, DartObject> value = result.toMapValue();
-    expect(
-        value.keys.map((e) => e.toStringValue()), unorderedEquals(['a', 'c']));
-    expect(value.values.map((e) => e.toIntValue()), unorderedEquals([1, 3]));
-  }
-
-  test_mapLiteral_withIf_true_withElse() async {
-    await _resolveTestCode('''
-const c = {'a' : 1, if (1 > 0) 'b' : 2 else 'c' : 3, 'd' : 4};
-''');
-    DartObjectImpl result = _evaluateConstant('c');
-    expect(
-        result.type,
-        typeProvider.mapType
-            .instantiate([typeProvider.stringType, typeProvider.intType]));
-    Map<DartObject, DartObject> value = result.toMapValue();
-    expect(value.keys.map((e) => e.toStringValue()),
-        unorderedEquals(['a', 'b', 'd']));
-    expect(value.values.map((e) => e.toIntValue()), unorderedEquals([1, 2, 4]));
-  }
-
-  test_mapLiteral_withIf_true_withoutElse() async {
-    await _resolveTestCode('''
-const c = {'a' : 1, if (1 > 0) 'b' : 2, 'c' : 3};
-''');
-    DartObjectImpl result = _evaluateConstant('c');
-    expect(
-        result.type,
-        typeProvider.mapType
-            .instantiate([typeProvider.stringType, typeProvider.intType]));
-    Map<DartObject, DartObject> value = result.toMapValue();
-    expect(value.keys.map((e) => e.toStringValue()),
-        unorderedEquals(['a', 'b', 'c']));
-    expect(value.values.map((e) => e.toIntValue()), unorderedEquals([1, 2, 3]));
-  }
-
-  test_mapLiteral_withSpread() async {
+  test_mapLiteral_spreadElement() async {
     await _resolveTestCode('''
 const c = {'a' : 1, ...{'b' : 2, 'c' : 3}, 'd' : 4};
 ''');
@@ -957,6 +957,46 @@
         value.values.map((e) => e.toIntValue()), unorderedEquals([1, 2, 3, 4]));
   }
 
+  test_setLiteral_ifElement_false_withElse() async {
+    await _resolveTestCode('''
+const c = {1, if (1 < 0) 2 else 3, 4};
+''');
+    DartObjectImpl result = _evaluateConstant('c');
+    expect(
+        result.type, typeProvider.setType.instantiate([typeProvider.intType]));
+    expect(result.toSetValue().map((e) => e.toIntValue()), [1, 3, 4]);
+  }
+
+  test_setLiteral_ifElement_false_withoutElse() async {
+    await _resolveTestCode('''
+const c = {1, if (1 < 0) 2, 3};
+''');
+    DartObjectImpl result = _evaluateConstant('c');
+    expect(
+        result.type, typeProvider.setType.instantiate([typeProvider.intType]));
+    expect(result.toSetValue().map((e) => e.toIntValue()), [1, 3]);
+  }
+
+  test_setLiteral_ifElement_true_withElse() async {
+    await _resolveTestCode('''
+const c = {1, if (1 > 0) 2 else 3, 4};
+''');
+    DartObjectImpl result = _evaluateConstant('c');
+    expect(
+        result.type, typeProvider.setType.instantiate([typeProvider.intType]));
+    expect(result.toSetValue().map((e) => e.toIntValue()), [1, 2, 4]);
+  }
+
+  test_setLiteral_ifElement_true_withoutElse() async {
+    await _resolveTestCode('''
+const c = {1, if (1 > 0) 2, 3};
+''');
+    DartObjectImpl result = _evaluateConstant('c');
+    expect(
+        result.type, typeProvider.setType.instantiate([typeProvider.intType]));
+    expect(result.toSetValue().map((e) => e.toIntValue()), [1, 2, 3]);
+  }
+
   test_setLiteral_nested() async {
     await _resolveTestCode('''
 const c = {1, if (1 > 0) if (2 > 1) 2, 3};
@@ -967,47 +1007,7 @@
     expect(result.toSetValue().map((e) => e.toIntValue()), [1, 2, 3]);
   }
 
-  test_setLiteral_withIf_false_withElse() async {
-    await _resolveTestCode('''
-const c = {1, if (1 < 0) 2 else 3, 4};
-''');
-    DartObjectImpl result = _evaluateConstant('c');
-    expect(
-        result.type, typeProvider.setType.instantiate([typeProvider.intType]));
-    expect(result.toSetValue().map((e) => e.toIntValue()), [1, 3, 4]);
-  }
-
-  test_setLiteral_withIf_false_withoutElse() async {
-    await _resolveTestCode('''
-const c = {1, if (1 < 0) 2, 3};
-''');
-    DartObjectImpl result = _evaluateConstant('c');
-    expect(
-        result.type, typeProvider.setType.instantiate([typeProvider.intType]));
-    expect(result.toSetValue().map((e) => e.toIntValue()), [1, 3]);
-  }
-
-  test_setLiteral_withIf_true_withElse() async {
-    await _resolveTestCode('''
-const c = {1, if (1 > 0) 2 else 3, 4};
-''');
-    DartObjectImpl result = _evaluateConstant('c');
-    expect(
-        result.type, typeProvider.setType.instantiate([typeProvider.intType]));
-    expect(result.toSetValue().map((e) => e.toIntValue()), [1, 2, 4]);
-  }
-
-  test_setLiteral_withIf_true_withoutElse() async {
-    await _resolveTestCode('''
-const c = {1, if (1 > 0) 2, 3};
-''');
-    DartObjectImpl result = _evaluateConstant('c');
-    expect(
-        result.type, typeProvider.setType.instantiate([typeProvider.intType]));
-    expect(result.toSetValue().map((e) => e.toIntValue()), [1, 2, 3]);
-  }
-
-  test_setLiteral_withSpread() async {
+  test_setLiteral_spreadElement() async {
     await _resolveTestCode('''
 const c = {1, ...{2, 3}, 4};
 ''');
diff --git a/pkg/analyzer/test/src/diagnostics/const_eval_throws_exception_test.dart b/pkg/analyzer/test/src/diagnostics/const_eval_throws_exception_test.dart
index c39bd8d..b7ca6b6 100644
--- a/pkg/analyzer/test/src/diagnostics/const_eval_throws_exception_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/const_eval_throws_exception_test.dart
@@ -2,6 +2,8 @@
 // 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/generated/engine.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -10,6 +12,7 @@
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(ConstEvalThrowsExceptionTest);
+    defineReflectiveTests(ConstEvalThrowsExceptionWithUIAsCodeTest);
   });
 }
 
@@ -125,3 +128,28 @@
     assertNoTestErrors();
   }
 }
+
+@reflectiveTest
+class ConstEvalThrowsExceptionWithUIAsCodeTest
+    extends ConstEvalThrowsExceptionTest {
+  @override
+  AnalysisOptionsImpl get analysisOptions => AnalysisOptionsImpl()
+    ..enabledExperiments = [
+      EnableString.control_flow_collections,
+      EnableString.spread_collections,
+    ];
+
+  test_ifElement_false_thenNotEvaluated() async {
+    assertNoErrorsInCode('''
+const dynamic nil = null;
+const c = [if (1 < 0) nil + 1];
+''');
+  }
+
+  test_ifElement_true_elseNotEvaluated() async {
+    assertNoErrorsInCode('''
+const dynamic nil = null;
+const c = [if (0 < 1) 3 else nil + 1];
+''');
+  }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/equal_keys_in_map_test.dart b/pkg/analyzer/test/src/diagnostics/equal_keys_in_map_test.dart
index 2469819..361ac61 100644
--- a/pkg/analyzer/test/src/diagnostics/equal_keys_in_map_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/equal_keys_in_map_test.dart
@@ -24,44 +24,43 @@
       EnableString.spread_collections,
     ];
 
-  test_ifElement_elseBranch_evaluated_addsDuplicate() async {
+  test_ifElement_false_elseEvaluated() async {
     await assertErrorsInCode('''
-void main() {
-  const notTrue = false;
-  const {1: null, if (notTrue) 2: null else 1: null};
-}
+const c = {1: null, if (1 < 0) 2: null else 1: null};
 ''', [StaticWarningCode.EQUAL_KEYS_IN_MAP]);
   }
 
-  test_ifElement_evaluated_addsDuplicate() async {
+  test_ifElement_false_onlyElseEvaluated() async {
+    assertNoErrorsInCode('''
+const c = {if (0 < 1) 1 : 1 else 1 : 2};
+''');
+  }
+
+  test_ifElement_false_thenNotEvaluated() async {
+    assertNoErrorsInCode('''
+const c = {2 : 1, if (1 < 0) 2 : 2};
+''');
+  }
+
+  test_ifElement_true_elseNotEvaluated() async {
+    assertNoErrorsInCode('''
+const c = {1 : 1, if (0 < 1) 2 : 2 else 1 : 2};
+''');
+  }
+
+  test_ifElement_true_onlyThenEvaluated() async {
+    assertNoErrorsInCode('''
+const c = {if (0 < 1) 1 : 1 else 1 : 2};
+''');
+  }
+
+  test_ifElement_true_thenEvaluated() async {
     await assertErrorsInCode('''
-void main() {
-  const {1: null, if (true) 1: null};
-}
+const c = {1: null, if (0 < 1) 1: null};
 ''', [StaticWarningCode.EQUAL_KEYS_IN_MAP]);
   }
 
   @failingTest
-  test_ifElement_notEvaluated_doesntAddDuplicate() async {
-    await assertNoErrorsInCode('''
-void main() {
-  const notTrue = false;
-  const {1: null, if (notTrue) 1: null};
-}
-''');
-  }
-
-  @failingTest
-  test_ifElement_withElse_evaluated_doesntAddDuplicate() async {
-    await assertNoErrorsInCode('''
-void main() {
-  const isTrue = true;
-  const {if (isTrue) 1: null : 1 :null};
-}
-''');
-  }
-
-  @failingTest
   test_nonConst_noDuplicateReported() async {
     await assertNoErrorsInCode('''
 void main() {
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 459422f..eb39184 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
@@ -74,7 +74,6 @@
 ''', [CompileTimeErrorCode.NON_CONSTANT_MAP_ELEMENT]);
   }
 
-  @failingTest
   test_ifElementWithElse_mayBeConst() async {
     await assertNoErrorsInCode('''
 void main() {