From 10c1d883df50af05e5cd6e78c36c96f8a22eaa72 Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Mon, 7 Oct 2024 18:01:54 +0000 Subject: [PATCH] analyzer: Write out qualified extension names in error messages Fixes https://github.com/dart-lang/sdk/issues/56269 Change-Id: I025966fd4aa3d7c5b71175321f21f95e8c41f086 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/388580 Reviewed-by: Konstantin Shcheglov Commit-Queue: Samuel Rawlins Reviewed-by: Brian Wilkerson --- .../services/correction/error_fix_status.yaml | 4 +- .../src/services/correction/fix_internal.dart | 5 +- .../fix/add_extension_override_test.dart | 2 +- pkg/analyzer/lib/error/listener.dart | 243 ++++++++++-------- .../resolver/extension_member_resolver.dart | 42 +-- pkg/analyzer/lib/src/error/codes.g.dart | 21 +- .../lib/src/error/error_code_values.g.dart | 3 +- pkg/analyzer/messages.yaml | 17 +- ...mbiguous_extension_member_access_test.dart | 80 ++++-- pkg/analyzer/tool/diagnostics/diagnostics.md | 3 + 10 files changed, 269 insertions(+), 151 deletions(-) diff --git a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml index 27f02b5b6e7..1fcdf4170d1 100644 --- a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml +++ b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml @@ -151,7 +151,9 @@ CompileTimeErrorCode.AMBIGUOUS_EXPORT: status: needsFix notes: |- For each exported name, add a fix to hide the name. -CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS: +CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_THREE_OR_MORE: + status: hasFix +CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO: status: hasFix CompileTimeErrorCode.AMBIGUOUS_IMPORT: status: needsFix diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart index 30d44f6326f..b5fb4a48914 100644 --- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart +++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart @@ -747,7 +747,10 @@ final _builtInLintProducers = >{ }; final _builtInNonLintMultiProducers = { - CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS: [ + CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO: [ + AddExtensionOverride.new, + ], + CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_THREE_OR_MORE: [ AddExtensionOverride.new, ], CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE: [ diff --git a/pkg/analysis_server/test/src/services/correction/fix/add_extension_override_test.dart b/pkg/analysis_server/test/src/services/correction/fix/add_extension_override_test.dart index 6e3f949f999..dbb2b44f17e 100644 --- a/pkg/analysis_server/test/src/services/correction/fix/add_extension_override_test.dart +++ b/pkg/analysis_server/test/src/services/correction/fix/add_extension_override_test.dart @@ -44,7 +44,7 @@ f() { } ''', expectedNumberOfFixesForKind: 1, errorFilter: (error) { return error.errorCode == - CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS; + CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO; }); } diff --git a/pkg/analyzer/lib/error/listener.dart b/pkg/analyzer/lib/error/listener.dart index 08963223861..7a50e6ef8c5 100644 --- a/pkg/analyzer/lib/error/listener.dart +++ b/pkg/analyzer/lib/error/listener.dart @@ -11,8 +11,10 @@ import 'package:analyzer/dart/element/type.dart'; import 'package:analyzer/diagnostic/diagnostic.dart'; import 'package:analyzer/error/error.dart'; import 'package:analyzer/source/source.dart'; +import 'package:analyzer/src/dart/element/extensions.dart'; import 'package:analyzer/src/dart/element/type.dart'; import 'package:analyzer/src/diagnostic/diagnostic.dart'; +import 'package:analyzer/src/utilities/extensions/collection.dart'; import 'package:meta/meta.dart'; import 'package:source_span/source_span.dart'; @@ -168,7 +170,19 @@ class ErrorReporter { return; } - _convertElements(arguments); + if (arguments != null) { + var invalid = arguments + .whereNotType() + .whereNotType() + .whereNotType() + .whereNotType() + .whereNotType(); + if (invalid.isNotEmpty) { + throw ArgumentError('Tried to format an error using ' + '${invalid.map((e) => e.runtimeType).join(', ')}'); + } + } + contextMessages ??= []; contextMessages.addAll(_convertTypeNames(arguments)); _errorListener.onError( @@ -334,93 +348,77 @@ class ErrorReporter { ); } - /// Convert all [Element]s in the [arguments] into their display strings. - void _convertElements(List? arguments) { - if (arguments == null) { - return; - } - - for (var i = 0; i < arguments.length; i++) { - var argument = arguments[i]; - if (argument is Element) { - arguments[i] = argument.getDisplayString(); - } else if (!(argument is String || - argument is DartType || - argument is int || - argument is Uri)) { - throw ArgumentError( - 'Tried to format an error using ${argument.runtimeType}'); - } - } - } - - /// Given an array of [arguments] that is expected to contain two or more - /// types, convert the types into strings by using the display names of the - /// types, unless there are two or more types with the same names, in which - /// case the extended display names of the types will be used in order to + /// Given an array of [arguments] that may contain [DartType]s and [Element]s, + /// converts the types and elements into strings by using the display names of + /// each, unless there are two or more types or elements with the same display + /// names, in which case the extended display names will be used in order to /// clarify the message. List _convertTypeNames(List? arguments) { - var messages = []; if (arguments == null) { - return messages; + return const []; } - Map> typeGroups = {}; - for (int i = 0; i < arguments.length; i++) { + var typeGroups = >{}; + for (var i = 0; i < arguments.length; i++) { var argument = arguments[i]; if (argument is TypeImpl) { - String displayName = argument.getDisplayString( - preferTypeAlias: true, - ); - List<_TypeToConvert> types = - typeGroups.putIfAbsent(displayName, () => <_TypeToConvert>[]); + var displayName = argument.getDisplayString(preferTypeAlias: true); + var types = typeGroups.putIfAbsent(displayName, () => []); types.add(_TypeToConvert(i, argument, displayName)); + } else if (argument is Element) { + var displayName = argument.getDisplayString(); + var types = typeGroups.putIfAbsent(displayName, () => []); + types.add(_ElementToConvert(i, argument, displayName)); } } - for (List<_TypeToConvert> typeGroup in typeGroups.values) { - if (typeGroup.length == 1) { - _TypeToConvert typeToConvert = typeGroup[0]; - arguments[typeToConvert.index] = typeToConvert.displayName; - } else { - Map> nameToElementMap = {}; - for (_TypeToConvert typeToConvert in typeGroup) { - for (Element element in typeToConvert.allElements()) { - Set elements = - nameToElementMap.putIfAbsent(element.name!, () => {}); - elements.add(element); - } - } - for (_TypeToConvert typeToConvert in typeGroup) { - // TODO(brianwilkerson): When clients do a better job of displaying - // context messages, remove the extra text added to the buffer. - StringBuffer? buffer; - for (Element element in typeToConvert.allElements()) { - String name = element.name!; - if (nameToElementMap[name]!.length > 1) { - if (buffer == null) { - buffer = StringBuffer(); - buffer.write('where '); - } else { - buffer.write(', '); - } - buffer.write('$name is defined in ${element.source!.fullName}'); - } - messages.add(DiagnosticMessageImpl( - filePath: element.source!.fullName, - length: element.nameLength, - message: '$name is defined in ${element.source!.fullName}', - offset: element.nameOffset, - url: null)); - } - if (buffer != null) { - arguments[typeToConvert.index] = - '${typeToConvert.displayName} ($buffer)'; - } else { - arguments[typeToConvert.index] = typeToConvert.displayName; - } + var messages = []; + for (var typeGroup in typeGroups.values) { + if (typeGroup.length == 1) { + var typeToConvert = typeGroup[0]; + // If the display name of a type is unambiguous, just replace the type + // in the arguments list with its display name. + arguments[typeToConvert.index] = typeToConvert.displayName; + continue; + } + + var nameToElementMap = >{}; + for (var typeToConvert in typeGroup) { + for (var element in typeToConvert.allElements) { + var elements = nameToElementMap.putIfAbsent(element.name!, () => {}); + elements.add(element); } } + + for (var typeToConvert in typeGroup) { + // TODO(brianwilkerson): When clients do a better job of displaying + // context messages, remove the extra text added to the buffer. + StringBuffer? buffer; + for (var element in typeToConvert.allElements) { + var name = element.name!; + var sourcePath = element.source!.fullName; + if (nameToElementMap[name]!.length > 1) { + if (buffer == null) { + buffer = StringBuffer(); + buffer.write('where '); + } else { + buffer.write(', '); + } + buffer.write('$name is defined in $sourcePath'); + } + messages.add(DiagnosticMessageImpl( + filePath: element.source!.fullName, + length: element.nameLength, + message: '$name is defined in $sourcePath', + offset: element.nameOffset, + url: null, + )); + } + + arguments[typeToConvert.index] = buffer != null + ? '${typeToConvert.displayName} ($buffer)' + : typeToConvert.displayName; + } } return messages; } @@ -453,6 +451,22 @@ class RecordingErrorListener implements AnalysisErrorListener { } } +/// Used by [ErrorReporter._convertTypeNames] to keep track of an error argument +/// that is an [Element], that is being converted to a display string. +class _ElementToConvert implements _ToConvert { + @override + final int index; + + @override + final String displayName; + + @override + final Iterable allElements; + + _ElementToConvert(this.index, Element element, this.displayName) + : allElements = [element]; +} + /// An [AnalysisErrorListener] that ignores error. class _NullErrorListener implements AnalysisErrorListener { @override @@ -461,42 +475,61 @@ class _NullErrorListener implements AnalysisErrorListener { } } -/// Used by `ErrorReporter._convertTypeNames` to keep track of a type that is -/// being converted. -class _TypeToConvert { +/// Used by [ErrorReporter._convertTypeNames] to keep track of an argument that +/// is being converted to a display string. +abstract class _ToConvert { + /// A list of all elements involved in the [DartType] or [Element]'s display + /// string. + Iterable get allElements; + + /// The argument's display string, to replace the argument in the argument + /// list. + String get displayName; + + /// The index of the argument in the argument list. + int get index; +} + +/// Used by [ErrorReporter._convertTypeNames] to keep track of an error argument +/// that is a [DartType], that is being converted to a display string. +class _TypeToConvert implements _ToConvert { + @override final int index; - final DartType type; + + final DartType _type; + + @override final String displayName; - List? _allElements; + @override + late final Iterable allElements = () { + var elements = {}; - _TypeToConvert(this.index, this.type, this.displayName); - - List allElements() { - if (_allElements == null) { - Set elements = {}; - - void addElementsFrom(DartType type) { - if (type is FunctionType) { - addElementsFrom(type.returnType); - for (ParameterElement parameter in type.parameters) { - addElementsFrom(parameter.type); - } - } else if (type is InterfaceType) { - if (elements.add(type.element)) { - for (DartType typeArgument in type.typeArguments) { - addElementsFrom(typeArgument); - } + void addElementsFrom(DartType type) { + if (type is FunctionType) { + addElementsFrom(type.returnType); + for (var parameter in type.parameters) { + addElementsFrom(parameter.type); + } + } else if (type is RecordType) { + for (var parameter in type.fields) { + addElementsFrom(parameter.type); + } + } else if (type is InterfaceType) { + if (elements.add(type.element)) { + for (var typeArgument in type.typeArguments) { + addElementsFrom(typeArgument); } } } - - addElementsFrom(type); - _allElements = elements.where((element) { - var name = element.name; - return name != null && name.isNotEmpty; - }).toList(); } - return _allElements!; - } + + addElementsFrom(_type); + return elements.where((element) { + var name = element.name; + return name != null && name.isNotEmpty; + }); + }(); + + _TypeToConvert(this.index, this._type, this.displayName); } diff --git a/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart index 1f51888cabc..789b233b73d 100644 --- a/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart +++ b/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart @@ -117,21 +117,33 @@ class ExtensionMemberResolver { } // The most specific extension is ambiguous. - _errorReporter.atEntity( - nameEntity, - CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, - arguments: [ - name.name, - mostSpecific.map((e) { - var name = e.extension.name; - if (name != null) { - return "extension '$name'"; - } - var type = e.extension.extendedType.getDisplayString(); - return "unnamed extension on '$type'"; - }).commaSeparatedWithAnd, - ], - ); + if (mostSpecific.length == 2) { + _errorReporter.atEntity( + nameEntity, + CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, + arguments: [ + name.name, + mostSpecific[0].extension, + mostSpecific[1].extension, + ], + ); + } else { + _errorReporter.atEntity( + nameEntity, + CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_THREE_OR_MORE, + arguments: [ + name.name, + mostSpecific.map((e) { + var name = e.extension.name; + if (name != null) { + return "extension '$name'"; + } + var type = e.extension.extendedType.getDisplayString(); + return "unnamed extension on '$type'"; + }).commaSeparatedWithAnd, + ], + ); + } return ResolutionResult.ambiguous; } diff --git a/pkg/analyzer/lib/src/error/codes.g.dart b/pkg/analyzer/lib/src/error/codes.g.dart index 343e8e57afa..ec125826081 100644 --- a/pkg/analyzer/lib/src/error/codes.g.dart +++ b/pkg/analyzer/lib/src/error/codes.g.dart @@ -70,14 +70,31 @@ class CompileTimeErrorCode extends AnalyzerErrorCode { /// Parameters: /// 0: the name of the member /// 1: the names of the declaring extensions - static const CompileTimeErrorCode AMBIGUOUS_EXTENSION_MEMBER_ACCESS = - CompileTimeErrorCode( + static const CompileTimeErrorCode + AMBIGUOUS_EXTENSION_MEMBER_ACCESS_THREE_OR_MORE = CompileTimeErrorCode( 'AMBIGUOUS_EXTENSION_MEMBER_ACCESS', "A member named '{0}' is defined in {1}, and none are more specific.", correctionMessage: "Try using an extension override to specify the extension you want to " "be chosen.", hasPublishedDocs: true, + uniqueName: 'AMBIGUOUS_EXTENSION_MEMBER_ACCESS_THREE_OR_MORE', + ); + + /// Parameters: + /// 0: the name of the member + /// 1: the name of the first declaring extension + /// 2: the names of the second declaring extension + static const CompileTimeErrorCode AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO = + CompileTimeErrorCode( + 'AMBIGUOUS_EXTENSION_MEMBER_ACCESS', + "A member named '{0}' is defined in '{1}' and '{2}', and neither is more " + "specific.", + correctionMessage: + "Try using an extension override to specify the extension you want to " + "be chosen.", + hasPublishedDocs: true, + uniqueName: 'AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO', ); /// Parameters: diff --git a/pkg/analyzer/lib/src/error/error_code_values.g.dart b/pkg/analyzer/lib/src/error/error_code_values.g.dart index c207246da76..407af9ca186 100644 --- a/pkg/analyzer/lib/src/error/error_code_values.g.dart +++ b/pkg/analyzer/lib/src/error/error_code_values.g.dart @@ -49,7 +49,8 @@ const List errorCodeValues = [ CompileTimeErrorCode.ABSTRACT_FIELD_INITIALIZER, CompileTimeErrorCode.ABSTRACT_SUPER_MEMBER_REFERENCE, CompileTimeErrorCode.AMBIGUOUS_EXPORT, - CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, + CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_THREE_OR_MORE, + CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, CompileTimeErrorCode.AMBIGUOUS_IMPORT, CompileTimeErrorCode.AMBIGUOUS_SET_OR_MAP_LITERAL_BOTH, CompileTimeErrorCode.AMBIGUOUS_SET_OR_MAP_LITERAL_EITHER, diff --git a/pkg/analyzer/messages.yaml b/pkg/analyzer/messages.yaml index 15aeb69d899..1ead2a7db36 100644 --- a/pkg/analyzer/messages.yaml +++ b/pkg/analyzer/messages.yaml @@ -408,14 +408,16 @@ CompileTimeErrorCode: export 'a.dart'; export 'b.dart' hide C; ``` - AMBIGUOUS_EXTENSION_MEMBER_ACCESS: - problemMessage: "A member named '{0}' is defined in {1}, and none are more specific." + AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO: + sharedName: AMBIGUOUS_EXTENSION_MEMBER_ACCESS + problemMessage: "A member named '{0}' is defined in '{1}' and '{2}', and neither is more specific." correctionMessage: Try using an extension override to specify the extension you want to be chosen. hasPublishedDocs: true comment: |- Parameters: 0: the name of the member - 1: the names of the declaring extensions + 1: the name of the first declaring extension + 2: the names of the second declaring extension documentation: |- #### Description @@ -471,6 +473,15 @@ CompileTimeErrorCode: print(E2(s).charCount); } ``` + AMBIGUOUS_EXTENSION_MEMBER_ACCESS_THREE_OR_MORE: + sharedName: AMBIGUOUS_EXTENSION_MEMBER_ACCESS + problemMessage: "A member named '{0}' is defined in {1}, and none are more specific." + correctionMessage: Try using an extension override to specify the extension you want to be chosen. + hasPublishedDocs: true + comment: |- + Parameters: + 0: the name of the member + 1: the names of the declaring extensions AMBIGUOUS_IMPORT: problemMessage: "The name '{0}' is defined in the libraries {1}." correctionMessage: "Try using 'as prefix' for one of the import directives, or hiding the name from all but one of the imports." diff --git a/pkg/analyzer/test/src/diagnostics/ambiguous_extension_member_access_test.dart b/pkg/analyzer/test/src/diagnostics/ambiguous_extension_member_access_test.dart index c96edd7e7a4..79953772b79 100644 --- a/pkg/analyzer/test/src/diagnostics/ambiguous_extension_member_access_test.dart +++ b/pkg/analyzer/test/src/diagnostics/ambiguous_extension_member_access_test.dart @@ -29,7 +29,7 @@ extension E2 on A { int f(A a) => a(); ''', [ - error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 110, 1), + error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 110, 1), ]); } @@ -47,7 +47,7 @@ f() { 0.a; } ''', [ - error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 98, 1), + error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 98, 1), ]); var node = findNode.propertyAccess('0.a'); @@ -111,7 +111,7 @@ f() { 0.a; } ''', [ - error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 91, 1), + error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 91, 1), ]); var node = findNode.propertyAccess('0.a'); @@ -144,7 +144,7 @@ f() { 0.a; } ''', [ - error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 96, 1), + error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 96, 1), ]); var node = findNode.propertyAccess('0.a'); @@ -172,8 +172,38 @@ void f() { 0.foo(); } ''', [ - error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 129, 3, - messageContains: ["in extension 'E1' and extension 'E2',"]), + error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 129, 3, + messageContains: [ + "in 'extension E1 on int' and 'extension E2 on int',", + ]), + ]); + } + + test_method_conflict_conflict_notSpecific_sameName() async { + var one = newFile('$testPackageLibPath/one.dart', ''' +extension E on int { void foo() {} } +'''); + var two = newFile('$testPackageLibPath/two.dart', ''' +extension E on int { void foo() {} } +'''); + await assertErrorsInCode(''' +// ignore_for_file: unused_import +import 'one.dart'; +import 'two.dart'; +void f() { + 0.foo(); +} +''', [ + error( + CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, + 87, + 3, + messageContains: [ + "'extension E on int (where E is defined in ${one.path})' and " + "'extension E on int (where E is defined in ${two.path})',", + ], + contextMessages: [message(one, 10, 1), message(two, 10, 1)], + ), ]); } @@ -197,8 +227,10 @@ void f() { 0.foo(); } ''', [ - error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 129, 3, - messageContains: ["in extension 'E1' and extension 'E2',"]), + error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 129, 3, + messageContains: [ + "in 'extension E1 on int' and 'extension E2 on int',", + ]), ]); } @@ -227,7 +259,7 @@ f() { 0.a(); } ''', [ - error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 88, 1), + error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 88, 1), ]); var node = findNode.methodInvocation('0.a()'); @@ -259,8 +291,10 @@ void f() { 0.foo(); } ''', [ - error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 129, 3, - messageContains: ["in extension 'E1' and extension 'E2',"]), + error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 129, 3, + messageContains: [ + "in 'extension E1 on int' and 'extension E2 on int'," + ]), ]); } @@ -274,7 +308,10 @@ void f() { 0.foo(); } ''', [ - error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 167, 3, + error( + CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_THREE_OR_MORE, + 167, + 3, messageContains: [ "in extension 'E1', extension 'E2', and extension 'E3'," ]), @@ -313,7 +350,7 @@ f(SubTarget t) { t.foo; } ''', [ - error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 396, 3), + error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 396, 3), ]); } @@ -331,7 +368,7 @@ extension E2 on A { A f(A a) => a + a; ''', [ - error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 122, 5), + error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 122, 5), ]); } @@ -351,7 +388,7 @@ void f(A a) { a += 0; } ''', [ - error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 130, 2), + error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 130, 2), ]); } @@ -369,7 +406,7 @@ extension E2 on A { int f(A a) => a[0]; ''', [ - error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 134, 1), + error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 134, 1), ]); } @@ -387,7 +424,7 @@ f() { 0[1] += 2; } ''', [ - error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 136, 1), + error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 136, 1), ]); } @@ -405,7 +442,7 @@ extension E2 on A { int f(A a) => -a; ''', [ - error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 123, 1), + error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 123, 1), ]); } @@ -423,7 +460,7 @@ f() { 0.a = 3; } ''', [ - error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 88, 1), + error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 88, 1), ]); assertResolvedNodeText(findNode.assignment('= 3'), r''' @@ -476,10 +513,9 @@ int f(List x) => x(); int g(List x) => x(); int h(List x) => x(); ''', [ - error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 167, 1, + error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 167, 1, messageContains: [ - "in unnamed extension on 'List' and unnamed extension on " - "'List'," + "'extension on List' and 'extension on List'," ]), ]); } diff --git a/pkg/analyzer/tool/diagnostics/diagnostics.md b/pkg/analyzer/tool/diagnostics/diagnostics.md index e5611dc8f84..90629168801 100644 --- a/pkg/analyzer/tool/diagnostics/diagnostics.md +++ b/pkg/analyzer/tool/diagnostics/diagnostics.md @@ -430,6 +430,9 @@ export 'b.dart' hide C; ### ambiguous_extension_member_access +_A member named '{0}' is defined in '{1}' and '{2}', and neither is more +specific._ + _A member named '{0}' is defined in {1}, and none are more specific._ #### Description