Add fix for `undefined_enum_constant`.

Bug: #47643
Change-Id: I45b59338be548214a220390bb0947451a84451f8
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/239667
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analysis_server/lib/src/cider/rename.dart b/pkg/analysis_server/lib/src/cider/rename.dart
index a306461..33ba10e 100644
--- a/pkg/analysis_server/lib/src/cider/rename.dart
+++ b/pkg/analysis_server/lib/src/cider/rename.dart
@@ -129,7 +129,7 @@
     if (node == null || element == null) {
       return null;
     }
-    if (element.source != null && element.source!.uri.isScheme('dart')) {
+    if (element.library?.isInSdk == true) {
       return null;
     }
     if (element is MethodElement && element.isOperator) {
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/add_enum_constant.dart b/pkg/analysis_server/lib/src/services/correction/dart/add_enum_constant.dart
new file mode 100644
index 0000000..f2ed14c
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/add_enum_constant.dart
@@ -0,0 +1,81 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// 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:analysis_server/src/services/correction/dart/abstract_producer.dart';
+import 'package:analysis_server/src/services/correction/fix.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
+
+class AddEnumConstant extends CorrectionProducer {
+  /// The name of the constant to be created.
+  String _constantName = '';
+
+  @override
+  // Not predictably the correct action.
+  bool get canBeAppliedInBulk => false;
+
+  @override
+  // Not predictably the correct action.
+  bool get canBeAppliedToFile => false;
+
+  @override
+  List<Object> get fixArguments => [_constantName];
+
+  @override
+  FixKind get fixKind => DartFixKind.ADD_ENUM_CONSTANT;
+
+  @override
+  Future<void> compute(ChangeBuilder builder) async {
+    var node = this.node;
+    if (node is! SimpleIdentifier) return;
+    var parent = node.parent;
+    if (parent is! PrefixedIdentifier) return;
+
+    _constantName = node.name;
+    var target = parent.prefix;
+
+    var targetElement = target.staticElement;
+    if (targetElement == null) return;
+    if (targetElement.library?.isInSdk == true) return;
+
+    var targetDeclarationResult =
+        await sessionHelper.getElementDeclaration(targetElement);
+    if (targetDeclarationResult == null) return;
+    var targetNode = targetDeclarationResult.node;
+    if (targetNode is! EnumDeclaration) return;
+
+    var targetUnit = targetDeclarationResult.resolvedUnit;
+    if (targetUnit == null) return;
+
+    var targetSource = targetElement.source;
+    var targetFile = targetSource?.fullName;
+    if (targetFile == null) return;
+
+    var constructors = targetNode.members
+        .whereType<ConstructorDeclaration>()
+        .where((con) => con.factoryKeyword == null);
+
+    if (constructors.any((con) => con.parameters.parameters.isNotEmpty)) {
+      return;
+    }
+
+    var length = constructors.length;
+    if (length > 1) return;
+
+    var name = length == 1 ? constructors.first.name?.name : null;
+
+    var offset = targetNode.constants.last.end;
+
+    var addition = name != null ? '.$name()' : '';
+
+    await builder.addDartFileEdit(targetFile, (builder) {
+      builder.addInsertion(offset, (builder) {
+        builder.write(', ');
+        builder.write(_constantName);
+        builder.write(addition);
+      });
+    });
+  }
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/create_field.dart b/pkg/analysis_server/lib/src/services/correction/dart/create_field.dart
index 287df7c..7d54c79 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/create_field.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/create_field.dart
@@ -96,7 +96,7 @@
     if (targetClassElement == null) {
       return;
     }
-    if (targetClassElement.librarySource.uri.isScheme('dart')) {
+    if (targetClassElement.library.isInSdk) {
       return;
     }
     utils.targetClassElement = targetClassElement;
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/create_getter.dart b/pkg/analysis_server/lib/src/services/correction/dart/create_getter.dart
index 60620d7..ad72979 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/create_getter.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/create_getter.dart
@@ -74,7 +74,7 @@
       return;
     }
     var targetSource = targetElement.source;
-    if (targetSource == null || targetSource.uri.isScheme('dart')) {
+    if (targetSource == null || targetElement.library?.isInSdk == true) {
       return;
     }
     // prepare target declaration
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/create_method.dart b/pkg/analysis_server/lib/src/services/correction/dart/create_method.dart
index 04a0cf8..14b09df 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/create_method.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/create_method.dart
@@ -154,7 +154,7 @@
         return;
       }
       targetElement = targetClassElement;
-      if (targetClassElement.librarySource.uri.isScheme('dart')) {
+      if (targetClassElement.library.isInSdk) {
         return;
       }
       // prepare target ClassDeclaration
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/create_setter.dart b/pkg/analysis_server/lib/src/services/correction/dart/create_setter.dart
index 9acc150..5133d6a 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/create_setter.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/create_setter.dart
@@ -73,7 +73,7 @@
       return;
     }
     var targetSource = targetElement.source;
-    if (targetSource == null || targetSource.uri.isScheme('dart')) {
+    if (targetSource == null || targetElement.library?.isInSdk == true) {
       return;
     }
     // prepare target declaration
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/import_library.dart b/pkg/analysis_server/lib/src/services/correction/dart/import_library.dart
index 4536421..e6b8f80 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/import_library.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/import_library.dart
@@ -306,7 +306,7 @@
       }
       // Compute the fix kind.
       FixKind fixKind;
-      if (librarySource.uri.isScheme('dart')) {
+      if (libraryElement.isInSdk) {
         fixKind = DartFixKind.IMPORT_LIBRARY_SDK;
       } else if (_isLibSrcPath(librarySource.fullName)) {
         // Bad: non-API.
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 447bb63..c63b94e 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
@@ -953,7 +953,8 @@
 CompileTimeErrorCode.UNDEFINED_CONSTRUCTOR_IN_INITIALIZER_DEFAULT:
   status: hasFix
 CompileTimeErrorCode.UNDEFINED_ENUM_CONSTANT:
-  status: needsEvaluation
+  status: hasFix
+  issue: https://github.com/dart-lang/sdk/issues/47643
 CompileTimeErrorCode.UNDEFINED_ENUM_CONSTRUCTOR_NAMED:
   status: needsFix
   since: 2.17
diff --git a/pkg/analysis_server/lib/src/services/correction/fix.dart b/pkg/analysis_server/lib/src/services/correction/fix.dart
index 0a1eee9..1909be2 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix.dart
@@ -123,6 +123,11 @@
     DartFixKindPriority.IN_FILE,
     'Add missing debug property references everywhere in file',
   );
+  static const ADD_ENUM_CONSTANT = FixKind(
+    'dart.fix.add.enumConstant',
+    DartFixKindPriority.DEFAULT,
+    "Add enum constant '{0}'",
+  );
   static const ADD_EOL_AT_END_OF_FILE = FixKind(
     'dart.fix.add.eolAtEndOfFile',
     DartFixKindPriority.DEFAULT,
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 a24a679..f0c4310 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -10,6 +10,7 @@
 import 'package:analysis_server/src/services/correction/dart/add_await.dart';
 import 'package:analysis_server/src/services/correction/dart/add_const.dart';
 import 'package:analysis_server/src/services/correction/dart/add_diagnostic_property_reference.dart';
+import 'package:analysis_server/src/services/correction/dart/add_enum_constant.dart';
 import 'package:analysis_server/src/services/correction/dart/add_eol_at_end_of_file.dart';
 import 'package:analysis_server/src/services/correction/dart/add_explicit_cast.dart';
 import 'package:analysis_server/src/services/correction/dart/add_field_formal_parameters.dart';
@@ -1083,6 +1084,9 @@
     CompileTimeErrorCode.UNDEFINED_CLASS_BOOLEAN: [
       ReplaceBooleanWithBool.new,
     ],
+    CompileTimeErrorCode.UNDEFINED_ENUM_CONSTANT: [
+      AddEnumConstant.new,
+    ],
     CompileTimeErrorCode.UNDEFINED_EXTENSION_GETTER: [
       ChangeTo.getterOrSetter,
       CreateGetter.new,
diff --git a/pkg/analysis_server/lib/src/services/refactoring/rename.dart b/pkg/analysis_server/lib/src/services/refactoring/rename.dart
index 8fbe15a..7f93bf1 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/rename.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/rename.dart
@@ -73,7 +73,7 @@
   @override
   Future<RefactoringStatus> checkInitialConditions() {
     var result = RefactoringStatus();
-    if (element.source!.uri.isScheme('dart')) {
+    if (element.library?.isInSdk == true) {
       var message = format(
           "The {0} '{1}' is defined in the SDK, so cannot be renamed.",
           getElementKindName(element),
diff --git a/pkg/analysis_server/test/src/services/correction/fix/add_enum_constant_test.dart b/pkg/analysis_server/test/src/services/correction/fix/add_enum_constant_test.dart
new file mode 100644
index 0000000..4fa8591
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/add_enum_constant_test.dart
@@ -0,0 +1,232 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// 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:analysis_server/src/services/correction/fix.dart';
+import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'fix_processor.dart';
+
+void main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(AddEnumConstantTest);
+  });
+}
+
+@reflectiveTest
+class AddEnumConstantTest extends FixProcessorTest {
+  @override
+  FixKind get kind => DartFixKind.ADD_ENUM_CONSTANT;
+
+  Future<void> test_add() async {
+    await resolveTestCode('''
+enum E {ONE}
+
+E e() {
+  return E.TWO;
+}
+''');
+    await assertHasFix('''
+enum E {ONE, TWO}
+
+E e() {
+  return E.TWO;
+}
+''', matchFixMessage: "Add enum constant 'TWO'");
+  }
+
+  Future<void> test_differentLibrary() async {
+    addSource('$testPackageLibPath/a.dart', '''
+enum E {ONE}
+''');
+
+    await resolveTestCode('''
+import 'a.dart';
+
+E e() {
+  return E.TWO;
+}
+''');
+
+    await assertHasFix('''
+enum E {ONE, TWO}
+''', target: '$testPackageLibPath/a.dart');
+  }
+
+  Future<void> test_named() async {
+    await resolveTestCode('''
+enum E {
+  ONE.named();
+
+  const E.named();
+}
+
+E e() {
+  return E.TWO;
+}
+''');
+
+    await assertHasFix('''
+enum E {
+  ONE.named(), TWO.named();
+
+  const E.named();
+}
+
+E e() {
+  return E.TWO;
+}
+''');
+  }
+
+  Future<void> test_named_factory() async {
+    await resolveTestCode('''
+enum E {
+  ONE.named();
+
+  const E.named();
+  factory E.f() => ONE;
+}
+
+E e() {
+  return E.TWO;
+}
+''');
+
+    await assertHasFix('''
+enum E {
+  ONE.named(), TWO.named();
+
+  const E.named();
+  factory E.f() => ONE;
+}
+
+E e() {
+  return E.TWO;
+}
+''');
+  }
+
+  Future<void> test_named_named() async {
+    await resolveTestCode('''
+enum E {
+  ONE.something(), TWO.other();
+
+  const E.something();
+  const E.other();
+}
+
+E e() {
+  return E.THREE;
+}
+''');
+
+    await assertNoFix();
+  }
+
+  Future<void> test_named_non_zero() async {
+    await resolveTestCode('''
+enum E {
+  ONE.named(1);
+
+  final int i;
+  const E.named(this.i);
+}
+
+E e() {
+  return E.TWO;
+}
+''');
+
+    await assertNoFix();
+  }
+
+  Future<void> test_named_unnamed() async {
+    await resolveTestCode('''
+enum E {
+  ONE.named();
+
+  const E.named();
+  const E();
+}
+
+E e() {
+  return E.TWO;
+}
+''');
+
+    await assertNoFix();
+  }
+
+  Future<void> test_unnamed() async {
+    await resolveTestCode('''
+enum E {
+  ONE;
+
+  const E();
+}
+
+E e() {
+  return E.TWO;
+}
+''');
+
+    await assertHasFix('''
+enum E {
+  ONE, TWO;
+
+  const E();
+}
+
+E e() {
+  return E.TWO;
+}
+''');
+  }
+
+  Future<void> test_unnamed_factory() async {
+    await resolveTestCode('''
+enum E {
+  ONE;
+
+  const E();
+  factory E.f() => ONE;
+}
+
+E e() {
+  return E.TWO;
+}
+''');
+
+    await assertHasFix('''
+enum E {
+  ONE, TWO;
+
+  const E();
+  factory E.f() => ONE;
+}
+
+E e() {
+  return E.TWO;
+}
+''');
+  }
+
+  Future<void> test_unnamed_non_zero() async {
+    await resolveTestCode('''
+enum E {
+  ONE(1);
+
+  final int i;
+  const E(this.i);
+}
+
+E e() {
+  return E.TWO;
+}
+''');
+
+    await assertNoFix();
+  }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/test_all.dart b/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
index d2b42a9..278017c 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
@@ -10,6 +10,7 @@
 import 'add_curly_braces_test.dart' as add_curly_braces;
 import 'add_diagnostic_property_reference_test.dart'
     as add_diagnostic_property_reference;
+import 'add_enum_constant_test.dart' as add_enum_constant_test;
 import 'add_eol_at_end_of_file_test.dart' as add_eol_at_end_of_file;
 import 'add_explicit_cast_test.dart' as add_explicit_cast;
 import 'add_field_formal_parameters_test.dart' as add_field_formal_parameters;
@@ -238,6 +239,7 @@
     add_const.main();
     add_curly_braces.main();
     add_diagnostic_property_reference.main();
+    add_enum_constant_test.main();
     add_eol_at_end_of_file.main();
     add_explicit_cast.main();
     add_field_formal_parameters.main();