Version 2.11.0-175.0.dev

Merge commit '5a087b90ce55c9d3da9bf1b63211fbd09a7bff7c' into 'dev'
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/code_template.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/code_template.dart
index cf17314..b5936f4 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/code_template.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/code_template.dart
@@ -17,7 +17,9 @@
 
   /// Initialize a newly generated code template with the given [kind] and
   /// [components].
-  CodeTemplate(this.kind, this.components);
+  CodeTemplate(this.kind, this.components)
+      : assert(kind != null),
+        assert(components != null);
 
   /// Use the [context] to validate that this template will be able to generate
   /// a value.
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_error_code.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_error_code.dart
index 2007991..8394acd 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_error_code.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_error_code.dart
@@ -20,6 +20,15 @@
 
   /**
    * Parameters:
+   * 0: the key with which the value is associated
+   * 1: the allowed values as a comma-separated list
+   */
+  static const TransformSetErrorCode invalidValueOneOf = TransformSetErrorCode(
+      'invalid_value_one_of',
+      "The value of '{0}' must be one of the following: '{1}'.");
+
+  /**
+   * Parameters:
    * 0: the missing key
    */
   static const TransformSetErrorCode missingKey =
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart
index 96b3af0..d283e88 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart
@@ -147,19 +147,21 @@
             TransformSetErrorCode.missingTemplateEnd,
             templateOffset + variableStart,
             2);
-        return null;
+        // Ignore the invalid component, treating it as if it extended to the
+        // end of the template.
+        return components;
       } else {
         var name = template.substring(variableStart + 2, endIndex).trim();
-        var extractor = generators[name];
-        if (extractor == null) {
+        var generator = generators[name];
+        if (generator == null) {
           errorReporter.reportErrorForOffset(
               TransformSetErrorCode.undefinedVariable,
               templateOffset + template.indexOf(name, variableStart),
               name.length,
               [name]);
-          return null;
+          // Ignore the invalid component.
         } else {
-          components.add(TemplateVariable(extractor));
+          components.add(TemplateVariable(generator));
         }
       }
       textStart = endIndex + 2;
@@ -217,6 +219,15 @@
     return null;
   }
 
+  /// Report that the value represented by the [node] does not have the
+  /// [expectedType], using the [context] to get the key to use in the message.
+  Null _reportInvalidValueOneOf(
+      YamlNode node, ErrorContext context, List<String> allowedValues) {
+    _reportError(TransformSetErrorCode.invalidValueOneOf, node,
+        [context.key, allowedValues.join(', ')]);
+    return null;
+  }
+
   /// Report that a required key is missing, using the [context] to locate the
   /// node associated with the diagnostic and the key to use in the message.
   Null _reportMissingKey(ErrorContext context) {
@@ -372,19 +383,25 @@
     if (node is YamlMap) {
       var kind = _translateString(node.valueAt(_kindKey),
           ErrorContext(key: _kindKey, parentNode: node));
-      if (kind == _addTypeParameterKind) {
-        return _translateAddTypeParameterChange(node);
-      } else if (kind == _renameKind) {
-        return _translateRenameChange(node);
+      if (kind == null) {
+        return null;
       } else if (kind == _addParameterKind) {
         _translateAddParameterChange(node);
         return null;
+      } else if (kind == _addTypeParameterKind) {
+        return _translateAddTypeParameterChange(node);
       } else if (kind == _removeParameterKind) {
         _translateRemoveParameterChange(node);
         return null;
+      } else if (kind == _renameKind) {
+        return _translateRenameChange(node);
       }
-      // TODO(brianwilkerson) Report the invalid change kind.
-      return null;
+      return _reportInvalidValueOneOf(node, context, [
+        _addParameterKind,
+        _addTypeParameterKind,
+        _removeParameterKind,
+        _renameKind,
+      ]);
     } else {
       return _reportInvalidValue(node, context, 'Map');
     }
@@ -425,10 +442,11 @@
           return null;
         }
       }
-      var extractors = _translateTemplateVariables(node.valueAt(_variablesKey),
+      // TODO(brianwilkerson) We should report unreferenced variables.
+      var generators = _translateTemplateVariables(node.valueAt(_variablesKey),
           ErrorContext(key: _variablesKey, parentNode: node));
       var components =
-          _extractTemplateComponents(template, extractors, templateOffset);
+          _extractTemplateComponents(template, generators, templateOffset);
       return CodeTemplate(kind, components);
     } else if (node == null) {
       if (required) {
@@ -668,7 +686,7 @@
       for (var entry in node.nodes.entries) {
         var name = _translateKey(entry.key);
         if (name != null) {
-          var value = _translateValueExtractor(
+          var value = _translateValueGenerator(
               entry.value, ErrorContext(key: name, parentNode: node));
           if (value != null) {
             generators[name] = value;
@@ -782,17 +800,21 @@
   /// extractor, or `null` if the [node] does not represent a valid value
   /// extractor. If the [node] is not valid, use the [context] to report the
   /// error.
-  ValueGenerator _translateValueExtractor(YamlNode node, ErrorContext context) {
+  ValueGenerator _translateValueGenerator(YamlNode node, ErrorContext context) {
     if (node is YamlMap) {
       var kind = _translateString(node.valueAt(_kindKey),
           ErrorContext(key: _kindKey, parentNode: node));
-      if (kind == _argumentKind) {
+      if (kind == null) {
+        return null;
+      } else if (kind == _argumentKind) {
         return _translateArgumentExtractor(node);
       } else if (kind == _importKind) {
         return _translateImportValue(node);
       }
-      // TODO(brianwilkerson) Report the invalid extractor kind.
-      return null;
+      return _reportInvalidValueOneOf(node, context, [
+        _argumentKind,
+        _importKind,
+      ]);
     } else if (node == null) {
       return _reportMissingKey(context);
     } else {
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/invalid_value_one_of_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/invalid_value_one_of_test.dart
new file mode 100644
index 0000000..2e606bb
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/invalid_value_one_of_test.dart
@@ -0,0 +1,56 @@
+// Copyright (c) 2020, 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/data_driven/transform_set_error_code.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../transform_set_parser_test_support.dart';
+
+void main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(InvalidValueOneOfTest);
+  });
+}
+
+@reflectiveTest
+class InvalidValueOneOfTest extends AbstractTransformSetParserTest {
+  void test_changeKind() {
+    assertErrors('''
+version: 1
+transforms:
+- title: 'Rename A'
+  date: 2020-09-08
+  element:
+    uris: ['test.dart']
+    class: 'A'
+  changes:
+    - kind: 'invalid'
+''', [
+      error(TransformSetErrorCode.invalidValueOneOf, 129, 16),
+    ]);
+  }
+
+  void test_valueKind() {
+    assertErrors('''
+version: 1
+transforms:
+- title: 'Rename A'
+  date: 2020-09-08
+  element:
+    uris: ['test.dart']
+    class: 'A'
+  changes:
+    - kind: 'addTypeParameter'
+      index: 0
+      name: 'T'
+      argumentValue: 
+        expression: ''
+        variables:
+          x:
+            kind: 'invalid'
+''', [
+      error(TransformSetErrorCode.invalidValueOneOf, 274, 16),
+    ]);
+  }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/test_all.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/test_all.dart
index 76bb9b4..e8bbf94 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/test_all.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/test_all.dart
@@ -4,6 +4,7 @@
 
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
+import 'invalid_value_one_of_test.dart' as invalid_value_one_of;
 import 'invalid_value_test.dart' as invalid_value;
 import 'missing_key_test.dart' as missing_key;
 import 'missing_template_end_test.dart' as missing_template_end;
@@ -13,6 +14,7 @@
 
 void main() {
   defineReflectiveSuite(() {
+    invalid_value_one_of.main();
     invalid_value.main();
     missing_key.main();
     missing_template_end.main();
diff --git a/pkg/dartdev/lib/src/commands/run.dart b/pkg/dartdev/lib/src/commands/run.dart
index 8a0a2ef..ae2ec2a 100644
--- a/pkg/dartdev/lib/src/commands/run.dart
+++ b/pkg/dartdev/lib/src/commands/run.dart
@@ -179,7 +179,9 @@
       // The arg.contains('.') matches a file name pattern, i.e. some 'foo.dart'
       if (arg.contains('.')) {
         argsContainFile = true;
-      } else if (arg == '--help' || arg == '-h' || arg == 'help') {
+      } else if (!argsContainFile &&
+          (arg == '--help' || arg == '-h' || arg == 'help')) {
+        // Only print usage if a help flag is provided before the script name.
         printUsage();
         return 0;
       }
diff --git a/pkg/dds/CHANGELOG.md b/pkg/dds/CHANGELOG.md
index b7dc7ba..dcacb48 100644
--- a/pkg/dds/CHANGELOG.md
+++ b/pkg/dds/CHANGELOG.md
@@ -1,3 +1,9 @@
+# 1.4.0
+- Added `done` property to `DartDevelopmentService`.
+- Throw `DartDeveloperServiceException` when shutdown occurs during startup.
+- Fixed issue where `StateError` was thrown when DDS was shutdown with pending
+  requests.
+
 # 1.3.5
 
 - Fixed issue where clients subscribing to the `Service` stream were not being
diff --git a/pkg/dds/lib/dds.dart b/pkg/dds/lib/dds.dart
index f35e4d3..0f716c6 100644
--- a/pkg/dds/lib/dds.dart
+++ b/pkg/dds/lib/dds.dart
@@ -118,6 +118,9 @@
   /// authentication code to connect.
   bool get authCodesEnabled;
 
+  /// Completes when this [DartDevelopmentService] has shut down.
+  Future<void> get done;
+
   /// The HTTP [Uri] of the remote VM service instance that this service will
   /// forward requests to.
   Uri get remoteVmServiceUri;
diff --git a/pkg/dds/lib/src/dds_impl.dart b/pkg/dds/lib/src/dds_impl.dart
index a4c2f81..06327a8 100644
--- a/pkg/dds/lib/src/dds_impl.dart
+++ b/pkg/dds/lib/src/dds_impl.dart
@@ -16,21 +16,50 @@
   }
 
   Future<void> startService() async {
+    bool started = false;
+    final completer = Completer<void>();
     // TODO(bkonyi): throw if we've already shutdown.
     // Establish the connection to the VM service.
     _vmServiceSocket = WebSocketChannel.connect(remoteVmServiceWsUri);
     _vmServiceClient = _BinaryCompatiblePeer(_vmServiceSocket, _streamManager);
     // Setup the JSON RPC client with the VM service.
-    unawaited(_vmServiceClient.listen().then((_) => shutdown()));
+    unawaited(
+      _vmServiceClient.listen().then(
+        (_) {
+          shutdown();
+          if (!started && !completer.isCompleted) {
+            completer.completeError(
+              DartDevelopmentServiceException._(
+                'Failed to start Dart Development Service',
+              ),
+            );
+          }
+        },
+        onError: (e, st) {
+          shutdown();
+          if (!completer.isCompleted) {
+            completer.completeError(e, st);
+          }
+        },
+      ),
+    );
+    try {
+      // Setup stream event handling.
+      await streamManager.listen();
 
-    // Setup stream event handling.
-    await streamManager.listen();
+      // Populate initial isolate state.
+      await _isolateManager.initialize();
 
-    // Populate initial isolate state.
-    await _isolateManager.initialize();
-
-    // Once we have a connection to the VM service, we're ready to spawn the intermediary.
-    await _startDDSServer();
+      // Once we have a connection to the VM service, we're ready to spawn the intermediary.
+      await _startDDSServer();
+      started = true;
+      completer.complete();
+    } on StateError {
+      /* Ignore json-rpc state errors */
+    } catch (e, st) {
+      completer.completeError(e, st);
+    }
+    return completer.future;
   }
 
   Future<void> _startDDSServer() async {
diff --git a/pkg/dds/lib/src/isolate_manager.dart b/pkg/dds/lib/src/isolate_manager.dart
index 50ace65..c94d4cd 100644
--- a/pkg/dds/lib/src/isolate_manager.dart
+++ b/pkg/dds/lib/src/isolate_manager.dart
@@ -160,6 +160,9 @@
 
   /// Initializes the set of running isolates.
   Future<void> initialize() async {
+    if (_initialized) {
+      return;
+    }
     final vm = await dds._vmServiceClient.sendRequest('getVM');
     final List<Map> isolateRefs = vm['isolates'].cast<Map<String, dynamic>>();
     // Check the pause event for each isolate to determine whether or not the
@@ -179,6 +182,7 @@
         isolateStarted(id, name);
       }
     }
+    _initialized = true;
   }
 
   /// Initializes state for a newly started isolate.
@@ -232,6 +236,7 @@
     return resumeResult;
   }
 
+  bool _initialized = false;
   final _DartDevelopmentService dds;
   final Map<String, _RunningIsolate> isolates = {};
 }
diff --git a/pkg/dds/pubspec.yaml b/pkg/dds/pubspec.yaml
index 4fb4ebe..1874281 100644
--- a/pkg/dds/pubspec.yaml
+++ b/pkg/dds/pubspec.yaml
@@ -3,7 +3,7 @@
   A library used to spawn the Dart Developer Service, used to communicate with
   a Dart VM Service instance.
 
-version: 1.3.5
+version: 1.4.0
 
 homepage: https://github.com/dart-lang/sdk/tree/master/pkg/dds
 
diff --git a/tools/VERSION b/tools/VERSION
index 275af0f..1fad799 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 11
 PATCH 0
-PRERELEASE 174
+PRERELEASE 175
 PRERELEASE_PATCH 0
\ No newline at end of file