`non_constant_identifier_names` support for augmentations

See: https://github.com/dart-lang/linter/issues/4883

Change-Id: Ia83e8a6e8e726bc3ef83ccbc5c0106f9698e4d4c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/352978
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Phil Quitslund <pquitslund@google.com>
diff --git a/pkg/linter/lib/src/extensions.dart b/pkg/linter/lib/src/extensions.dart
index 6143aac..b60b4b3 100644
--- a/pkg/linter/lib/src/extensions.dart
+++ b/pkg/linter/lib/src/extensions.dart
@@ -2,12 +2,12 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/ast/token.dart';
 import 'package:analyzer/dart/constant/value.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/type.dart';
 import 'package:analyzer/file_system/physical_file_system.dart';
+import 'package:analyzer/src/dart/ast/ast.dart'; // ignore: implementation_imports
 import 'package:analyzer/src/dart/element/member.dart'; // ignore: implementation_imports
 import 'package:collection/collection.dart';
 
@@ -25,6 +25,19 @@
 extension AstNodeExtension on AstNode {
   Iterable<AstNode> get childNodes => childEntities.whereType<AstNode>();
 
+  bool get isAugmentation {
+    var self = this;
+    return switch (self) {
+      ConstructorDeclaration() => self.augmentKeyword != null,
+      FunctionDeclarationImpl() => self.augmentKeyword != null,
+      FunctionExpression() => self.parent?.isAugmentation ?? false,
+      MethodDeclaration() => self.augmentKeyword != null,
+      // TODO(pq): unimplemented
+      // VariableDeclaration() => self.augmentKeyword != null,
+      _ => false
+    };
+  }
+
   bool get isEffectivelyPrivate {
     var node = this;
     if (node.isInternal) return true;
diff --git a/pkg/linter/lib/src/rules/non_constant_identifier_names.dart b/pkg/linter/lib/src/rules/non_constant_identifier_names.dart
index d49173d..6ea45e4 100644
--- a/pkg/linter/lib/src/rules/non_constant_identifier_names.dart
+++ b/pkg/linter/lib/src/rules/non_constant_identifier_names.dart
@@ -95,6 +95,7 @@
 
   @override
   void visitConstructorDeclaration(ConstructorDeclaration node) {
+    if (node.isAugmentation) return;
     // For rationale on accepting underscores, see:
     // https://github.com/dart-lang/linter/issues/1854
     checkIdentifier(node.name, underscoresOk: true);
@@ -118,7 +119,9 @@
 
   @override
   void visitFormalParameterList(FormalParameterList node) {
+    var inAugmentation = node.parent?.isAugmentation ?? false;
     for (var p in node.parameters) {
+      if (inAugmentation && p.isNamed) continue;
       if (p is! FieldFormalParameter) {
         checkIdentifier(p.name, underscoresOk: true);
       }
@@ -127,12 +130,14 @@
 
   @override
   void visitFunctionDeclaration(FunctionDeclaration node) {
+    if (node.isAugmentation) return;
+
     checkIdentifier(node.name);
   }
 
   @override
   void visitMethodDeclaration(MethodDeclaration node) {
-    if (!node.isOperator) {
+    if (!node.isOperator && !node.isAugmentation) {
       checkIdentifier(node.name);
     }
   }
diff --git a/pkg/linter/test/rules/non_constant_identifier_names_test.dart b/pkg/linter/test/rules/non_constant_identifier_names_test.dart
index 4f84d99..6600521 100644
--- a/pkg/linter/test/rules/non_constant_identifier_names_test.dart
+++ b/pkg/linter/test/rules/non_constant_identifier_names_test.dart
@@ -222,6 +222,180 @@
   @override
   String get lintRule => 'non_constant_identifier_names';
 
+  test_augmentedConstructor() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+import augment 'test.dart';
+
+class A {
+  A.Aa();
+}
+''');
+
+    await assertNoDiagnostics(r'''
+library augment 'a.dart';
+
+augment class A {
+  augment A.Aa();
+}
+''');
+  }
+
+  @FailingTest(reason: 'Null check operator used on a null value')
+  test_augmentedField() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+import augment 'test.dart';
+
+class A {
+  int Xx = 1;
+}
+''');
+
+    await assertNoDiagnostics(r'''
+library augment 'a.dart';
+
+augment class A {
+  augment int Xx = 2;
+}
+''');
+  }
+
+  test_augmentedFunction() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+import augment 'test.dart';
+
+void Ff() { }
+''');
+
+    await assertNoDiagnostics(r'''
+library augment 'a.dart';
+
+augment void Ff() { }
+''');
+  }
+
+  test_augmentedFunction_namedParam() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+import augment 'test.dart';
+
+void f({String? Ss}) { }
+''');
+
+    await assertNoDiagnostics(r'''
+library augment 'a.dart';
+
+augment void f({String? Ss}) { }
+''');
+  }
+
+  test_augmentedFunction_positionalParam() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+import augment 'test.dart';
+
+void f(String? Ss, [int? Xx]) { }
+''');
+
+    await assertDiagnostics(r'''
+library augment 'a.dart';
+
+augment void f(String? Ss, [int? Xx]) { }
+''', [
+      lint(50, 2),
+      lint(60, 2),
+    ]);
+  }
+
+  test_augmentedGetter() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+import augment 'test.dart';
+
+class A {
+  int get Gg => 1;
+}
+''');
+
+    await assertNoDiagnostics(r'''
+library augment 'a.dart';
+
+augment class A {
+  augment int get Gg => 2;
+}
+''');
+  }
+
+  test_augmentedMethod() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+import augment 'test.dart';
+
+class A {
+  void Mm() { }
+}
+''');
+
+    await assertNoDiagnostics(r'''
+library augment 'a.dart';
+
+augment class A {
+  augment void Mm() { }
+}
+''');
+  }
+
+  test_augmentedMethod_namedParam() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+import augment 'test.dart';
+
+class A {
+  void m({String? Ss}) { }
+}
+''');
+
+    await assertNoDiagnostics(r'''
+library augment 'a.dart';
+
+augment class A {
+  augment void m({String? Ss}) { }
+}
+''');
+  }
+
+  test_augmentedMethod_positionalParam() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+import augment 'test.dart';
+
+class A {
+  void m(String? Ss, [int? Xx]) { }
+}
+''');
+
+    await assertDiagnostics(r'''
+library augment 'a.dart';
+
+augment class A {
+  augment void m(String? Ss, [int? Xx]) { }
+}
+''', [
+      lint(70, 2),
+      lint(80, 2),
+    ]);
+  }
+
+  @FailingTest(
+      reason:
+          "CompileTimeErrorCode.DUPLICATE_DEFINITION [39, 2, The name 'Xx' is already defined.]")
+  test_augmentedTopLevelVariable() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+import augment 'test.dart';
+
+int Xx = 1;
+''');
+
+    await assertNoDiagnostics(r'''
+library augment 'a.dart';
+
+augment int Xx = 2;
+''');
+  }
+
   ///https://github.com/dart-lang/linter/issues/193
   test_ignoreSyntheticNodes() async {
     await assertDiagnostics(r'''