[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];