| Index: pkg/analyzer/lib/src/generated/resolver.dart
|
| diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
|
| index 1ab010209a97c8d33cb496fc946440673cfa1437..4f99160925a80b62af7953f4b5852250e7efcd5d 100644
|
| --- a/pkg/analyzer/lib/src/generated/resolver.dart
|
| +++ b/pkg/analyzer/lib/src/generated/resolver.dart
|
| @@ -64,6 +64,11 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
|
| final ErrorReporter _errorReporter;
|
|
|
| /**
|
| + * The type [Null].
|
| + */
|
| + final InterfaceType _nullType;
|
| +
|
| + /**
|
| * The type Future<Null>, which is needed for determining whether it is safe
|
| * to have a bare "return;" in an async method.
|
| */
|
| @@ -87,7 +92,8 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
|
| BestPracticesVerifier(
|
| this._errorReporter, TypeProvider typeProvider, this._currentLibrary,
|
| {TypeSystem typeSystem})
|
| - : _futureNullType = typeProvider.futureNullType,
|
| + : _nullType = typeProvider.nullType,
|
| + _futureNullType = typeProvider.futureNullType,
|
| _typeSystem = typeSystem ?? new TypeSystemImpl() {
|
| inDeprecatedMember = _currentLibrary.isDeprecated;
|
| }
|
| @@ -275,7 +281,8 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
|
|
|
| @override
|
| Object visitMethodInvocation(MethodInvocation node) {
|
| - _checkForCanBeNullAfterNullAware(node.realTarget, node.operator);
|
| + _checkForCanBeNullAfterNullAware(
|
| + node.realTarget, node.operator, null, node.methodName);
|
| DartType staticInvokeType = node.staticInvokeType;
|
| if (staticInvokeType is InterfaceType) {
|
| MethodElement methodElement = staticInvokeType.lookUpMethod(
|
| @@ -299,7 +306,8 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
|
|
|
| @override
|
| Object visitPropertyAccess(PropertyAccess node) {
|
| - _checkForCanBeNullAfterNullAware(node.realTarget, node.operator);
|
| + _checkForCanBeNullAfterNullAware(
|
| + node.realTarget, node.operator, node.propertyName, null);
|
| return super.visitPropertyAccess(node);
|
| }
|
|
|
| @@ -521,20 +529,37 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
|
| }
|
|
|
| /**
|
| - * Produce a hint if the given [target] could have a value of `null`.
|
| + * Produce a hint if the given [target] could have a value of `null`, and
|
| + * [identifier] is not a name of a getter or a method that exists in the
|
| + * class [Null].
|
| */
|
| - void _checkForCanBeNullAfterNullAware(Expression target, Token operator) {
|
| + void _checkForCanBeNullAfterNullAware(
|
| + Expression target, Token operator, SimpleIdentifier propertyName, SimpleIdentifier methodName) {
|
| if (operator?.type == TokenType.QUESTION_PERIOD) {
|
| return;
|
| }
|
| + bool isNullTypeMember() {
|
| + if (propertyName != null) {
|
| + String name = propertyName.name;
|
| + return _nullType.lookUpGetter(name, _currentLibrary) != null;
|
| + }
|
| + if (methodName != null) {
|
| + String name = methodName.name;
|
| + return _nullType.lookUpMethod(name, _currentLibrary) != null;
|
| + }
|
| + return false;
|
| + }
|
| +
|
| target = target?.unParenthesized;
|
| if (target is MethodInvocation) {
|
| - if (target.operator?.type == TokenType.QUESTION_PERIOD) {
|
| + if (target.operator?.type == TokenType.QUESTION_PERIOD &&
|
| + !isNullTypeMember()) {
|
| _errorReporter.reportErrorForNode(
|
| HintCode.CAN_BE_NULL_AFTER_NULL_AWARE, target);
|
| }
|
| } else if (target is PropertyAccess) {
|
| - if (target.operator.type == TokenType.QUESTION_PERIOD) {
|
| + if (target.operator.type == TokenType.QUESTION_PERIOD &&
|
| + !isNullTypeMember()) {
|
| _errorReporter.reportErrorForNode(
|
| HintCode.CAN_BE_NULL_AFTER_NULL_AWARE, target);
|
| }
|
|
|