[analyzer] Mark const iterables unknown if there is a unknown bool in an If Statement entry

Bools from environment such as `dart.library.js_util` are represented as unknown bools. They currently produce a `NON_BOOL_CONDITION` error when used in if statements in const lists, which is not the behaviour we want.

This CL marks iterables with these unknown bools in if statements as unknown and avoids other calculations like `==` upstream.

Bug: https://github.com/dart-lang/sdk/issues/53363, https://github.com/dart-lang/sdk/issues/51607

Change-Id: Ibd3e6dbc6a507c93516bc2782d59d0e1e452f17d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/326261
Commit-Queue: Kallen Tu <kallentu@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Kallen Tu 2023-10-25 22:31:32 +00:00 committed by Commit Queue
parent aac74693f5
commit 40aacb3482
4 changed files with 493 additions and 242 deletions

View file

@ -9,7 +9,6 @@ import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/ast/syntactic_entity.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/constant/value.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
@ -1008,26 +1007,19 @@ class ConstantVisitor extends UnifyingAstVisitor<Constant> {
return InvalidConstant.forEntity(
node, CompileTimeErrorCode.MISSING_CONST_IN_LIST_LITERAL);
}
var elements = <DartObjectImpl>[];
for (CollectionElement element in node.elements) {
var result = _addElementsToList(elements, element);
if (result is InvalidConstant) {
return result;
}
}
var nodeType = node.staticType;
DartType elementType =
var elementType =
nodeType is InterfaceType && nodeType.typeArguments.isNotEmpty
? nodeType.typeArguments[0]
: _typeProvider.dynamicType;
InterfaceType listType = _typeProvider.listType(elementType);
return DartObjectImpl(
var listType = _typeProvider.listType(elementType);
var list = <DartObjectImpl>[];
return _buildListConstant(
list,
node.elements,
typeSystem,
listType,
ListState(
elementType: elementType,
elements: elements,
),
elementType,
);
}
@ -1245,22 +1237,8 @@ class ConstantVisitor extends UnifyingAstVisitor<Constant> {
return InvalidConstant.forEntity(
node, CompileTimeErrorCode.MISSING_CONST_IN_MAP_LITERAL);
}
Map<DartObjectImpl, DartObjectImpl> map = {};
for (CollectionElement element in node.elements) {
var result = _addElementsToMap(map, element);
if (result is InvalidConstant) {
if (!node.isMap) {
// We don't report the error if we know this is an ambiguous map or
// set. [CompileTimeErrorCode.AMBIGUOUS_SET_OR_MAP_LITERAL_BOTH]
// or [CompileTimeErrorCode.AMBIGUOUS_SET_OR_MAP_LITERAL_EITHER] is
// already reported elsewhere.
result.avoidReporting = true;
}
return result;
}
}
DartType keyType = _typeProvider.dynamicType;
DartType valueType = _typeProvider.dynamicType;
var keyType = _typeProvider.dynamicType;
var valueType = _typeProvider.dynamicType;
var nodeType = node.staticType;
if (nodeType is InterfaceType) {
var typeArguments = nodeType.typeArguments;
@ -1269,27 +1247,30 @@ class ConstantVisitor extends UnifyingAstVisitor<Constant> {
valueType = typeArguments[1];
}
}
InterfaceType mapType = _typeProvider.mapType(keyType, valueType);
return DartObjectImpl(typeSystem, mapType, MapState(map));
var mapType = _typeProvider.mapType(keyType, valueType);
var map = <DartObjectImpl, DartObjectImpl>{};
var result = _buildMapConstant(map, node.elements, typeSystem, mapType);
if (result is InvalidConstant && !node.isMap) {
// We don't report the error if we know this is an ambiguous map or
// set. [CompileTimeErrorCode.AMBIGUOUS_SET_OR_MAP_LITERAL_BOTH]
// or [CompileTimeErrorCode.AMBIGUOUS_SET_OR_MAP_LITERAL_EITHER] is
// already reported elsewhere.
result.avoidReporting = true;
}
return result;
} else {
if (!node.isConst) {
return InvalidConstant.forEntity(
node, CompileTimeErrorCode.MISSING_CONST_IN_SET_LITERAL);
}
Set<DartObjectImpl> set = <DartObjectImpl>{};
for (CollectionElement element in node.elements) {
var result = _addElementsToSet(set, element);
if (result is InvalidConstant) {
return result;
}
}
var nodeType = node.staticType;
DartType elementType =
var elementType =
nodeType is InterfaceType && nodeType.typeArguments.isNotEmpty
? nodeType.typeArguments[0]
: _typeProvider.dynamicType;
InterfaceType setType = _typeProvider.setType(elementType);
return DartObjectImpl(typeSystem, setType, SetState(set));
var setType = _typeProvider.setType(elementType);
var set = <DartObjectImpl>{};
return _buildSetConstant(set, node.elements, typeSystem, setType);
}
}
@ -1342,190 +1323,263 @@ class ConstantVisitor extends UnifyingAstVisitor<Constant> {
@override
Constant visitTypeLiteral(TypeLiteral node) => evaluateConstant(node.type);
/// Add the entries produced by evaluating the given collection [element] to
/// the given [list]. Return an [InvalidConstant] if the evaluation of one or
/// more of the elements failed.
InvalidConstant? _addElementsToList(
List<DartObject> list, CollectionElement element) {
switch (element) {
case Expression():
var expression = evaluateConstant(element);
switch (expression) {
case InvalidConstant():
return expression;
case DartObjectImpl():
list.add(expression);
return null;
}
case ForElement():
return InvalidConstant.forEntity(
element, CompileTimeErrorCode.CONST_EVAL_FOR_ELEMENT);
case IfElement():
var condition = evaluateConstant(element.expression);
switch (condition) {
case InvalidConstant():
return condition;
case DartObjectImpl():
var conditionValue = condition.toBoolValue();
if (conditionValue == null) {
return InvalidConstant.forEntity(
element.expression, CompileTimeErrorCode.NON_BOOL_CONDITION);
} else if (conditionValue) {
return _addElementsToList(list, element.thenElement);
} else if (element.elseElement != null) {
return _addElementsToList(list, element.elseElement!);
}
// There's no else element, but the condition value is false.
return null;
}
case MapLiteralEntry():
return InvalidConstant.forEntity(
element, CompileTimeErrorCode.MAP_ENTRY_NOT_IN_MAP);
case SpreadElement():
var spread = evaluateConstant(element.expression);
switch (spread) {
case InvalidConstant():
return spread;
case DartObjectImpl():
// Special case for ...?
if (spread.isNull && element.isNullAware) {
return null;
}
var listValue = spread.toListValue() ?? spread.toSetValue();
if (listValue == null) {
return InvalidConstant.forEntity(element.expression,
CompileTimeErrorCode.CONST_SPREAD_EXPECTED_LIST_OR_SET);
}
list.addAll(listValue);
return null;
}
/// Builds a list constant by adding the evaluated entries of [elements] to
/// the given [list].
///
/// The [typeSystem], [listType], and [elementType] are used to create a valid
/// constant. We return an [InvalidConstant] if the evaluation of any of the
/// elements failed.
Constant _buildListConstant(
List<DartObjectImpl> list,
List<CollectionElement> elements,
TypeSystemImpl typeSystem,
DartType listType,
DartType elementType,
) {
for (var element in elements) {
switch (element) {
case Expression():
var expression = evaluateConstant(element);
switch (expression) {
case InvalidConstant():
return expression;
case DartObjectImpl():
list.add(expression);
}
case ForElement():
return InvalidConstant.forEntity(
element, CompileTimeErrorCode.CONST_EVAL_FOR_ELEMENT);
case IfElement():
var condition = evaluateConstant(element.expression);
switch (condition) {
case InvalidConstant():
return condition;
case DartObjectImpl():
// If the condition is unknown, we mark this list as unknown.
if (condition.isUnknown) {
return DartObjectImpl.validWithUnknownValue(
typeSystem,
listType,
listElementType: elementType,
);
}
var conditionValue = condition.toBoolValue();
Constant? branchResult;
if (conditionValue == null) {
return InvalidConstant.forEntity(element.expression,
CompileTimeErrorCode.NON_BOOL_CONDITION);
} else if (conditionValue) {
branchResult = _buildListConstant(
list,
[element.thenElement],
typeSystem,
listType,
elementType,
);
} else if (element.elseElement != null) {
branchResult = _buildListConstant(
list,
[element.elseElement!],
typeSystem,
listType,
elementType,
);
}
if (branchResult is InvalidConstant) {
return branchResult;
}
}
case MapLiteralEntry():
return InvalidConstant.forEntity(
element, CompileTimeErrorCode.MAP_ENTRY_NOT_IN_MAP);
case SpreadElement():
var spread = evaluateConstant(element.expression);
switch (spread) {
case InvalidConstant():
return spread;
case DartObjectImpl():
// Special case for ...?
if (spread.isNull && element.isNullAware) {
continue;
}
var listValue = spread.toListValue() ?? spread.toSetValue();
if (listValue == null) {
return InvalidConstant.forEntity(element.expression,
CompileTimeErrorCode.CONST_SPREAD_EXPECTED_LIST_OR_SET);
}
list.addAll(listValue);
}
}
}
return DartObjectImpl(
typeSystem,
listType,
ListState(
elementType: elementType,
elements: list,
),
);
}
/// Add the entries produced by evaluating the given map [element] to the
/// given [map]. Return an [InvalidConstant] if the evaluation of one or
/// more of the elements failed.
InvalidConstant? _addElementsToMap(
Map<DartObjectImpl, DartObjectImpl> map, CollectionElement element) {
switch (element) {
case Expression():
return InvalidConstant.forEntity(
element, CompileTimeErrorCode.EXPRESSION_IN_MAP);
case ForElement():
return InvalidConstant.forEntity(
element, CompileTimeErrorCode.CONST_EVAL_FOR_ELEMENT);
case IfElement():
var condition = evaluateConstant(element.expression);
switch (condition) {
case InvalidConstant():
return condition;
case DartObjectImpl():
var conditionValue = condition.toBoolValue();
if (conditionValue == null) {
return InvalidConstant.forEntity(
element.expression, CompileTimeErrorCode.NON_BOOL_CONDITION);
} else if (conditionValue) {
return _addElementsToMap(map, element.thenElement);
} else if (element.elseElement != null) {
return _addElementsToMap(map, element.elseElement!);
}
// There's no else element, but the condition value is false.
return null;
}
case MapLiteralEntry():
var keyResult = evaluateConstant(element.key);
var valueResult = evaluateConstant(element.value);
switch (keyResult) {
case InvalidConstant():
return keyResult;
case DartObjectImpl():
switch (valueResult) {
case InvalidConstant():
return valueResult;
case DartObjectImpl():
map[keyResult] = valueResult;
}
}
return null;
case SpreadElement():
var spread = evaluateConstant(element.expression);
switch (spread) {
case InvalidConstant():
return spread;
case DartObjectImpl():
// Special case for ...?
if (spread.isNull && element.isNullAware) {
return null;
}
var mapValue = spread.toMapValue();
if (mapValue == null) {
return InvalidConstant.forEntity(element.expression,
CompileTimeErrorCode.CONST_SPREAD_EXPECTED_MAP);
}
map.addAll(mapValue);
return null;
}
/// Builds a map constant by adding the evaluated entries of [elements] to
/// the given [map].
///
/// The [typeSystem] and [mapType] are used to create a valid map constant.
/// We return an [InvalidConstant] if the evaluation of any of the elements
/// failed.
Constant _buildMapConstant(
Map<DartObjectImpl, DartObjectImpl> map,
List<CollectionElement> elements,
TypeSystemImpl typeSystem,
DartType mapType) {
for (var element in elements) {
switch (element) {
case Expression():
return InvalidConstant.forEntity(
element, CompileTimeErrorCode.EXPRESSION_IN_MAP);
case ForElement():
return InvalidConstant.forEntity(
element, CompileTimeErrorCode.CONST_EVAL_FOR_ELEMENT);
case IfElement():
var condition = evaluateConstant(element.expression);
switch (condition) {
case InvalidConstant():
return condition;
case DartObjectImpl():
// If the condition is unknown, we mark this map as unknown.
if (condition.isUnknown) {
return DartObjectImpl.validWithUnknownValue(
typeSystem, mapType);
}
Constant? branchResult;
var conditionValue = condition.toBoolValue();
if (conditionValue == null) {
return InvalidConstant.forEntity(element.expression,
CompileTimeErrorCode.NON_BOOL_CONDITION);
} else if (conditionValue) {
branchResult = _buildMapConstant(
map, [element.thenElement], typeSystem, mapType);
} else if (element.elseElement != null) {
branchResult = _buildMapConstant(
map, [element.elseElement!], typeSystem, mapType);
}
if (branchResult is InvalidConstant) {
return branchResult;
}
}
case MapLiteralEntry():
var keyResult = evaluateConstant(element.key);
var valueResult = evaluateConstant(element.value);
switch (keyResult) {
case InvalidConstant():
return keyResult;
case DartObjectImpl():
switch (valueResult) {
case InvalidConstant():
return valueResult;
case DartObjectImpl():
map[keyResult] = valueResult;
}
}
case SpreadElement():
var spread = evaluateConstant(element.expression);
switch (spread) {
case InvalidConstant():
return spread;
case DartObjectImpl():
// Special case for ...?
if (spread.isNull && element.isNullAware) {
continue;
}
var mapValue = spread.toMapValue();
if (mapValue == null) {
return InvalidConstant.forEntity(element.expression,
CompileTimeErrorCode.CONST_SPREAD_EXPECTED_MAP);
}
map.addAll(mapValue);
}
}
}
return DartObjectImpl(typeSystem, mapType, MapState(map));
}
/// Add the entries produced by evaluating the given collection [element] to
/// the given [set]. Return an [InvalidConstant] if the evaluation of one or
/// more of the elements failed.
InvalidConstant? _addElementsToSet(
Set<DartObject> set, CollectionElement element) {
switch (element) {
case Expression():
var expression = evaluateConstant(element);
switch (expression) {
case InvalidConstant():
return expression;
case DartObjectImpl():
set.add(expression);
return null;
}
case ForElement():
return InvalidConstant.forEntity(
element, CompileTimeErrorCode.CONST_EVAL_FOR_ELEMENT);
case IfElement():
var condition = evaluateConstant(element.expression);
switch (condition) {
case InvalidConstant():
return condition;
case DartObjectImpl():
var conditionValue = condition.toBoolValue();
if (conditionValue == null) {
return InvalidConstant.forEntity(
element.expression, CompileTimeErrorCode.NON_BOOL_CONDITION);
} else if (conditionValue) {
return _addElementsToSet(set, element.thenElement);
} else if (element.elseElement != null) {
return _addElementsToSet(set, element.elseElement!);
}
// There's no else element, but the condition value is false.
return null;
}
case MapLiteralEntry():
return InvalidConstant.forEntity(
element, CompileTimeErrorCode.MAP_ENTRY_NOT_IN_MAP);
case SpreadElement():
var spread = evaluateConstant(element.expression);
switch (spread) {
case InvalidConstant():
return spread;
case DartObjectImpl():
// Special case for ...?
if (spread.isNull && element.isNullAware) {
return null;
}
var setValue = spread.toSetValue() ?? spread.toListValue();
if (setValue == null) {
return InvalidConstant.forEntity(element.expression,
CompileTimeErrorCode.CONST_SPREAD_EXPECTED_LIST_OR_SET);
}
set.addAll(setValue);
return null;
}
/// Builds a set constant by adding the evaluated entries of [elements] to
/// the given [set].
///
/// The [typeSystem] and [setType] are used to create a valid set constant.
/// We return an [InvalidConstant] if the evaluation of any of the elements
/// failed.
Constant _buildSetConstant(
Set<DartObjectImpl> set,
List<CollectionElement> elements,
TypeSystemImpl typeSystem,
DartType setType) {
for (var element in elements) {
switch (element) {
case Expression():
var expression = evaluateConstant(element);
switch (expression) {
case InvalidConstant():
return expression;
case DartObjectImpl():
set.add(expression);
}
case ForElement():
return InvalidConstant.forEntity(
element, CompileTimeErrorCode.CONST_EVAL_FOR_ELEMENT);
case IfElement():
var condition = evaluateConstant(element.expression);
switch (condition) {
case InvalidConstant():
return condition;
case DartObjectImpl():
// If the condition is unknown, we mark this set as unknown.
if (condition.isUnknown) {
return DartObjectImpl.validWithUnknownValue(
typeSystem, setType);
}
Constant? branchResult;
var conditionValue = condition.toBoolValue();
if (conditionValue == null) {
return InvalidConstant.forEntity(element.expression,
CompileTimeErrorCode.NON_BOOL_CONDITION);
} else if (conditionValue) {
branchResult = _buildSetConstant(
set, [element.thenElement], typeSystem, setType);
} else if (element.elseElement != null) {
branchResult = _buildSetConstant(
set, [element.elseElement!], typeSystem, setType);
}
if (branchResult is InvalidConstant) {
return branchResult;
}
}
case MapLiteralEntry():
return InvalidConstant.forEntity(
element, CompileTimeErrorCode.MAP_ENTRY_NOT_IN_MAP);
case SpreadElement():
var spread = evaluateConstant(element.expression);
switch (spread) {
case InvalidConstant():
return spread;
case DartObjectImpl():
// Special case for ...?
if (spread.isNull && element.isNullAware) {
continue;
}
var setValue = spread.toSetValue() ?? spread.toListValue();
if (setValue == null) {
return InvalidConstant.forEntity(element.expression,
CompileTimeErrorCode.CONST_SPREAD_EXPECTED_LIST_OR_SET);
}
set.addAll(setValue);
}
}
}
return DartObjectImpl(typeSystem, setType, SetState(set));
}
/// Returns the result of concatenating [astNodes].

View file

@ -192,15 +192,24 @@ class DartObjectImpl implements DartObject, Constant {
/// Create an object to represent an unknown value.
factory DartObjectImpl.validWithUnknownValue(
TypeSystemImpl typeSystem,
DartType type,
) {
TypeSystemImpl typeSystem, DartType type,
{DartType? listElementType}) {
if (type.isDartCoreBool) {
return DartObjectImpl(typeSystem, type, BoolState.UNKNOWN_VALUE);
} else if (type.isDartCoreDouble) {
return DartObjectImpl(typeSystem, type, DoubleState.UNKNOWN_VALUE);
} else if (type.isDartCoreInt) {
return DartObjectImpl(typeSystem, type, IntState.UNKNOWN_VALUE);
} else if (type.isDartCoreList) {
return DartObjectImpl(
typeSystem,
type,
ListState.unknown(
listElementType ?? typeSystem.typeProvider.dynamicType));
} else if (type.isDartCoreMap) {
return DartObjectImpl(typeSystem, type, MapState.UNKNOWN);
} else if (type.isDartCoreSet) {
return DartObjectImpl(typeSystem, type, SetState.UNKNOWN);
} else if (type.isDartCoreString) {
return DartObjectImpl(typeSystem, type, StringState.UNKNOWN_VALUE);
}
@ -2472,10 +2481,18 @@ class ListState extends InstanceState {
final DartType elementType;
final List<DartObjectImpl> elements;
/// Whether the list contains an element that has an unknown value.
final bool _isUnknown;
ListState({
required this.elementType,
required this.elements,
});
bool isUnknown = false,
}) : _isUnknown = isUnknown;
/// Creates a state that represents a list whose value is not known.
factory ListState.unknown(DartType elementType) =>
ListState(elementType: elementType, elements: [], isUnknown: true);
@override
int get hashCode {
@ -2487,6 +2504,9 @@ class ListState extends InstanceState {
return value;
}
@override
bool get isUnknown => _isUnknown;
@override
String get typeName => "List";
@ -2523,6 +2543,9 @@ class ListState extends InstanceState {
@override
BoolState isIdentical(TypeSystemImpl typeSystem, InstanceState rightOperand) {
if (isUnknown || rightOperand.isUnknown) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.from(this == rightOperand);
}
@ -2546,12 +2569,18 @@ class ListState extends InstanceState {
/// The state of an object representing a map.
class MapState extends InstanceState {
/// A state that represents a map whose value is not known.
static MapState UNKNOWN = MapState({}, isUnknown: true);
/// The entries in the map.
final Map<DartObjectImpl, DartObjectImpl> entries;
/// Initialize a newly created state to represent a map with the given
/// Whether the map contains an entry that has an unknown value.
final bool _isUnknown;
/// Initializes a newly created state to represent a map with the given
/// [entries].
MapState(this.entries);
MapState(this.entries, {bool isUnknown = false}) : _isUnknown = isUnknown;
@override
int get hashCode {
@ -2562,6 +2591,9 @@ class MapState extends InstanceState {
return value;
}
@override
bool get isUnknown => _isUnknown;
@override
String get typeName => "Map";
@ -2600,6 +2632,9 @@ class MapState extends InstanceState {
@override
BoolState isIdentical(TypeSystemImpl typeSystem, InstanceState rightOperand) {
if (isUnknown || rightOperand.isUnknown) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.from(this == rightOperand);
}
@ -2818,12 +2853,18 @@ class RecordState extends InstanceState {
/// The state of an object representing a set.
class SetState extends InstanceState {
/// A state that represents a set whose value is not known.
static SetState UNKNOWN = SetState({}, isUnknown: true);
/// The elements of the set.
final Set<DartObjectImpl> elements;
/// Initialize a newly created state to represent a set with the given
/// Whether the set contains an entry that has an unknown value.
final bool _isUnknown;
/// Initializes a newly created state to represent a set with the given
/// [elements].
SetState(this.elements);
SetState(this.elements, {bool isUnknown = false}) : _isUnknown = isUnknown;
@override
int get hashCode {
@ -2834,6 +2875,9 @@ class SetState extends InstanceState {
return value;
}
@override
bool get isUnknown => _isUnknown;
@override
String get typeName => "Set";
@ -2871,6 +2915,9 @@ class SetState extends InstanceState {
@override
BoolState isIdentical(TypeSystemImpl typeSystem, InstanceState rightOperand) {
if (isUnknown || rightOperand.isUnknown) {
return BoolState.UNKNOWN_VALUE;
}
return BoolState.from(this == rightOperand);
}

View file

@ -4476,16 +4476,166 @@ A
/// See https://github.com/dart-lang/sdk/issues/50045
test_bool_fromEnvironment_dartLibraryJsUtil() async {
await resolveTestCode('''
await assertNoErrorsInCode('''
const a = bool.fromEnvironment('dart.library.js_util');
''');
assertDartObjectText(_topLevelVar('a'), '''
final result = _topLevelVar('a');
assertDartObjectText(result, '''
<unknown> bool
variable: self::@variable::a
''');
}
test_bool_fromEnvironment_dartLibraryJsUtil_ifStatement_list() async {
await assertNoErrorsInCode('''
const a = bool.fromEnvironment('dart.library.js_util');
const x = [3, if (a) ...[1] else ...[1, 2], 4];
''');
final result = _topLevelVar('x');
assertDartObjectText(result, '''
<unknown> List<int>
variable: self::@variable::x
''');
}
test_bool_fromEnvironment_dartLibraryJsUtil_ifStatement_list_eqeq_known() async {
await assertNoErrorsInCode('''
const a = bool.fromEnvironment('dart.library.js_util');
const b = [3, if (a) ...[1] else ...[1, 2], 4];
const left = [3, 1, 2, 4] == b;
const right = b == [3, 1, 2, 4];
''');
final leftResult = _topLevelVar('left');
assertDartObjectText(leftResult, '''
<unknown> bool
variable: self::@variable::left
''');
final rightResult = _topLevelVar('right');
assertDartObjectText(rightResult, '''
<unknown> bool
variable: self::@variable::right
''');
}
test_bool_fromEnvironment_dartLibraryJsUtil_ifStatement_list_eqeq_unknown() async {
await assertNoErrorsInCode('''
const a = bool.fromEnvironment('dart.library.js_util');
const b = [3, if (a) ...[1] else ...[1, 2], 4];
const left = [3, if (a) ...[1] else ...[1, 2], 4] == b;
const right = b == [3, if (a) ...[1] else ...[1, 2], 4];
''');
final leftResult = _topLevelVar('left');
assertDartObjectText(leftResult, '''
<unknown> bool
variable: self::@variable::left
''');
final rightResult = _topLevelVar('right');
assertDartObjectText(rightResult, '''
<unknown> bool
variable: self::@variable::right
''');
}
test_bool_fromEnvironment_dartLibraryJsUtil_ifStatement_map() async {
await assertNoErrorsInCode('''
const a = bool.fromEnvironment('dart.library.js_util');
const x = {3:'3', if (a) 1:'1' else 2:'2', 4:'4'};
''');
final result = _topLevelVar('x');
assertDartObjectText(result, '''
<unknown> Map<int, String>
variable: self::@variable::x
''');
}
test_bool_fromEnvironment_dartLibraryJsUtil_ifStatement_map_eqeq_known() async {
await assertNoErrorsInCode('''
const a = bool.fromEnvironment('dart.library.js_util');
const b = {3:'3', if (a) 1:'1' else 2:'2', 4:'4'};
const left = {3:'3', 2:'2', 4:'4'} == b;
const right = b == {3:'3', 2:'2', 4:'4'};
''');
final leftResult = _topLevelVar('left');
assertDartObjectText(leftResult, '''
<unknown> bool
variable: self::@variable::left
''');
final rightResult = _topLevelVar('right');
assertDartObjectText(rightResult, '''
<unknown> bool
variable: self::@variable::right
''');
}
test_bool_fromEnvironment_dartLibraryJsUtil_ifStatement_map_eqeq_unknown() async {
await assertNoErrorsInCode('''
const a = bool.fromEnvironment('dart.library.js_util');
const b = {3:'3', if (a) 1:'1' else 2:'2', 4:'4'};
const left = {3:'3', if (a) 1:'1' else 2:'2', 4:'4'} == b;
const right = b == {3:'3', if (a) 1:'1' else 2:'2', 4:'4'};
''');
final leftResult = _topLevelVar('left');
assertDartObjectText(leftResult, '''
<unknown> bool
variable: self::@variable::left
''');
final rightResult = _topLevelVar('right');
assertDartObjectText(rightResult, '''
<unknown> bool
variable: self::@variable::right
''');
}
test_bool_fromEnvironment_dartLibraryJsUtil_ifStatement_set() async {
await assertNoErrorsInCode('''
const a = bool.fromEnvironment('dart.library.js_util');
const x = {3, if (a) ...[1] else ...[1, 2], 4};
''');
final result = _topLevelVar('x');
assertDartObjectText(result, '''
<unknown> Set<int>
variable: self::@variable::x
''');
}
test_bool_fromEnvironment_dartLibraryJsUtil_ifStatement_set_eqeq_known() async {
await assertNoErrorsInCode('''
const a = bool.fromEnvironment('dart.library.js_util');
const b = {3, if (a) ...[1] else ...[1, 2], 4};
const left = {3, 1, 4} == b;
const right = b == {3, 1, 4};
''');
final leftResult = _topLevelVar('left');
assertDartObjectText(leftResult, '''
<unknown> bool
variable: self::@variable::left
''');
final rightResult = _topLevelVar('right');
assertDartObjectText(rightResult, '''
<unknown> bool
variable: self::@variable::right
''');
}
test_bool_fromEnvironment_dartLibraryJsUtil_ifStatement_set_eqeq_unknown() async {
await assertNoErrorsInCode('''
const a = bool.fromEnvironment('dart.library.js_util');
const b = {3, if (a) ...[1] else ...[1, 2], 4};
const left = {3, if (a) ...[1] else ...[1, 2], 4} == b;
const right = b == {3, if (a) ...[1] else ...[1, 2], 4};
''');
final leftResult = _topLevelVar('left');
assertDartObjectText(leftResult, '''
<unknown> bool
variable: self::@variable::left
''');
final rightResult = _topLevelVar('right');
assertDartObjectText(rightResult, '''
<unknown> bool
variable: self::@variable::right
''');
}
test_fieldInitializer_functionReference_withTypeParameter() async {
await assertNoErrorsInCode('''
void g<U>(U a) {}

View file

@ -198,17 +198,6 @@ B g() => B([f()]);
assertCanBeConst("B([", false);
}
void test_false_argument_list_nonBool() async {
await resolve('''
const bool kIsWeb = bool.fromEnvironment('dart.library.js_util');
class A {
const A(List<int> l);
}
A f() => A([if (!kIsWeb) ...[1, 2, 3] else ...[1]]);
''');
assertCanBeConst("A([", false);
}
void test_false_argument_nonConstConstructor() async {
await resolve('''
class A {}
@ -274,6 +263,17 @@ f<U>() => A<U>();
assertCanBeConst("A<U>", false);
}
void test_true_argument_list_nonBool() async {
await resolve('''
const bool kIsWeb = bool.fromEnvironment('dart.library.js_util');
class A {
const A(List<int> l);
}
A f() => A([if (!kIsWeb) ...[1, 2, 3] else ...[1]]);
''');
assertCanBeConst("A([", true);
}
void test_true_computeDependencies() async {
newFile('$testPackageLibPath/a.dart', r'''
const a = 0;