diagnostic property types support

Adds support for:

* ints
* doubles
* enums
* Strings

Up next: Colors, Iterables, Transforms and a catch-all.

Also note the TODO to change how types are being written (will be needed for Iterables).

See: https://github.com/dart-lang/sdk/issues/38633

Change-Id: I9d545ce9e090059ae330f9ae5dadf8e7d4b373c7
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/119725
Commit-Queue: Phil Quitslund <pquitslund@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analysis_server/lib/src/services/correction/base_processor.dart b/pkg/analysis_server/lib/src/services/correction/base_processor.dart
index 1ce5e03..3c3870b 100644
--- a/pkg/analysis_server/lib/src/services/correction/base_processor.dart
+++ b/pkg/analysis_server/lib/src/services/correction/base_processor.dart
@@ -74,7 +74,7 @@
     SimpleIdentifier name = node;
     final parent = node.parent;
 
-    var type;
+    DartType type;
 
     // Getter.
     if (parent is MethodDeclaration) {
@@ -96,53 +96,72 @@
 
     if (type == null) {
       return null;
-    } else if (type.isDartCoreBool) {
-      ClassDeclaration classDeclaration =
-          parent.thisOrAncestorOfType<ClassDeclaration>();
-      final debugFillProperties =
-          classDeclaration.getMethod('debugFillProperties');
-      if (debugFillProperties != null) {
-        final body = debugFillProperties.body;
-        if (body is BlockFunctionBody) {
-          BlockFunctionBody functionBody = body;
+    }
 
-          var offset;
-          var prefix;
-          if (functionBody.block.statements.isEmpty) {
-            offset = functionBody.block.leftBracket.offset;
-            prefix = utils.getLinePrefix(offset) + utils.getIndent(1);
-          } else {
-            offset = functionBody.block.statements.last.endToken.offset;
-            prefix = utils.getLinePrefix(offset);
-          }
+    var constructorInvocation;
+    if (type.isDartCoreBool) {
+      constructorInvocation = 'DiagnosticsProperty<bool>';
+    } else if (type.isDartCoreInt) {
+      constructorInvocation = 'IntProperty';
+    } else if (type.isDartCoreDouble) {
+      constructorInvocation = 'DoubleProperty';
+    } else if (type.isDartCoreString) {
+      constructorInvocation = 'StringProperty';
+    } else if (isEnum(type)) {
+      constructorInvocation = 'EnumProperty';
+    }
 
-          var parameters = debugFillProperties.parameters.parameters;
-          var propertiesBuilderName;
-          for (var parameter in parameters) {
-            if (parameter is SimpleFormalParameter) {
-              final type = parameter.type;
-              if (type is TypeName) {
-                if (type.name.name == 'DiagnosticPropertiesBuilder') {
-                  propertiesBuilderName = parameter.identifier.name;
-                  break;
-                }
+    // todo (pq): migrate type string generation to within change and use DartEditBuilder.writeType
+
+    if (constructorInvocation == null) {
+      return null;
+    }
+
+    ClassDeclaration classDeclaration =
+        parent.thisOrAncestorOfType<ClassDeclaration>();
+    final debugFillProperties =
+        classDeclaration.getMethod('debugFillProperties');
+    if (debugFillProperties != null) {
+      final body = debugFillProperties.body;
+      if (body is BlockFunctionBody) {
+        BlockFunctionBody functionBody = body;
+
+        var offset;
+        var prefix;
+        if (functionBody.block.statements.isEmpty) {
+          offset = functionBody.block.leftBracket.offset;
+          prefix = utils.getLinePrefix(offset) + utils.getIndent(1);
+        } else {
+          offset = functionBody.block.statements.last.endToken.offset;
+          prefix = utils.getLinePrefix(offset);
+        }
+
+        var parameters = debugFillProperties.parameters.parameters;
+        var propertiesBuilderName;
+        for (var parameter in parameters) {
+          if (parameter is SimpleFormalParameter) {
+            final type = parameter.type;
+            if (type is TypeName) {
+              if (type.name.name == 'DiagnosticPropertiesBuilder') {
+                propertiesBuilderName = parameter.identifier.name;
+                break;
               }
             }
           }
-          if (propertiesBuilderName == null) {
-            return null;
-          }
-
-          final changeBuilder = _newDartChangeBuilder();
-          await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
-            builder.addInsertion(utils.getLineNext(offset),
-                (DartEditBuilder builder) {
-              builder.write(
-                  "$prefix$propertiesBuilderName.add(DiagnosticsProperty<bool>('${name.name}', ${name.name}));$eol");
-            });
-          });
-          return changeBuilder;
         }
+        if (propertiesBuilderName == null) {
+          return null;
+        }
+
+        final changeBuilder = _newDartChangeBuilder();
+        await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
+          builder.addInsertion(utils.getLineNext(offset),
+              (DartEditBuilder builder) {
+            builder.write(
+                "$prefix$propertiesBuilderName.add($constructorInvocation('${name.name}', ${name.name}));$eol");
+          });
+        });
+        return changeBuilder;
       }
     }
 
@@ -1189,6 +1208,11 @@
     return null;
   }
 
+  bool isEnum(DartType type) {
+    final element = type.element;
+    return element is ClassElement && element.isEnum;
+  }
+
   @protected
   bool setupCompute() {
     final locator = NodeLocator(selectionOffset, selectionEnd);
diff --git a/pkg/analysis_server/test/src/services/correction/fix/add_diagnostic_property_reference_test.dart b/pkg/analysis_server/test/src/services/correction/fix/add_diagnostic_property_reference_test.dart
index 8912d34..13f5ab3 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/add_diagnostic_property_reference_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/add_diagnostic_property_reference_test.dart
@@ -115,14 +115,100 @@
 ''');
   }
 
+  test_doubleField_debugFillProperties() async {
+    await resolveTestUnit('''
+class A extends Widget {
+  double /*LINT*/field;
+  @override
+  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+    super.debugFillProperties(properties);
+  }
+}
+''');
+    await assertHasFix('''
+class A extends Widget {
+  double /*LINT*/field;
+  @override
+  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+    super.debugFillProperties(properties);
+    properties.add(DoubleProperty('field', field));
+  }
+}
+''');
+  }
+
+  test_enumField_debugFillProperties() async {
+    await resolveTestUnit('''
+enum foo {bar}
+class A extends Widget {
+  foo /*LINT*/field;
+  @override
+  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+    super.debugFillProperties(properties);
+  }
+}
+''');
+    await assertHasFix('''
+enum foo {bar}
+class A extends Widget {
+  foo /*LINT*/field;
+  @override
+  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+    super.debugFillProperties(properties);
+    properties.add(EnumProperty('field', field));
+  }
+}
+''');
+  }
+
+  test_intField_debugFillProperties() async {
+    await resolveTestUnit('''
+class A extends Widget {
+  int /*LINT*/field;
+  @override
+  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+    super.debugFillProperties(properties);
+  }
+}
+''');
+    await assertHasFix('''
+class A extends Widget {
+  int /*LINT*/field;
+  @override
+  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+    super.debugFillProperties(properties);
+    properties.add(IntProperty('field', field));
+  }
+}
+''');
+  }
+
+  test_stringField_debugFillProperties() async {
+    await resolveTestUnit('''
+class A extends Widget {
+  String /*LINT*/field;
+  @override
+  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+    super.debugFillProperties(properties);
+  }
+}
+''');
+    await assertHasFix('''
+class A extends Widget {
+  String /*LINT*/field;
+  @override
+  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+    super.debugFillProperties(properties);
+    properties.add(StringProperty('field', field));
+  }
+}
+''');
+  }
+
   // todo (pq): tests for no debugFillProperties method
   // todo (pq): consider a test for a body w/ no CR
   // todo (pq): support for ColorProperty -- for Color
-  // todo (pq): support for EnumProperty -- for any enum class
-  // todo (pq): support for IntProperty -- int
-  // todo (pq): support for DoubleProperty -- double
   // todo (pq): support for IterableProperty -- any iterable
-  // todo (pq): support for StringProperty -- string
   // todo (pq): support for TransformProperty -- Matrix4
   // todo (pq): support for DiagnosticsProperty for any T that doesn't match one of the other cases
 }