analyzer: Resolve extension methods on nullable Never type

Fixes https://github.com/dart-lang/sdk/issues/55000

Cq-Include-Trybots: luci.dart.try:flutter-analyze-try,analyzer-win-release-try,pkg-win-release-try
Change-Id: Ie71d4762bc294028787c28976b2e0a7e604ab2fa
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/372880
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Sam Rawlins <srawlins@google.com>
This commit is contained in:
Sam Rawlins 2024-06-24 17:44:09 +00:00 committed by Commit Queue
parent 89d9912790
commit 4d20dea313
3 changed files with 85 additions and 15 deletions

View file

@ -71,6 +71,10 @@ class MethodInvocationResolver with ScopeHelpers {
TypeSystemImpl get _typeSystem => _resolver.typeSystem; TypeSystemImpl get _typeSystem => _resolver.typeSystem;
/// Resolves the method invocation, [node].
///
/// If [node] is rewritten to be a [FunctionExpressionInvocation] in the
/// process, then returns that new node. Otherwise, returns `null`.
FunctionExpressionInvocation? resolve( FunctionExpressionInvocation? resolve(
MethodInvocationImpl node, List<WhyNotPromotedGetter> whyNotPromotedList, MethodInvocationImpl node, List<WhyNotPromotedGetter> whyNotPromotedList,
{required DartType contextType}) { {required DartType contextType}) {
@ -142,9 +146,9 @@ class MethodInvocationResolver with ScopeHelpers {
} }
if (receiverType is NeverTypeImpl) { if (receiverType is NeverTypeImpl) {
_resolveReceiverNever(node, receiver, receiverType, whyNotPromotedList, return _resolveReceiverNever(
contextType: contextType); node, receiver, receiverType, whyNotPromotedList,
return null; contextType: contextType, nameNode: nameNode, name: name);
} }
if (receiverType is VoidType) { if (receiverType is VoidType) {
@ -344,6 +348,10 @@ class MethodInvocationResolver with ScopeHelpers {
return null; return null;
} }
/// Resolves the method invocation, [node], as an extension member.
///
/// If [node] is rewritten to be a [FunctionExpressionInvocation] in the
/// process, then returns that new node. Otherwise, returns `null`.
FunctionExpressionInvocation? _resolveExtensionMember( FunctionExpressionInvocation? _resolveExtensionMember(
MethodInvocationImpl node, MethodInvocationImpl node,
Identifier receiver, Identifier receiver,
@ -380,6 +388,11 @@ class MethodInvocationResolver with ScopeHelpers {
return null; return null;
} }
/// Resolves the method invocation, [node], as called on an extension
/// override.
///
/// If [node] is rewritten to be a [FunctionExpressionInvocation] in the
/// process, then returns that new node. Otherwise, returns `null`.
FunctionExpressionInvocation? _resolveExtensionOverride( FunctionExpressionInvocation? _resolveExtensionOverride(
MethodInvocationImpl node, MethodInvocationImpl node,
ExtensionOverride override, ExtensionOverride override,
@ -468,9 +481,20 @@ class MethodInvocationResolver with ScopeHelpers {
.resolveInvocation(rawType: rawType); .resolveInvocation(rawType: rawType);
} }
void _resolveReceiverNever(MethodInvocationImpl node, Expression receiver, /// Resolves the method invocation, [node], as an instance invocation on an
DartType receiverType, List<WhyNotPromotedGetter> whyNotPromotedList, /// expression of type `Never` or `Never?`.
{required DartType contextType}) { ///
/// If [node] is rewritten to be a [FunctionExpressionInvocation] in the
/// process, then returns that new node. Otherwise, returns `null`.
FunctionExpressionInvocation? _resolveReceiverNever(
MethodInvocationImpl node,
Expression receiver,
DartType receiverType,
List<WhyNotPromotedGetter> whyNotPromotedList, {
required DartType contextType,
required SimpleIdentifierImpl nameNode,
required String name,
}) {
_setExplicitTypeArgumentTypes(); _setExplicitTypeArgumentTypes();
if (receiverType == NeverTypeImpl.instanceNullable) { if (receiverType == NeverTypeImpl.instanceNullable) {
@ -485,16 +509,19 @@ class MethodInvocationResolver with ScopeHelpers {
whyNotPromotedList, whyNotPromotedList,
contextType: contextType, contextType: contextType,
); );
return null;
} else { } else {
_setInvalidTypeResolution(node, return _resolveReceiverType(
whyNotPromotedList: whyNotPromotedList, contextType: contextType); node: node,
_resolver.nullableDereferenceVerifier.report( receiver: receiver,
CompileTimeErrorCode.UNCHECKED_METHOD_INVOCATION_OF_NULLABLE_VALUE, receiverType: receiverType,
methodName, nameNode: nameNode,
receiverType, name: name,
receiverErrorNode: receiver,
whyNotPromotedList: whyNotPromotedList,
contextType: contextType,
); );
} }
return;
} }
if (receiverType == NeverTypeImpl.instance) { if (receiverType == NeverTypeImpl.instance) {
@ -514,10 +541,16 @@ class MethodInvocationResolver with ScopeHelpers {
node.methodName.setPseudoExpressionStaticType(_dynamicType); node.methodName.setPseudoExpressionStaticType(_dynamicType);
node.staticInvokeType = _dynamicType; node.staticInvokeType = _dynamicType;
node.recordStaticType(NeverTypeImpl.instance, resolver: _resolver); node.recordStaticType(NeverTypeImpl.instance, resolver: _resolver);
return; return null;
} }
return null;
} }
/// Resolves the method invocation, [node], as an instance invocation on an
/// expression of type `Null`.
///
/// If [node] is rewritten to be a [FunctionExpressionInvocation] in the
/// process, then returns that new node. Otherwise, returns `null`.
FunctionExpressionInvocation? _resolveReceiverNull( FunctionExpressionInvocation? _resolveReceiverNull(
MethodInvocationImpl node, MethodInvocationImpl node,
SimpleIdentifierImpl nameNode, SimpleIdentifierImpl nameNode,
@ -620,6 +653,11 @@ class MethodInvocationResolver with ScopeHelpers {
); );
} }
/// Resolves the method invocation, [node], as a top-level function
/// invocation, referenced with a prefix.
///
/// If [node] is rewritten to be a [FunctionExpressionInvocation] in the
/// process, then returns that new node. Otherwise, returns `null`.
FunctionExpressionInvocation? _resolveReceiverPrefix( FunctionExpressionInvocation? _resolveReceiverPrefix(
MethodInvocationImpl node, MethodInvocationImpl node,
PrefixElement prefix, PrefixElement prefix,
@ -679,6 +717,11 @@ class MethodInvocationResolver with ScopeHelpers {
return null; return null;
} }
/// Resolves the method invocation, [node], as an instance invocation a
/// `super` expression.
///
/// If [node] is rewritten to be a [FunctionExpressionInvocation] in the
/// process, then returns that new node. Otherwise, returns `null`.
FunctionExpressionInvocation? _resolveReceiverSuper( FunctionExpressionInvocation? _resolveReceiverSuper(
MethodInvocationImpl node, MethodInvocationImpl node,
SuperExpression receiver, SuperExpression receiver,
@ -740,6 +783,10 @@ class MethodInvocationResolver with ScopeHelpers {
return null; return null;
} }
/// Resolves the type of the receiver of the method invocation, [node].
///
/// If [node] is rewritten to be a [FunctionExpressionInvocation] in the
/// process, then returns that new node. Otherwise, returns `null`.
FunctionExpressionInvocation? _resolveReceiverType({ FunctionExpressionInvocation? _resolveReceiverType({
required MethodInvocationImpl node, required MethodInvocationImpl node,
required Expression? receiver, required Expression? receiver,
@ -830,6 +877,11 @@ class MethodInvocationResolver with ScopeHelpers {
return null; return null;
} }
/// Resolves the method invocation, [node], as an method invocation with a
/// type literal target.
///
/// If [node] is rewritten to be a [FunctionExpressionInvocation] in the
/// process, then returns that new node. Otherwise, returns `null`.
FunctionExpressionInvocation? _resolveReceiverTypeLiteral( FunctionExpressionInvocation? _resolveReceiverTypeLiteral(
MethodInvocationImpl node, MethodInvocationImpl node,
InterfaceElement receiver, InterfaceElement receiver,
@ -882,12 +934,14 @@ class MethodInvocationResolver with ScopeHelpers {
return null; return null;
} }
/// Rewrites [node] as a [FunctionExpressionInvocation].
///
/// We have identified that [node] is not a real [MethodInvocation], /// We have identified that [node] is not a real [MethodInvocation],
/// because it does not invoke a method, but instead invokes the result /// because it does not invoke a method, but instead invokes the result
/// of a getter execution, or implicitly invokes the `call` method of /// of a getter execution, or implicitly invokes the `call` method of
/// an [InterfaceType]. So, it should be represented as instead as a /// an [InterfaceType]. So, it should be represented as instead as a
/// [FunctionExpressionInvocation]. /// [FunctionExpressionInvocation].
FunctionExpressionInvocation? _rewriteAsFunctionExpressionInvocation( FunctionExpressionInvocation _rewriteAsFunctionExpressionInvocation(
MethodInvocationImpl node, DartType getterReturnType, MethodInvocationImpl node, DartType getterReturnType,
{bool isSuperAccess = false}) { {bool isSuperAccess = false}) {
var targetType = _typeSystem.resolveToBound(getterReturnType); var targetType = _typeSystem.resolveToBound(getterReturnType);

View file

@ -267,6 +267,10 @@ class ElementResolver {
_resolveAnnotations(node.metadata); _resolveAnnotations(node.metadata);
} }
/// Resolves the method invocation, [node].
///
/// If [node] is rewritten to be a [FunctionExpressionInvocation] in the
/// process, then returns that new node. Otherwise, returns `null`.
FunctionExpressionInvocation? visitMethodInvocation(MethodInvocation node, FunctionExpressionInvocation? visitMethodInvocation(MethodInvocation node,
{List<WhyNotPromotedGetter>? whyNotPromotedList, {List<WhyNotPromotedGetter>? whyNotPromotedList,
required DartType contextType}) { required DartType contextType}) {

View file

@ -216,6 +216,18 @@ extension E on A? {
]); ]);
} }
test_methodInvocation_nuverNullable_extensionMethod() async {
await assertNoErrorsInCode(r'''
extension<X> on X {
X m() => this;
}
Future<void> f(Never? x) async {
(await x).m();
}
''');
}
test_prefixExpression_minus_nonNullable() async { test_prefixExpression_minus_nonNullable() async {
await assertNoErrorsInCode(r''' await assertNoErrorsInCode(r'''
class A { class A {