diff --git a/pkg/analyzer/lib/src/dart/resolver/type_property_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/type_property_resolver.dart index d1d9a15bf21..fa98571cf79 100644 --- a/pkg/analyzer/lib/src/dart/resolver/type_property_resolver.dart +++ b/pkg/analyzer/lib/src/dart/resolver/type_property_resolver.dart @@ -5,6 +5,7 @@ import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/ast/syntactic_entity.dart'; import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/dart/element/nullability_suffix.dart'; import 'package:analyzer/dart/element/type.dart'; import 'package:analyzer/diagnostic/diagnostic.dart'; import 'package:analyzer/src/dart/element/element.dart'; @@ -87,8 +88,14 @@ class TypePropertyResolver { return _toResult(); } - if (_typeSystem.isPotentiallyNullable(receiverType) && - !receiverType.isExtensionType) { + bool isNullable; + if (receiverType.isExtensionType) { + isNullable = receiverType.nullabilitySuffix == NullabilitySuffix.question; + } else { + isNullable = _typeSystem.isPotentiallyNullable(receiverType); + } + + if (isNullable) { _lookupInterfaceType(_typeProvider.objectType); if (_hasGetterOrSetter) { return _toResult(); diff --git a/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart b/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart index a016b8facd0..ca85e412eec 100644 --- a/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart +++ b/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart @@ -4320,6 +4320,71 @@ MethodInvocation '''); } + test_hasReceiver_interfaceType_extensionType_declared_nullableType() async { + await assertErrorsInCode(r''' +extension type A(int it) { + int foo() => 0; +} + +void f(A? a) { + a.foo(); +} +''', [ + error(CompileTimeErrorCode.UNCHECKED_METHOD_INVOCATION_OF_NULLABLE_VALUE, + 67, 3), + ]); + + final node = findNode.singleMethodInvocation; + assertResolvedNodeText(node, r''' +MethodInvocation + target: SimpleIdentifier + token: a + staticElement: self::@function::f::@parameter::a + staticType: A? + operator: . + methodName: SimpleIdentifier + token: foo + staticElement: self::@extensionType::A::@method::foo + staticType: int Function() + argumentList: ArgumentList + leftParenthesis: ( + rightParenthesis: ) + staticInvokeType: int Function() + staticType: int +'''); + } + + test_hasReceiver_interfaceType_extensionType_declared_nullableType_nullAware() async { + await assertNoErrorsInCode(r''' +extension type A(int it) { + int foo() => 0; +} + +void f(A? a) { + a?.foo(); +} +'''); + + final node = findNode.singleMethodInvocation; + assertResolvedNodeText(node, r''' +MethodInvocation + target: SimpleIdentifier + token: a + staticElement: self::@function::f::@parameter::a + staticType: A? + operator: ?. + methodName: SimpleIdentifier + token: foo + staticElement: self::@extensionType::A::@method::foo + staticType: int Function() + argumentList: ArgumentList + leftParenthesis: ( + rightParenthesis: ) + staticInvokeType: int Function() + staticType: int? +'''); + } + test_hasReceiver_interfaceType_extensionType_exposed() async { await assertNoErrorsInCode(r''' class A { diff --git a/pkg/analyzer/test/src/dart/resolution/prefixed_identifier_test.dart b/pkg/analyzer/test/src/dart/resolution/prefixed_identifier_test.dart index 6dbebf6a286..df51363047a 100644 --- a/pkg/analyzer/test/src/dart/resolution/prefixed_identifier_test.dart +++ b/pkg/analyzer/test/src/dart/resolution/prefixed_identifier_test.dart @@ -850,6 +850,64 @@ PrefixedIdentifier '''); } + test_ofExtensionType_read_nullableType() async { + await assertErrorsInCode(r''' +extension type A(int it) { + int get foo => 0; +} + +void f(A? a) { + a.foo; +} +''', [ + error(CompileTimeErrorCode.UNCHECKED_PROPERTY_ACCESS_OF_NULLABLE_VALUE, + 69, 3), + ]); + + final node = findNode.singlePrefixedIdentifier; + assertResolvedNodeText(node, r''' +PrefixedIdentifier + prefix: SimpleIdentifier + token: a + staticElement: self::@function::f::@parameter::a + staticType: A? + period: . + identifier: SimpleIdentifier + token: foo + staticElement: self::@extensionType::A::@getter::foo + staticType: int + staticElement: self::@extensionType::A::@getter::foo + staticType: int +'''); + } + + test_ofExtensionType_read_nullableType_nullAware() async { + await assertNoErrorsInCode(r''' +extension type A(int it) { + int get foo => 0; +} + +void f(A? a) { + a?.foo; +} +'''); + + final node = findNode.singlePropertyAccess; + assertResolvedNodeText(node, r''' +PropertyAccess + target: SimpleIdentifier + token: a + staticElement: self::@function::f::@parameter::a + staticType: A? + operator: ?. + propertyName: SimpleIdentifier + token: foo + staticElement: self::@extensionType::A::@getter::foo + staticType: int + staticType: int? +'''); + } + test_ofExtensionType_write() async { await assertNoErrorsInCode(r''' extension type A(int it) {