[Analyzer] Make LSP literal types optional in constructors and validate their values

Change-Id: Id6dddd4ed4819a75da4226ee57d73a65c15c9048
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/164965
Commit-Queue: Danny Tuppeny <danny@tuppeny.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analysis_server/lib/lsp_protocol/protocol_generated.dart b/pkg/analysis_server/lib/lsp_protocol/protocol_generated.dart
index daf702f..6578de6 100644
--- a/pkg/analysis_server/lib/lsp_protocol/protocol_generated.dart
+++ b/pkg/analysis_server/lib/lsp_protocol/protocol_generated.dart
@@ -4897,9 +4897,9 @@
   static const jsonHandler =
       LspJsonHandler(CreateFile.canParse, CreateFile.fromJson);
 
-  CreateFile({@required this.kind, @required this.uri, this.options}) {
-    if (kind == null) {
-      throw 'kind is required but was not provided';
+  CreateFile({this.kind = 'create', @required this.uri, this.options}) {
+    if (kind != 'create') {
+      throw 'kind may only be the literal \'create\'';
     }
     if (uri == null) {
       throw 'uri is required but was not provided';
@@ -5922,9 +5922,9 @@
   static const jsonHandler =
       LspJsonHandler(DeleteFile.canParse, DeleteFile.fromJson);
 
-  DeleteFile({@required this.kind, @required this.uri, this.options}) {
-    if (kind == null) {
-      throw 'kind is required but was not provided';
+  DeleteFile({this.kind = 'delete', @required this.uri, this.options}) {
+    if (kind != 'delete') {
+      throw 'kind may only be the literal \'delete\'';
     }
     if (uri == null) {
       throw 'uri is required but was not provided';
@@ -16615,12 +16615,12 @@
       LspJsonHandler(RenameFile.canParse, RenameFile.fromJson);
 
   RenameFile(
-      {@required this.kind,
+      {this.kind = 'rename',
       @required this.oldUri,
       @required this.newUri,
       this.options}) {
-    if (kind == null) {
-      throw 'kind is required but was not provided';
+    if (kind != 'rename') {
+      throw 'kind may only be the literal \'rename\'';
     }
     if (oldUri == null) {
       throw 'oldUri is required but was not provided';
@@ -23768,13 +23768,13 @@
       WorkDoneProgressBegin.canParse, WorkDoneProgressBegin.fromJson);
 
   WorkDoneProgressBegin(
-      {@required this.kind,
+      {this.kind = 'begin',
       @required this.title,
       this.cancellable,
       this.message,
       this.percentage}) {
-    if (kind == null) {
-      throw 'kind is required but was not provided';
+    if (kind != 'begin') {
+      throw 'kind may only be the literal \'begin\'';
     }
     if (title == null) {
       throw 'title is required but was not provided';
@@ -24088,9 +24088,9 @@
   static const jsonHandler = LspJsonHandler(
       WorkDoneProgressEnd.canParse, WorkDoneProgressEnd.fromJson);
 
-  WorkDoneProgressEnd({@required this.kind, this.message}) {
-    if (kind == null) {
-      throw 'kind is required but was not provided';
+  WorkDoneProgressEnd({this.kind = 'end', this.message}) {
+    if (kind != 'end') {
+      throw 'kind may only be the literal \'end\'';
     }
   }
   static WorkDoneProgressEnd fromJson(Map<String, dynamic> json) {
@@ -24433,9 +24433,9 @@
       WorkDoneProgressReport.canParse, WorkDoneProgressReport.fromJson);
 
   WorkDoneProgressReport(
-      {@required this.kind, this.cancellable, this.message, this.percentage}) {
-    if (kind == null) {
-      throw 'kind is required but was not provided';
+      {this.kind = 'report', this.cancellable, this.message, this.percentage}) {
+    if (kind != 'report') {
+      throw 'kind may only be the literal \'report\'';
     }
   }
   static WorkDoneProgressReport fromJson(Map<String, dynamic> json) {
diff --git a/pkg/analysis_server/lib/src/lsp/constants.dart b/pkg/analysis_server/lib/src/lsp/constants.dart
index 57febf1..b65a4ca 100644
--- a/pkg/analysis_server/lib/src/lsp/constants.dart
+++ b/pkg/analysis_server/lib/src/lsp/constants.dart
@@ -49,10 +49,9 @@
 /// Characters to trigger formatting when format-on-type is enabled.
 const dartTypeFormattingCharacters = ['}', ';'];
 
-final analyzingProgressBegin =
-    WorkDoneProgressBegin(kind: 'begin', title: 'Analyzing…');
+final analyzingProgressBegin = WorkDoneProgressBegin(title: 'Analyzing…');
 
-final analyzingProgressEnd = WorkDoneProgressEnd(kind: 'end');
+final analyzingProgressEnd = WorkDoneProgressEnd();
 
 /// A [ProgressToken] used for reporting progress when the server is analyzing.
 final analyzingProgressToken = Either2<num, String>.t2('ANALYZING');
diff --git a/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart b/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart
index 415f142..b911615 100644
--- a/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart
+++ b/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart
@@ -272,25 +272,42 @@
   buffer
     ..writeIndented('${interface.name}({')
     ..write(allFields.map((field) {
-      final annotation =
-          !field.allowsNull && !field.allowsUndefined ? '@required' : '';
-      return '$annotation this.${field.name}';
+      final isLiteral = field.type is LiteralType;
+      final isRequired =
+          !isLiteral && !field.allowsNull && !field.allowsUndefined;
+      final annotation = isRequired ? '@required' : '';
+      final valueCode =
+          isLiteral ? ' = ${(field.type as LiteralType).literal}' : '';
+      return '$annotation this.${field.name}$valueCode';
     }).join(', '))
     ..write('})');
-  final fieldsWithValidation =
-      allFields.where((f) => !f.allowsNull && !f.allowsUndefined).toList();
+  final fieldsWithValidation = allFields
+      .where(
+          (f) => (!f.allowsNull && !f.allowsUndefined) || f.type is LiteralType)
+      .toList();
   if (fieldsWithValidation.isNotEmpty) {
     buffer
       ..writeIndentedln(' {')
       ..indent();
     for (var field in fieldsWithValidation) {
-      buffer
-        ..writeIndentedln('if (${field.name} == null) {')
-        ..indent()
-        ..writeIndentedln(
-            "throw '${field.name} is required but was not provided';")
-        ..outdent()
-        ..writeIndentedln('}');
+      final type = field.type;
+      if (type is LiteralType) {
+        buffer
+          ..writeIndentedln('if (${field.name} != ${type.literal}) {')
+          ..indent()
+          ..writeIndentedln(
+              "throw '${field.name} may only be the literal ${type.literal.replaceAll("'", "\\'")}';")
+          ..outdent()
+          ..writeIndentedln('}');
+      } else if (!field.allowsNull && !field.allowsUndefined) {
+        buffer
+          ..writeIndentedln('if (${field.name} == null) {')
+          ..indent()
+          ..writeIndentedln(
+              "throw '${field.name} is required but was not provided';")
+          ..outdent()
+          ..writeIndentedln('}');
+      }
     }
     buffer
       ..outdent()