Version 2.17.0-68.0.dev

Merge commit 'd1baf327a18dfc92476049a09985b645c08072f5' into 'dev'
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/extension_member_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/extension_member_contributor.dart
index 185cc30..acfcb38 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/extension_member_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/extension_member_contributor.dart
@@ -37,29 +37,23 @@
         return;
       }
 
-      var classOrMixin = request.target.containingNode
-          .thisOrAncestorOfType<ClassOrMixinDeclaration>();
-      if (classOrMixin != null) {
-        var type = classOrMixin.declaredElement?.thisType;
-        if (type != null) {
-          _addExtensionMembers(extensions, defaultKind, type);
-        }
+      var thisClassType = request.target.enclosingClassElement?.thisType;
+      if (thisClassType != null) {
+        _addExtensionMembers(extensions, defaultKind, thisClassType);
       } else {
-        var extension = request.target.containingNode
-            .thisOrAncestorOfType<ExtensionDeclaration>();
-        if (extension != null) {
-          var extendedType = extension.extendedType.type;
-          if (extendedType is InterfaceType) {
-            var types = [extendedType, ...extendedType.allSupertypes];
-            for (var type in types) {
-              var inheritanceDistance = memberBuilder.request.featureComputer
-                  .inheritanceDistanceFeature(
-                      extendedType.element, type.element);
-              _addTypeMembers(type, defaultKind, inheritanceDistance);
-            }
-            _addExtensionMembers(extensions, defaultKind, extendedType);
+        var thisExtendedType =
+            request.target.enclosingExtensionElement?.extendedType;
+        if (thisExtendedType is InterfaceType) {
+          var types = [thisExtendedType, ...thisExtendedType.allSupertypes];
+          for (var type in types) {
+            var inheritanceDistance = memberBuilder.request.featureComputer
+                .inheritanceDistanceFeature(
+                    thisExtendedType.element, type.element);
+            _addTypeMembers(type, defaultKind, inheritanceDistance);
           }
+          _addExtensionMembers(extensions, defaultKind, thisExtendedType);
         }
+        // TODO(scheglov) It seems that we don't support non-interface types.
       }
       return;
     }
diff --git a/pkg/analyzer_plugin/lib/src/utilities/completion/completion_target.dart b/pkg/analyzer_plugin/lib/src/utilities/completion/completion_target.dart
index 9c20706..ce716df 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/completion/completion_target.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/completion/completion_target.dart
@@ -117,6 +117,16 @@
   /// otherwise this is `null`.
   ParameterElement? _parameterElement;
 
+  /// The enclosing [ClassElement], or `null` if not in a class.
+  late final ClassElement? enclosingClassElement = containingNode
+      .thisOrAncestorOfType<ClassOrMixinDeclaration>()
+      ?.declaredElement;
+
+  /// The enclosing [ExtensionElement], or `null` if not in an extension.
+  late final ExtensionElement? enclosingExtensionElement = containingNode
+      .thisOrAncestorOfType<ExtensionDeclaration>()
+      ?.declaredElement;
+
   /// Compute the appropriate [CompletionTarget] for the given [offset] within
   /// the [entryPoint].
   factory CompletionTarget.forOffset(AstNode entryPoint, int offset) {
diff --git a/pkg/dartdev/lib/src/commands/devtools.dart b/pkg/dartdev/lib/src/commands/devtools.dart
index 35d216c..63ff69c 100644
--- a/pkg/dartdev/lib/src/commands/devtools.dart
+++ b/pkg/dartdev/lib/src/commands/devtools.dart
@@ -2,6 +2,7 @@
 // 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:args/args.dart';
 import 'package:dds/devtools_server.dart';
 import 'package:dds/src/devtools/utils.dart';
 import 'package:path/path.dart' as path;
@@ -10,162 +11,27 @@
 import '../sdk.dart';
 
 class DevToolsCommand extends DartdevCommand {
-  static const commandDescription =
-      'Open DevTools (optionally connecting to an existing application).';
-
-  static const protocolVersion = '1.1.0';
-  static const argHelp = 'help';
-  static const argVmUri = 'vm-uri';
-  static const argEnableNotifications = 'enable-notifications';
-  static const argAllowEmbedding = 'allow-embedding';
-  static const argAppSizeBase = 'appSizeBase';
-  static const argAppSizeTest = 'appSizeTest';
-  static const argHeadlessMode = 'headless';
-  static const argDebugMode = 'debug';
-  static const argLaunchBrowser = 'launch-browser';
-  static const argMachine = 'machine';
-  static const argHost = 'host';
-  static const argPort = 'port';
-  static const argProfileMemory = 'record-memory-profile';
-  static const argTryPorts = 'try-ports';
-  static const argVerbose = 'verbose';
-  static const argVersion = 'version';
-  static const launchDevToolsService = 'launchDevTools';
-
   DevToolsCommand({
     this.customDevToolsPath,
     bool verbose = false,
-  }) : super(
+  })  : _argParser = DevToolsServer.buildArgParser(verbose: verbose),
+        super(
           'devtools',
-          commandDescription,
+          DevToolsServer.commandDescription,
           verbose,
-        ) {
-    argParser
-      ..addFlag(
-        argVersion,
-        negatable: false,
-        help: 'Prints the DevTools version.',
-      )
-      ..addFlag(
-        argVerbose,
-        negatable: false,
-        abbr: 'v',
-        help: 'Output more informational messages.',
-      )
-      ..addOption(
-        argHost,
-        valueHelp: 'host',
-        help: 'Hostname to serve DevTools on (defaults to localhost).',
-      )
-      ..addOption(
-        argPort,
-        defaultsTo: '9100',
-        valueHelp: 'port',
-        help: 'Port to serve DevTools on; specify 0 to automatically use any '
-            'available port.',
-      )
-      ..addFlag(
-        argLaunchBrowser,
-        help:
-            'Launches DevTools in a browser immediately at start.\n(defaults to on unless in --machine mode)',
-      )
-      ..addFlag(
-        argMachine,
-        negatable: false,
-        help: 'Sets output format to JSON for consumption in tools.',
-      )
-      ..addSeparator('Memory profiling options:')
-      ..addOption(
-        argProfileMemory,
-        valueHelp: 'file',
-        defaultsTo: 'memory_samples.json',
-        help:
-            'Start devtools headlessly and write memory profiling samples to the '
-            'indicated file.',
-      );
-
-    if (verbose) {
-      argParser.addSeparator('App size options:');
-    }
-
-    // TODO(devoncarew): --appSizeBase and --appSizeTest should be renamed to
-    // something like --app-size-base and --app-size-test; #3146.
-    argParser
-      ..addOption(
-        argAppSizeBase,
-        valueHelp: 'appSizeBase',
-        help: 'Path to the base app size file used for app size debugging.',
-        hide: !verbose,
-      )
-      ..addOption(
-        argAppSizeTest,
-        valueHelp: 'appSizeTest',
-        help:
-            'Path to the test app size file used for app size debugging.\nThis '
-            'file should only be specified if --$argAppSizeBase is also specified.',
-        hide: !verbose,
-      );
-
-    if (verbose) {
-      argParser.addSeparator('Advanced options:');
-    }
-
-    // Args to show for verbose mode.
-    argParser
-      ..addOption(
-        argTryPorts,
-        defaultsTo: DevToolsServer.defaultTryPorts.toString(),
-        valueHelp: 'count',
-        help: 'The number of ascending ports to try binding to before failing '
-            'with an error. ',
-        hide: !verbose,
-      )
-      ..addFlag(
-        argEnableNotifications,
-        negatable: false,
-        help: 'Requests notification permissions immediately when a client '
-            'connects back to the server.',
-        hide: !verbose,
-      )
-      ..addFlag(
-        argAllowEmbedding,
-        help: 'Allow embedding DevTools inside an iframe.',
-        hide: !verbose,
-      )
-      ..addFlag(
-        argHeadlessMode,
-        negatable: false,
-        help: 'Causes the server to spawn Chrome in headless mode for use in '
-            'automated testing.',
-        hide: !verbose,
-      );
-
-    // Deprecated and hidden args.
-    // TODO: Remove this - prefer that clients use the rest arg.
-    argParser
-      ..addOption(
-        argVmUri,
-        defaultsTo: '',
-        help: 'VM Service protocol URI.',
-        hide: true,
-      )
-
-      // Development only args.
-      ..addFlag(
-        argDebugMode,
-        negatable: false,
-        help: 'Run a debug build of the DevTools web frontend.',
-        hide: true,
-      );
-  }
+        );
 
   final String? customDevToolsPath;
 
   @override
+  ArgParser get argParser => _argParser;
+  late final ArgParser _argParser;
+
+  @override
   String get name => 'devtools';
 
   @override
-  String get description => commandDescription;
+  String get description => DevToolsServer.commandDescription;
 
   @override
   String get invocation => '${super.invocation} [service protocol uri]';
@@ -173,29 +39,33 @@
   @override
   Future<int> run() async {
     final args = argResults!;
-    final bool version = args[argVersion];
-    final bool machineMode = args[argMachine];
+    final bool version = args[DevToolsServer.argVersion];
+    final bool machineMode = args[DevToolsServer.argMachine];
     // launchBrowser defaults based on machine-mode if not explicitly supplied.
-    final bool launchBrowser = args.wasParsed(argLaunchBrowser)
-        ? args[argLaunchBrowser]
+    final bool launchBrowser = args.wasParsed(DevToolsServer.argLaunchBrowser)
+        ? args[DevToolsServer.argLaunchBrowser]
         : !machineMode;
-    final bool enableNotifications = args[argEnableNotifications];
-    final bool allowEmbedding =
-        args.wasParsed(argAllowEmbedding) ? args[argAllowEmbedding] : true;
+    final bool enableNotifications =
+        args[DevToolsServer.argEnableNotifications];
+    final bool allowEmbedding = args.wasParsed(DevToolsServer.argAllowEmbedding)
+        ? args[DevToolsServer.argAllowEmbedding]
+        : true;
 
-    final port = args[argPort] != null ? int.tryParse(args[argPort]) ?? 0 : 0;
+    final port = args[DevToolsServer.argPort] != null
+        ? int.tryParse(args[DevToolsServer.argPort]) ?? 0
+        : 0;
 
-    final bool headlessMode = args[argHeadlessMode];
-    final bool debugMode = args[argDebugMode];
+    final bool headlessMode = args[DevToolsServer.argHeadlessMode];
+    final bool debugMode = args[DevToolsServer.argDebugMode];
 
-    final numPortsToTry = args[argTryPorts] != null
-        ? int.tryParse(args[argTryPorts]) ?? 0
+    final numPortsToTry = args[DevToolsServer.argTryPorts] != null
+        ? int.tryParse(args[DevToolsServer.argTryPorts]) ?? 0
         : DevToolsServer.defaultTryPorts;
 
-    final bool verboseMode = args[argVerbose];
-    final String? hostname = args[argHost];
-    final String? appSizeBase = args[argAppSizeBase];
-    final String? appSizeTest = args[argAppSizeTest];
+    final bool verboseMode = args[DevToolsServer.argVerbose];
+    final String? hostname = args[DevToolsServer.argHost];
+    final String? appSizeBase = args[DevToolsServer.argAppSizeBase];
+    final String? appSizeTest = args[DevToolsServer.argAppSizeTest];
 
     final sdkDir = path.dirname(sdk.dart);
     final fullSdk = sdkDir.endsWith('bin');
@@ -219,14 +89,14 @@
     String? serviceProtocolUri;
     if (args.rest.isNotEmpty) {
       serviceProtocolUri = args.rest.first;
-    } else if (args.wasParsed(argVmUri)) {
-      serviceProtocolUri = args[argVmUri];
+    } else if (args.wasParsed(DevToolsServer.argVmUri)) {
+      serviceProtocolUri = args[DevToolsServer.argVmUri];
     }
 
     // Support collecting profile data.
     String? profileFilename;
-    if (args.wasParsed(argProfileMemory)) {
-      profileFilename = args[argProfileMemory];
+    if (args.wasParsed(DevToolsServer.argProfileMemory)) {
+      profileFilename = args[DevToolsServer.argProfileMemory];
     }
     if (profileFilename != null && !path.isAbsolute(profileFilename)) {
       profileFilename = path.absolute(profileFilename);
diff --git a/pkg/dds/lib/devtools_server.dart b/pkg/dds/lib/devtools_server.dart
index ee451fa..1462458 100644
--- a/pkg/dds/lib/devtools_server.dart
+++ b/pkg/dds/lib/devtools_server.dart
@@ -5,8 +5,10 @@
 import 'dart:async';
 import 'dart:io';
 
+import 'package:args/args.dart';
 import 'package:browser_launcher/browser_launcher.dart';
 import 'package:http_multi_server/http_multi_server.dart';
+import 'package:path/path.dart' as path;
 import 'package:shelf/shelf.dart' as shelf;
 import 'package:shelf/shelf_io.dart' as shelf;
 
@@ -20,11 +22,154 @@
 class DevToolsServer {
   static const protocolVersion = '1.1.0';
   static const defaultTryPorts = 10;
+  static const commandDescription =
+      'Open DevTools (optionally connecting to an existing application).';
+
+  static const argHelp = 'help';
+  static const argVmUri = 'vm-uri';
+  static const argEnableNotifications = 'enable-notifications';
+  static const argAllowEmbedding = 'allow-embedding';
+  static const argAppSizeBase = 'appSizeBase';
+  static const argAppSizeTest = 'appSizeTest';
+  static const argHeadlessMode = 'headless';
+  static const argDebugMode = 'debug';
+  static const argLaunchBrowser = 'launch-browser';
+  static const argMachine = 'machine';
+  static const argHost = 'host';
+  static const argPort = 'port';
+  static const argProfileMemory = 'record-memory-profile';
+  static const argTryPorts = 'try-ports';
+  static const argVerbose = 'verbose';
+  static const argVersion = 'version';
+  static const launchDevToolsService = 'launchDevTools';
 
   MachineModeCommandHandler? _machineModeCommandHandler;
   late ClientManager clientManager;
   final bool _isChromeOS = File('/dev/.cros_milestone').existsSync();
 
+  static ArgParser buildArgParser({bool verbose = false}) {
+    final argParser = ArgParser();
+    argParser
+      ..addFlag(
+        argVersion,
+        negatable: false,
+        help: 'Prints the DevTools version.',
+      )
+      ..addFlag(
+        argVerbose,
+        negatable: false,
+        abbr: 'v',
+        help: 'Output more informational messages.',
+      )
+      ..addOption(
+        argHost,
+        valueHelp: 'host',
+        help: 'Hostname to serve DevTools on (defaults to localhost).',
+      )
+      ..addOption(
+        argPort,
+        defaultsTo: '9100',
+        valueHelp: 'port',
+        help: 'Port to serve DevTools on; specify 0 to automatically use any '
+            'available port.',
+      )
+      ..addFlag(
+        argLaunchBrowser,
+        help:
+            'Launches DevTools in a browser immediately at start.\n(defaults to on unless in --machine mode)',
+      )
+      ..addFlag(
+        argMachine,
+        negatable: false,
+        help: 'Sets output format to JSON for consumption in tools.',
+      )
+      ..addSeparator('Memory profiling options:')
+      ..addOption(
+        argProfileMemory,
+        valueHelp: 'file',
+        defaultsTo: 'memory_samples.json',
+        help:
+            'Start devtools headlessly and write memory profiling samples to the '
+            'indicated file.',
+      );
+
+    if (verbose) {
+      argParser.addSeparator('App size options:');
+    }
+
+    // TODO(devoncarew): --appSizeBase and --appSizeTest should be renamed to
+    // something like --app-size-base and --app-size-test; #3146.
+    argParser
+      ..addOption(
+        argAppSizeBase,
+        valueHelp: 'appSizeBase',
+        help: 'Path to the base app size file used for app size debugging.',
+        hide: !verbose,
+      )
+      ..addOption(
+        argAppSizeTest,
+        valueHelp: 'appSizeTest',
+        help:
+            'Path to the test app size file used for app size debugging.\nThis '
+            'file should only be specified if --$argAppSizeBase is also specified.',
+        hide: !verbose,
+      );
+
+    if (verbose) {
+      argParser.addSeparator('Advanced options:');
+    }
+
+    // Args to show for verbose mode.
+    argParser
+      ..addOption(
+        argTryPorts,
+        defaultsTo: DevToolsServer.defaultTryPorts.toString(),
+        valueHelp: 'count',
+        help: 'The number of ascending ports to try binding to before failing '
+            'with an error. ',
+        hide: !verbose,
+      )
+      ..addFlag(
+        argEnableNotifications,
+        negatable: false,
+        help: 'Requests notification permissions immediately when a client '
+            'connects back to the server.',
+        hide: !verbose,
+      )
+      ..addFlag(
+        argAllowEmbedding,
+        help: 'Allow embedding DevTools inside an iframe.',
+        hide: !verbose,
+      )
+      ..addFlag(
+        argHeadlessMode,
+        negatable: false,
+        help: 'Causes the server to spawn Chrome in headless mode for use in '
+            'automated testing.',
+        hide: !verbose,
+      );
+
+    // Deprecated and hidden args.
+    // TODO: Remove this - prefer that clients use the rest arg.
+    argParser
+      ..addOption(
+        argVmUri,
+        defaultsTo: '',
+        help: 'VM Service protocol URI.',
+        hide: true,
+      )
+
+      // Development only args.
+      ..addFlag(
+        argDebugMode,
+        negatable: false,
+        help: 'Run a debug build of the DevTools web frontend.',
+        hide: true,
+      );
+
+    return argParser;
+  }
+
   /// Serves DevTools.
   ///
   /// `handler` is the [shelf.Handler] that the server will use for all requests.
@@ -199,6 +344,131 @@
     return server;
   }
 
+  void _printUsage(ArgParser argParser) {
+    print(commandDescription);
+    print('\nUsage: devtools [arguments] [service protocol uri]');
+    print(argParser.usage);
+  }
+
+  /// Wraps [serveDevTools] `arguments` parsed, as from the command line.
+  ///
+  /// For more information on `handler`, see [serveDevTools].
+  // Note: this method is used in google3 as well as by DevTools' main method.
+  Future<HttpServer?> serveDevToolsWithArgs(
+    List<String> arguments, {
+    shelf.Handler? handler,
+    String? customDevToolsPath,
+  }) async {
+    ArgResults args;
+    final verbose = arguments.contains('-v') || arguments.contains('--verbose');
+    final argParser = buildArgParser(verbose: verbose);
+    try {
+      args = argParser.parse(arguments);
+    } on FormatException catch (e) {
+      print(e.message);
+      print('');
+      _printUsage(argParser);
+      return null;
+    }
+
+    return await _serveDevToolsWithArgs(
+      args,
+      verbose,
+      handler: handler,
+      customDevToolsPath: customDevToolsPath,
+    );
+  }
+
+  Future<HttpServer?> _serveDevToolsWithArgs(
+    ArgResults args,
+    bool verbose, {
+    shelf.Handler? handler,
+    String? customDevToolsPath,
+  }) async {
+    final help = args[argHelp];
+    final bool version = args[argVersion];
+    final bool machineMode = args[argMachine];
+    // launchBrowser defaults based on machine-mode if not explicitly supplied.
+    final bool launchBrowser = args.wasParsed(argLaunchBrowser)
+        ? args[argLaunchBrowser]
+        : !machineMode;
+    final bool enableNotifications = args[argEnableNotifications];
+    final bool allowEmbedding =
+        args.wasParsed(argAllowEmbedding) ? args[argAllowEmbedding] : true;
+
+    final port = args[argPort] != null ? int.tryParse(args[argPort]) ?? 0 : 0;
+
+    final bool headlessMode = args[argHeadlessMode];
+    final bool debugMode = args[argDebugMode];
+
+    final numPortsToTry = args[argTryPorts] != null
+        ? int.tryParse(args[argTryPorts]) ?? 0
+        : defaultTryPorts;
+
+    final bool verboseMode = args[argVerbose];
+    final String? hostname = args[argHost];
+    final String? appSizeBase = args[argAppSizeBase];
+    final String? appSizeTest = args[argAppSizeTest];
+
+    if (help) {
+      print(
+          'Dart DevTools version ${await DevToolsUtils.getVersion(customDevToolsPath ?? "")}');
+      print('');
+      _printUsage(buildArgParser(verbose: verbose));
+      return null;
+    }
+
+    if (version) {
+      final versionStr =
+          await DevToolsUtils.getVersion(customDevToolsPath ?? '');
+      DevToolsUtils.printOutput(
+        'Dart DevTools version $versionStr',
+        {
+          'version': versionStr,
+        },
+        machineMode: machineMode,
+      );
+      return null;
+    }
+
+    // Prefer getting the VM URI from the rest args; fall back on the 'vm-url'
+    // option otherwise.
+    String? serviceProtocolUri;
+    if (args.rest.isNotEmpty) {
+      serviceProtocolUri = args.rest.first;
+    } else if (args.wasParsed(argVmUri)) {
+      serviceProtocolUri = args[argVmUri];
+    }
+
+    // Support collecting profile data.
+    String? profileFilename;
+    if (args.wasParsed(argProfileMemory)) {
+      profileFilename = args[argProfileMemory];
+    }
+    if (profileFilename != null && !path.isAbsolute(profileFilename)) {
+      profileFilename = path.absolute(profileFilename);
+    }
+
+    return serveDevTools(
+      machineMode: machineMode,
+      debugMode: debugMode,
+      launchBrowser: launchBrowser,
+      enableNotifications: enableNotifications,
+      allowEmbedding: allowEmbedding,
+      port: port,
+      headlessMode: headlessMode,
+      numPortsToTry: numPortsToTry,
+      handler: handler,
+      customDevToolsPath: customDevToolsPath,
+      serviceProtocolUri: serviceProtocolUri,
+      profileFilename: profileFilename,
+      verboseMode: verboseMode,
+      hostname: hostname,
+      appSizeBase: appSizeBase,
+      appSizeTest: appSizeTest,
+    );
+  }
+
   Future<Map<String, dynamic>> launchDevTools(
       Map<String, dynamic> params,
       Uri vmServiceUri,
diff --git a/pkg/dds/pubspec.yaml b/pkg/dds/pubspec.yaml
index 22e251c..cb3837e 100644
--- a/pkg/dds/pubspec.yaml
+++ b/pkg/dds/pubspec.yaml
@@ -11,6 +11,7 @@
   sdk: '>=2.13.0 <3.0.0'
 
 dependencies:
+  args: ^2.0.0
   async: ^2.4.1
   browser_launcher: ^1.0.0
   collection: ^1.15.0
@@ -32,7 +33,6 @@
   web_socket_channel: ^2.0.0
 
 dev_dependencies:
-  args: ^2.0.0
   http: ^0.13.0
   test: ^1.0.0
   webdriver: ^3.0.0
diff --git a/tools/VERSION b/tools/VERSION
index 3987f2f..3153f2c 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 17
 PATCH 0
-PRERELEASE 67
+PRERELEASE 68
 PRERELEASE_PATCH 0
\ No newline at end of file