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;
/// 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(
MethodInvocationImpl node, List<WhyNotPromotedGetter> whyNotPromotedList,
{required DartType contextType}) {
@ -142,9 +146,9 @@ class MethodInvocationResolver with ScopeHelpers {
}
if (receiverType is NeverTypeImpl) {
_resolveReceiverNever(node, receiver, receiverType, whyNotPromotedList,
contextType: contextType);
return null;
return _resolveReceiverNever(
node, receiver, receiverType, whyNotPromotedList,
contextType: contextType, nameNode: nameNode, name: name);
}
if (receiverType is VoidType) {
@ -344,6 +348,10 @@ class MethodInvocationResolver with ScopeHelpers {
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(
MethodInvocationImpl node,
Identifier receiver,
@ -380,6 +388,11 @@ class MethodInvocationResolver with ScopeHelpers {
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(
MethodInvocationImpl node,
ExtensionOverride override,
@ -468,9 +481,20 @@ class MethodInvocationResolver with ScopeHelpers {
.resolveInvocation(rawType: rawType);
}
void _resolveReceiverNever(MethodInvocationImpl node, Expression receiver,
DartType receiverType, List<WhyNotPromotedGetter> whyNotPromotedList,
{required DartType contextType}) {
/// Resolves the method invocation, [node], as an instance invocation on an
/// expression of type `Never` or `Never?`.
///
/// 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();
if (receiverType == NeverTypeImpl.instanceNullable) {
@ -485,16 +509,19 @@ class MethodInvocationResolver with ScopeHelpers {
whyNotPromotedList,
contextType: contextType,
);
return null;
} else {
_setInvalidTypeResolution(node,
whyNotPromotedList: whyNotPromotedList, contextType: contextType);
_resolver.nullableDereferenceVerifier.report(
CompileTimeErrorCode.UNCHECKED_METHOD_INVOCATION_OF_NULLABLE_VALUE,
methodName,
receiverType,
return _resolveReceiverType(
node: node,
receiver: receiver,
receiverType: receiverType,
nameNode: nameNode,
name: name,
receiverErrorNode: receiver,
whyNotPromotedList: whyNotPromotedList,
contextType: contextType,
);
}
return;
}
if (receiverType == NeverTypeImpl.instance) {
@ -514,10 +541,16 @@ class MethodInvocationResolver with ScopeHelpers {
node.methodName.setPseudoExpressionStaticType(_dynamicType);
node.staticInvokeType = _dynamicType;
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(
MethodInvocationImpl node,
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(
MethodInvocationImpl node,
PrefixElement prefix,
@ -679,6 +717,11 @@ class MethodInvocationResolver with ScopeHelpers {
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(
MethodInvocationImpl node,
SuperExpression receiver,
@ -740,6 +783,10 @@ class MethodInvocationResolver with ScopeHelpers {
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({
required MethodInvocationImpl node,
required Expression? receiver,
@ -830,6 +877,11 @@ class MethodInvocationResolver with ScopeHelpers {
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(
MethodInvocationImpl node,
InterfaceElement receiver,
@ -882,12 +934,14 @@ class MethodInvocationResolver with ScopeHelpers {
return null;
}
/// Rewrites [node] as a [FunctionExpressionInvocation].
///
/// We have identified that [node] is not a real [MethodInvocation],
/// because it does not invoke a method, but instead invokes the result
/// of a getter execution, or implicitly invokes the `call` method of
/// an [InterfaceType]. So, it should be represented as instead as a
/// [FunctionExpressionInvocation].
FunctionExpressionInvocation? _rewriteAsFunctionExpressionInvocation(
FunctionExpressionInvocation _rewriteAsFunctionExpressionInvocation(
MethodInvocationImpl node, DartType getterReturnType,
{bool isSuperAccess = false}) {
var targetType = _typeSystem.resolveToBound(getterReturnType);

View file

@ -267,6 +267,10 @@ class ElementResolver {
_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,
{List<WhyNotPromotedGetter>? whyNotPromotedList,
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 {
await assertNoErrorsInCode(r'''
class A {