[analyzer] Ensure Element.Identifier is unique for augmentations Augmentations can have duplicated names within a library so this appends `nameOffset` to them (in the same way that local functions and closures do) to keep them unique. Fixes https://github.com/dart-lang/sdk/issues/55408 Change-Id: I0b482e5a945dfb08c5a3cc0ab2254b386f24dd18 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/362120 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analysis_server/test/lsp/code_lens/augmentations_test.dart b/pkg/analysis_server/test/lsp/code_lens/augmentations_test.dart index ace1922..ac7feb4 100644 --- a/pkg/analysis_server/test/lsp/code_lens/augmentations_test.dart +++ b/pkg/analysis_server/test/lsp/code_lens/augmentations_test.dart
@@ -316,7 +316,6 @@ @override Uri get targetUri => mainFileAugmentationUri; - @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/55408') test_available_class_augmentationOf_augmentationOf_declarationInAugmentationFile() async { setLibraryContent(r''); setAugmentationContent(r''' @@ -393,7 +392,6 @@ @override Uri get targetUri => mainFileUri; - @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/55408') test_available_class_augmentationOf_augmentationOf_declarationInAugmentationFile() async { setLibraryContent(r''); setAugmentationContent(r''' @@ -411,7 +409,6 @@ ); } - @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/55408') test_available_class_augmentationOf_augmentationOf_declarationInLibraryFile() async { setLibraryContent(r''' class A {}
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart index 336ef09..6a789ef 100644 --- a/pkg/analyzer/lib/src/dart/element/element.dart +++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -2321,7 +2321,15 @@ /// Return an identifier that uniquely identifies this element among the /// children of this element's parent. - String get identifier => name!; + String get identifier { + var identifier = name!; + + if (_includeNameOffsetInIdentifier) { + identifier += "@$nameOffset"; + } + + return considerCanonicalizeString(identifier); + } bool get isNonFunctionTypeAliasesEnabled { return library!.featureSet.isEnabled(Feature.nonfunction_type_aliases); @@ -2418,6 +2426,16 @@ return enclosingElement?.source; } + /// Whether to include the [nameOffset] in [identifier] to disambiguiate + /// elements that might otherwise have the same identifier. + bool get _includeNameOffsetInIdentifier { + var element = this; + if (element is AugmentableElement) { + return element.isAugmentation; + } + return false; + } + @override bool operator ==(Object other) { if (identical(this, other)) { @@ -3199,16 +3217,6 @@ ExecutableElement get declaration => this; @override - String get identifier { - String identifier = super.identifier; - Element? enclosing = enclosingElement; - if (enclosing is ExecutableElement || enclosing is VariableElement) { - identifier += "@$nameOffset"; - } - return considerCanonicalizeString(identifier); - } - - @override bool get isDartCoreIdentical { return isStatic && name == 'identical' && library.isDartCore; } @@ -3222,6 +3230,13 @@ ElementKind get kind => ElementKind.FUNCTION; @override + bool get _includeNameOffsetInIdentifier { + return super._includeNameOffsetInIdentifier || + enclosingElement is ExecutableElement || + enclosingElement is VariableElement; + } + + @override T? accept<T>(ElementVisitor<T> visitor) => visitor.visitFunctionElement(this); }
diff --git a/pkg/analyzer/test/src/dart/element/element_test.dart b/pkg/analyzer/test/src/dart/element/element_test.dart index 2e806f1..c67e81f 100644 --- a/pkg/analyzer/test/src/dart/element/element_test.dart +++ b/pkg/analyzer/test/src/dart/element/element_test.dart
@@ -2216,6 +2216,67 @@ @reflectiveTest class UniqueLocationTest extends PubPackageResolutionTest { + test_ambiguous_augmentation_class() async { + await resolveTestCode(''' +class A {} +augment class A {} // 1 +augment class A {} // 2 +'''); + expect( + findNode + .classDeclaration('augment class A {} // 1') + .declaredElement! + .location, + isNot( + findNode + .classDeclaration('augment class A {} // 2') + .declaredElement! + .location, + )); + } + + test_ambiguous_augmentation_classMember() async { + await resolveTestCode(''' +class A { + void f() {} +} +augment class A { + augment void f() {} // 1 + augment void f() {} // 2 +} +'''); + expect( + findNode + .methodDeclaration('augment void f() {} // 1') + .declaredElement! + .location, + isNot( + findNode + .methodDeclaration('augment void f() {} // 2') + .declaredElement! + .location, + )); + } + + test_ambiguous_augmentation_topLevel() async { + await resolveTestCode(''' +void f() {} +augment void f() {} // 1 +augment void f() {} // 2 +'''); + expect( + findNode + .functionDeclaration('augment void f() {} // 1') + .declaredElement! + .location, + isNot( + findNode + .functionDeclaration('augment void f() {} // 2') + .declaredElement! + .location, + )); + } + test_ambiguous_closure_in_executable() async { await resolveTestCode(''' void f() => [() => 0, () => 1];