From 6dfd5766de8031dfba2ca6fef4397d956eb5c660 Mon Sep 17 00:00:00 2001 From: pq Date: Thu, 18 Jul 2019 03:50:27 +0000 Subject: [PATCH] error code for ambiguous extension method accesses Change-Id: I97b0bc8ba32d1cb0ee3b361525f4c161845f1d01 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/109463 Commit-Queue: Phil Quitslund Reviewed-by: Brian Wilkerson --- pkg/analyzer/lib/error/error.dart | 1 + .../resolver/method_invocation_resolver.dart | 19 ++++++++++---- pkg/analyzer/lib/src/error/codes.dart | 18 +++++++++++++ .../resolution/extension_method_test.dart | 25 ++++++++++++++++--- 4 files changed, 54 insertions(+), 9 deletions(-) diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart index d954b111b20..e9fdd4d12c6 100644 --- a/pkg/analyzer/lib/error/error.dart +++ b/pkg/analyzer/lib/error/error.dart @@ -66,6 +66,7 @@ const List errorCodeValues = const [ CompileTimeErrorCode.ABSTRACT_SUPER_MEMBER_REFERENCE, CompileTimeErrorCode.ACCESS_PRIVATE_ENUM_FIELD, CompileTimeErrorCode.AMBIGUOUS_EXPORT, + CompileTimeErrorCode.AMBIGUOUS_EXTENSION_METHOD_ACCESS, CompileTimeErrorCode.AMBIGUOUS_SET_OR_MAP_LITERAL_BOTH, CompileTimeErrorCode.AMBIGUOUS_SET_OR_MAP_LITERAL_EITHER, CompileTimeErrorCode.ANNOTATION_WITH_NON_CLASS, diff --git a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart index bc6f6d473ae..bb362e1f99e 100644 --- a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart +++ b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart @@ -158,13 +158,12 @@ class MethodInvocationResolver { var t1 = e1.extendedType; var t2 = e2.extendedType; - // todo(pq): broaden platform beyond dart:core - if (t2.element.library.isDartCore) { + if (t2.element.library.isInSdk) { // 1. T2 is declared in a platform library, and T1 is not - if (!t1.element.library.isDartCore) { + if (!t1.element.library.isInSdk) { return -1; } - } else if (t1.element.library.isDartCore) { + } else if (t1.element.library.isInSdk) { return 1; } @@ -504,7 +503,17 @@ class MethodInvocationResolver { ExtensionElement extension = _chooseMostSpecificExtension(extensions, receiverType); if (extension == null) { - // todo(pq): report an error + _setDynamicResolution(node); + _resolver.errorReporter.reportErrorForNode( + CompileTimeErrorCode.AMBIGUOUS_EXTENSION_METHOD_ACCESS, + nameNode, + [ + name, + extensions[0].name, + extensions[1].name, + ], + ); + return; } else { Element member = extension.getMethod(name) ?? extension.getGetter(name) ?? diff --git a/pkg/analyzer/lib/src/error/codes.dart b/pkg/analyzer/lib/src/error/codes.dart index ca9fc48b87a..ac26285c78e 100644 --- a/pkg/analyzer/lib/src/error/codes.dart +++ b/pkg/analyzer/lib/src/error/codes.dart @@ -142,6 +142,24 @@ class CompileTimeErrorCode extends ErrorCode { correction: "Try removing the export of one of the libraries, or " "explicitly hiding the name in one of the export directives."); + /** + * It is a compile time error if there are two applicable extensions defining + * the same member and neither is more specific than the other. + * + * Parameters: + * 0: the name of the member + * 1: the name of the first declaring extension + * 2: the name of the second declaring extension + */ + static const CompileTimeErrorCode AMBIGUOUS_EXTENSION_METHOD_ACCESS = + const CompileTimeErrorCode( + 'AMBIGUOUS_EXTENSION_METHOD_ACCESS', + "A member named '{0}' is defined in extensions '{1}' and '{2}' and " + "neither is more specific.", + correction: + "Try using an extension override to specify the extension " + "you want to to be chosen."); + /** * No parameters. */ diff --git a/pkg/analyzer/test/src/dart/resolution/extension_method_test.dart b/pkg/analyzer/test/src/dart/resolution/extension_method_test.dart index 24e37c036a0..4fc02f837de 100644 --- a/pkg/analyzer/test/src/dart/resolution/extension_method_test.dart +++ b/pkg/analyzer/test/src/dart/resolution/extension_method_test.dart @@ -60,10 +60,6 @@ f() { expect(invocation.methodName.staticElement.library.isDartCore, isFalse); } - test_multi_match_ambiguous() async { - // todo(pq): implement - } - test_multipleExtensions() async { await assertNoErrorsInCode(''' class A {} @@ -87,6 +83,27 @@ f() { ''', [StaticTypeWarningCode.UNDEFINED_METHOD]); } + test_noMostSpecificExtension() async { + await assertErrorsInCode(''' +class A { } + +extension A1_Ext on A { + void a() { } +} + +extension A2_Ext on A { + void a() { } +} + +f() { + A a = A(); + a.a(); +} +''', [ + error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_METHOD_ACCESS, 120, 1), + ]); + } + test_one_match() async { await assertNoErrorsInCode(''' class B { }