Skip to content

Commit f2bd00a

Browse files
scheglovCommit Queue
authored and
Commit Queue
committed
Rewrite expressions in ConstantPattern(s) into TypeLiteral when appropriate.
Bug: https://github.com/dart-lang/linter/issues/4195 Change-Id: I5cf06da31aed9347002684580c106747a057e1f1 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/290701 Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Konstantin Shcheglov <[email protected]>
1 parent 443d6d3 commit f2bd00a

8 files changed

+244
-6
lines changed

pkg/analyzer/lib/src/dart/constant/evaluation.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -936,6 +936,8 @@ class ConstantVisitor extends UnifyingAstVisitor<DartObjectImpl> {
936936

937937
if (!_isNonNullableByDefault && hasTypeParameterReference(type)) {
938938
return super.visitNamedType(node);
939+
} else {
940+
node.name.accept(this);
939941
}
940942

941943
if (_substitution != null) {

pkg/analyzer/lib/src/dart/resolver/ast_rewrite.dart

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -251,15 +251,21 @@ class AstRewriter {
251251
return node;
252252
}
253253
var prefix = node.prefix;
254-
var element = nameScope.lookup(prefix.name).getter;
255-
if (element is InterfaceElement) {
254+
var prefixElement = nameScope.lookup(prefix.name).getter;
255+
if (parent is ConstantPattern && prefixElement is PrefixElement) {
256+
final element = prefixElement.scope.lookup(node.identifier.name).getter;
257+
if (element is TypeDefiningElement) {
258+
return _toPatternTypeLiteral(parent, node);
259+
}
260+
}
261+
if (prefixElement is InterfaceElement) {
256262
// Example:
257263
// class C { C.named(); }
258264
// C.named
259265
return _toConstructorReference_prefixed(
260-
node: node, classElement: element);
261-
} else if (element is TypeAliasElement) {
262-
var aliasedType = element.aliasedType;
266+
node: node, classElement: prefixElement);
267+
} else if (prefixElement is TypeAliasElement) {
268+
var aliasedType = prefixElement.aliasedType;
263269
if (aliasedType is InterfaceType) {
264270
// Example:
265271
// class C { C.named(); }
@@ -376,6 +382,18 @@ class AstRewriter {
376382
return node;
377383
}
378384

385+
AstNode simpleIdentifier(Scope nameScope, SimpleIdentifierImpl node) {
386+
final parent = node.parent;
387+
if (parent is ConstantPattern) {
388+
final element = nameScope.lookup(node.name).getter;
389+
if (element is TypeDefiningElement) {
390+
return _toPatternTypeLiteral(parent, node);
391+
}
392+
}
393+
394+
return node;
395+
}
396+
379397
AstNode _instanceCreation_prefix_type_name({
380398
required MethodInvocationImpl node,
381399
required PrefixedIdentifierImpl typeNameIdentifier,
@@ -627,4 +645,20 @@ class AstRewriter {
627645
NodeReplacer.replace(node, methodInvocation);
628646
return methodInvocation;
629647
}
648+
649+
TypeLiteralImpl _toPatternTypeLiteral(
650+
ConstantPattern parent,
651+
IdentifierImpl node,
652+
) {
653+
final result = TypeLiteralImpl(
654+
typeName: NamedTypeImpl(
655+
name: node,
656+
typeArguments: null,
657+
question: null,
658+
),
659+
);
660+
result.staticType = _typeProvider.typeType;
661+
NodeReplacer.replace(node, result, parent: parent);
662+
return result;
663+
}
630664
}

pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1156,6 +1156,16 @@ class ResolutionVisitor extends RecursiveAstVisitor<void> {
11561156
_setOrCreateMetadataElements(element, node.metadata);
11571157
}
11581158

1159+
@override
1160+
void visitSimpleIdentifier(covariant SimpleIdentifierImpl node) {
1161+
final newNode = _astRewriter.simpleIdentifier(_nameScope, node);
1162+
if (newNode != node) {
1163+
return newNode.accept(this);
1164+
}
1165+
1166+
super.visitSimpleIdentifier(node);
1167+
}
1168+
11591169
@override
11601170
void visitSuperFormalParameter(covariant SuperFormalParameterImpl node) {
11611171
SuperFormalParameterElementImpl element;

pkg/analyzer/test/src/dart/constant/evaluation_test.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1017,7 +1017,9 @@ class C<U> {
10171017
}
10181018
}
10191019
''');
1020-
var result = _evaluateConstantLocal('g')!;
1020+
var result = _evaluateConstantLocal('g', errorCodes: [
1021+
CompileTimeErrorCode.INVALID_CONSTANT,
1022+
])!;
10211023
assertType(result.type, 'void Function(U)');
10221024
assertElement(result.toFunctionValue(), findElement.topFunction('f'));
10231025
_assertTypeArguments(result, ['U']);

pkg/analyzer/test/src/dart/resolution/constant_pattern_test.dart

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,151 @@ ConstantPattern
279279
''');
280280
}
281281

282+
test_expression_typeLiteral_notPrefixed() async {
283+
await assertNoErrorsInCode(r'''
284+
void f(Object? x) {
285+
if (x case int) {}
286+
}
287+
''');
288+
final node = findNode.singleGuardedPattern.pattern;
289+
assertResolvedNodeText(node, r'''
290+
ConstantPattern
291+
expression: TypeLiteral
292+
type: NamedType
293+
name: SimpleIdentifier
294+
token: int
295+
staticElement: dart:core::@class::int
296+
staticType: null
297+
type: int
298+
staticType: Type
299+
matchedValueType: Object?
300+
''');
301+
}
302+
303+
test_expression_typeLiteral_notPrefixed_nested() async {
304+
await assertNoErrorsInCode(r'''
305+
void f(Object? x) {
306+
if (x case [0, int]) {}
307+
}
308+
''');
309+
final node = findNode.singleGuardedPattern.pattern;
310+
assertResolvedNodeText(node, r'''
311+
ListPattern
312+
leftBracket: [
313+
elements
314+
ConstantPattern
315+
expression: IntegerLiteral
316+
literal: 0
317+
staticType: int
318+
matchedValueType: Object?
319+
ConstantPattern
320+
expression: TypeLiteral
321+
type: NamedType
322+
name: SimpleIdentifier
323+
token: int
324+
staticElement: dart:core::@class::int
325+
staticType: null
326+
type: int
327+
staticType: Type
328+
matchedValueType: Object?
329+
rightBracket: ]
330+
matchedValueType: Object?
331+
requiredType: List<Object?>
332+
''');
333+
}
334+
335+
test_expression_typeLiteral_notPrefixed_typeAlias() async {
336+
await assertNoErrorsInCode(r'''
337+
typedef A = int;
338+
339+
void f(Object? x) {
340+
if (x case A) {}
341+
}
342+
''');
343+
final node = findNode.singleGuardedPattern.pattern;
344+
assertResolvedNodeText(node, r'''
345+
ConstantPattern
346+
expression: TypeLiteral
347+
type: NamedType
348+
name: SimpleIdentifier
349+
token: A
350+
staticElement: self::@typeAlias::A
351+
staticType: null
352+
type: int
353+
alias: self::@typeAlias::A
354+
staticType: Type
355+
matchedValueType: Object?
356+
''');
357+
}
358+
359+
test_expression_typeLiteral_prefixed() async {
360+
await assertNoErrorsInCode(r'''
361+
import 'dart:math' as math;
362+
363+
void f(Object? x) {
364+
if (x case math.Random) {}
365+
}
366+
''');
367+
final node = findNode.singleGuardedPattern.pattern;
368+
assertResolvedNodeText(node, r'''
369+
ConstantPattern
370+
expression: TypeLiteral
371+
type: NamedType
372+
name: PrefixedIdentifier
373+
prefix: SimpleIdentifier
374+
token: math
375+
staticElement: self::@prefix::math
376+
staticType: null
377+
period: .
378+
identifier: SimpleIdentifier
379+
token: Random
380+
staticElement: dart:math::@class::Random
381+
staticType: null
382+
staticElement: dart:math::@class::Random
383+
staticType: null
384+
type: Random
385+
staticType: Type
386+
matchedValueType: Object?
387+
''');
388+
}
389+
390+
test_expression_typeLiteral_prefixed_typeAlias() async {
391+
newFile('$testPackageLibPath/a.dart', r'''
392+
typedef A = int;
393+
''');
394+
395+
await assertNoErrorsInCode(r'''
396+
import 'a.dart' as prefix;
397+
398+
void f(Object? x) {
399+
if (x case prefix.A) {}
400+
}
401+
''');
402+
403+
final node = findNode.singleGuardedPattern.pattern;
404+
assertResolvedNodeText(node, r'''
405+
ConstantPattern
406+
expression: TypeLiteral
407+
type: NamedType
408+
name: PrefixedIdentifier
409+
prefix: SimpleIdentifier
410+
token: prefix
411+
staticElement: self::@prefix::prefix
412+
staticType: null
413+
period: .
414+
identifier: SimpleIdentifier
415+
token: A
416+
staticElement: package:test/a.dart::@typeAlias::A
417+
staticType: null
418+
staticElement: package:test/a.dart::@typeAlias::A
419+
staticType: null
420+
type: int
421+
alias: package:test/a.dart::@typeAlias::A
422+
staticType: Type
423+
matchedValueType: Object?
424+
''');
425+
}
426+
282427
test_location_ifCase() async {
283428
await assertNoErrorsInCode(r'''
284429
void f(x) {

pkg/analyzer/test/src/diagnostics/const_with_type_parameters_test.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ class A<U> {
144144
''', [
145145
error(CompileTimeErrorCode.CONST_WITH_TYPE_PARAMETERS_FUNCTION_TEAROFF,
146146
65, 1),
147+
error(CompileTimeErrorCode.NON_CONSTANT_DEFAULT_VALUE, 65, 1),
147148
]);
148149
}
149150

@@ -159,6 +160,8 @@ class A<U> {
159160
error(HintCode.UNUSED_LOCAL_VARIABLE, 54, 1),
160161
error(CompileTimeErrorCode.CONST_WITH_TYPE_PARAMETERS_FUNCTION_TEAROFF,
161162
60, 1),
163+
error(CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE, 60,
164+
1),
162165
]);
163166
}
164167

pkg/analyzer/test/src/diagnostics/constant_pattern_with_non_constant_expression_test.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,4 +558,15 @@ GuardedPattern
558558
matchedValueType: dynamic
559559
''');
560560
}
561+
562+
test_typeLiteral_typeParameter() async {
563+
await assertErrorsInCode(r'''
564+
void f<T>(x) {
565+
if (x case T) {}
566+
}
567+
''', [
568+
error(CompileTimeErrorCode.CONSTANT_PATTERN_WITH_NON_CONSTANT_EXPRESSION,
569+
28, 1),
570+
]);
571+
}
561572
}

pkg/analyzer/test/src/diagnostics/non_constant_case_expression_from_deferred_library_test.dart

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,37 @@ void f(int e) {
110110
error(expectedErrorCode, 74, 1),
111111
]);
112112
}
113+
114+
test_simple_typeLiteral() async {
115+
newFile('$testPackageLibPath/a.dart', '''
116+
class A {}
117+
''');
118+
119+
final ErrorCode expectedErrorCode;
120+
switch (_variant) {
121+
case _Variant.nullSafe:
122+
expectedErrorCode = CompileTimeErrorCode
123+
.NON_CONSTANT_CASE_EXPRESSION_FROM_DEFERRED_LIBRARY;
124+
break;
125+
case _Variant.patterns:
126+
expectedErrorCode =
127+
CompileTimeErrorCode.PATTERN_CONSTANT_FROM_DEFERRED_LIBRARY;
128+
break;
129+
}
130+
131+
await assertErrorsInCode('''
132+
import 'a.dart' deferred as a;
133+
134+
void f(Object? x) {
135+
switch (x) {
136+
case a.A:
137+
break;
138+
}
139+
}
140+
''', [
141+
error(expectedErrorCode, 78, 1),
142+
]);
143+
}
113144
}
114145

115146
enum _Variant { nullSafe, patterns }

0 commit comments

Comments
 (0)