Skip to content

Commit c9cb9c6

Browse files
committed
Infer type arguments in constructor redirections.
[email protected] Bug: #30855 Change-Id: I05e9102fe7b7a304be6ac6dd4d59e11bdafee59c Reviewed-on: https://dart-review.googlesource.com/46575 Reviewed-by: Brian Wilkerson <[email protected]>
1 parent 236c8da commit c9cb9c6

File tree

3 files changed

+98
-2
lines changed

3 files changed

+98
-2
lines changed

pkg/analyzer/lib/src/dart/analysis/driver.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ class AnalysisDriver implements AnalysisDriverGeneric {
9494
/**
9595
* The version of data format, should be incremented on every format change.
9696
*/
97-
static const int DATA_VERSION = 51;
97+
static const int DATA_VERSION = 52;
9898

9999
/**
100100
* The number of exception contexts allowed to write. Once this field is

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

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8401,7 +8401,13 @@ class TypeNameResolver {
84018401
typeSystem.instantiateTypeFormalsToBounds(element.typeParameters);
84028402
type = element.typeAfterSubstitution(typeArguments) ?? dynamicType;
84038403
} else {
8404-
type = typeSystem.instantiateToBounds(type);
8404+
DartType redirectedType =
8405+
_inferTypeArgumentsForRedirectedConstructor(node, type);
8406+
if (redirectedType != null) {
8407+
type = redirectedType;
8408+
} else {
8409+
type = typeSystem.instantiateToBounds(type);
8410+
}
84058411
}
84068412
}
84078413
typeName.staticType = type;
@@ -8509,6 +8515,34 @@ class TypeNameResolver {
85098515
return type;
85108516
}
85118517

8518+
/**
8519+
* If the [node] is the type name in a redirected factory constructor,
8520+
* infer type arguments using the enclosing class declaration. Return `null`
8521+
* otherwise.
8522+
*/
8523+
DartType _inferTypeArgumentsForRedirectedConstructor(
8524+
TypeName node, DartType type) {
8525+
AstNode constructorName = node.parent;
8526+
AstNode enclosingConstructor = constructorName?.parent;
8527+
TypeSystem ts = typeSystem;
8528+
if (constructorName is ConstructorName &&
8529+
enclosingConstructor is ConstructorDeclaration &&
8530+
enclosingConstructor.redirectedConstructor == constructorName &&
8531+
type is InterfaceType &&
8532+
ts is StrongTypeSystemImpl) {
8533+
ClassDeclaration enclosingClassNode = enclosingConstructor.parent;
8534+
ClassElement enclosingClassElement = enclosingClassNode.element;
8535+
if (enclosingClassElement == type.element) {
8536+
return type;
8537+
} else {
8538+
InterfaceType contextType = enclosingClassElement.type;
8539+
return ts.inferGenericFunctionOrType(
8540+
type, const <ParameterElement>[], const <DartType>[], contextType);
8541+
}
8542+
}
8543+
return null;
8544+
}
8545+
85128546
/**
85138547
* Checks if the given [typeName] is used as the type in an as expression.
85148548
*/

pkg/analyzer/test/generated/strong_mode_test.dart

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2476,6 +2476,68 @@ num test(Iterable values) => values.fold(values.first as num, max);
24762476
_isInstantiationOf(_hasElement(elementA))([_isNum, _isNum])(type);
24772477
}
24782478

2479+
test_redirectedConstructor_named() async {
2480+
Source source = addSource(r'''
2481+
class A<T, U> implements B<T, U> {
2482+
A.named();
2483+
}
2484+
2485+
class B<T2, U2> {
2486+
factory B() = A.named;
2487+
}
2488+
''');
2489+
TestAnalysisResult result = await computeAnalysisResult(source);
2490+
assertNoErrors(source);
2491+
2492+
ClassDeclaration b = result.unit.declarations[1];
2493+
ConstructorDeclaration bConstructor = b.members[0];
2494+
ConstructorName redirected = bConstructor.redirectedConstructor;
2495+
2496+
TypeName typeName = redirected.type;
2497+
expect(typeName.type.toString(), 'A<T2, U2>');
2498+
expect(typeName.type.toString(), 'A<T2, U2>');
2499+
2500+
var constructorMember = redirected.staticElement;
2501+
expect(constructorMember.toString(), 'A.named() → A<T2, U2>');
2502+
expect(redirected.name.staticElement, constructorMember);
2503+
}
2504+
2505+
test_redirectedConstructor_self() async {
2506+
Source source = addSource(r'''
2507+
class A<T> {
2508+
A();
2509+
factory A.redirected() = A;
2510+
}
2511+
''');
2512+
await computeAnalysisResult(source);
2513+
assertNoErrors(source);
2514+
}
2515+
2516+
test_redirectedConstructor_unnamed() async {
2517+
Source source = addSource(r'''
2518+
class A<T, U> implements B<T, U> {
2519+
A();
2520+
}
2521+
2522+
class B<T2, U2> {
2523+
factory B() = A;
2524+
}
2525+
''');
2526+
TestAnalysisResult result = await computeAnalysisResult(source);
2527+
assertNoErrors(source);
2528+
2529+
ClassDeclaration b = result.unit.declarations[1];
2530+
ConstructorDeclaration bConstructor = b.members[0];
2531+
ConstructorName redirected = bConstructor.redirectedConstructor;
2532+
2533+
TypeName typeName = redirected.type;
2534+
expect(typeName.type.toString(), 'A<T2, U2>');
2535+
expect(typeName.type.toString(), 'A<T2, U2>');
2536+
2537+
expect(redirected.name, isNull);
2538+
expect(redirected.staticElement.toString(), 'A() → A<T2, U2>');
2539+
}
2540+
24792541
test_redirectingConstructor_propagation() async {
24802542
String code = r'''
24812543
class A {

0 commit comments

Comments
 (0)