Version 2.15.0-174.0.dev

Merge commit '7fb8c7afea0e4d9efc89bfcfe7006c9753e08ef2' into 'dev'
diff --git a/pkg/analysis_server/benchmark/benchmarks.dart b/pkg/analysis_server/benchmark/benchmarks.dart
index 28c658d..a92b60e 100644
--- a/pkg/analysis_server/benchmark/benchmarks.dart
+++ b/pkg/analysis_server/benchmark/benchmarks.dart
@@ -15,6 +15,7 @@
 
 import 'perf/benchmarks_impl.dart';
 import 'perf/flutter_analyze_benchmark.dart';
+import 'perf/flutter_completion_benchmark.dart';
 
 Future main(List<String> args) async {
   var benchmarks = <Benchmark>[
@@ -23,6 +24,8 @@
     AnalysisBenchmark(ServerBenchmark.das),
     AnalysisBenchmark(ServerBenchmark.lsp),
     FlutterAnalyzeBenchmark(),
+    FlutterCompletionBenchmark.das,
+    FlutterCompletionBenchmark.lsp,
   ];
 
   var runner = CommandRunner(
diff --git a/pkg/analysis_server/benchmark/perf/flutter_completion_benchmark.dart b/pkg/analysis_server/benchmark/perf/flutter_completion_benchmark.dart
new file mode 100644
index 0000000..1784e96
--- /dev/null
+++ b/pkg/analysis_server/benchmark/perf/flutter_completion_benchmark.dart
@@ -0,0 +1,317 @@
+// Copyright (c) 2021, 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 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:path/path.dart' as path;
+
+import '../benchmarks.dart';
+import 'memory_tests.dart';
+
+Future<int> _runProcess(
+  String command,
+  List<String> args, {
+  String? cwd,
+  bool failOnError = true,
+}) async {
+  print('\n$command ${args.join(' ')}');
+
+  var process = await Process.start(command, args, workingDirectory: cwd);
+
+  process.stdout
+      .transform(utf8.decoder)
+      .transform(LineSplitter())
+      .listen((line) {
+    print('  $line');
+  });
+  process.stderr
+      .transform(utf8.decoder)
+      .transform(LineSplitter())
+      .listen((line) => print('  $line'));
+
+  var exitCode = await process.exitCode;
+  if (exitCode != 0 && failOnError) {
+    throw '$command exited with $exitCode';
+  }
+
+  return exitCode;
+}
+
+/// benchmarks:
+///   - analysis-server-warm-analysis
+///   - analysis-server-warm-memory
+///   - analysis-server-edit
+///   - analysis-server-completion
+class FlutterCompletionBenchmark extends Benchmark {
+  static final das = FlutterCompletionBenchmark(
+    'das',
+    () => AnalysisServerBenchmarkTest(),
+  );
+
+  static final lsp = FlutterCompletionBenchmark(
+    'lsp',
+    () => LspAnalysisServerBenchmarkTest(),
+  );
+
+  final AbstractBenchmarkTest Function() testConstructor;
+
+  late String flutterPath;
+
+  FlutterCompletionBenchmark(String protocolName, this.testConstructor)
+      : super(
+          '$protocolName-flutter-completion',
+          'Completion benchmarks with Flutter.',
+          kind: 'group',
+        );
+
+  @override
+  bool get needsSetup => true;
+
+  @override
+  Future oneTimeSetup() async {
+    flutterPath = Directory.systemTemp.createTempSync('flutter').path;
+
+    // git clone https://github.com/flutter/flutter $flutterDir
+    await _runProcess('git', [
+      'clone',
+      'https://github.com/flutter/flutter',
+      path.canonicalize(flutterPath)
+    ]);
+
+    var flutterTool = path.join(flutterPath, 'bin', 'flutter');
+
+    // flutter --version
+    await _runProcess(flutterTool, ['--version'], cwd: flutterPath);
+
+    // flutter update-packages
+    await _runProcess(
+      flutterTool,
+      ['pub', 'get'],
+      cwd: path.join(flutterPath, 'packages', 'flutter'),
+    );
+  }
+
+  @override
+  Future<BenchMarkResult> run({
+    bool quick = false,
+    bool verbose = false,
+  }) async {
+    if (!quick) {
+      deleteServerCache();
+    }
+
+    var test = testConstructor();
+    if (verbose) {
+      test.debugStdio();
+    }
+
+    final flutterPkgPath = path.join(flutterPath, 'packages', 'flutter');
+
+    // Open a small directory, but with the package config that allows us
+    // to analyze any file in `package:flutter`, including tests.
+    var startTimer = Stopwatch()..start();
+    await test.setUp([
+      '$flutterPkgPath/lib/src/physics',
+    ]);
+
+    await test.analysisFinished;
+    startTimer.stop();
+
+    var result = CompoundBenchMarkResult(id);
+    result.add(
+      'start',
+      BenchMarkResult(
+        'micros',
+        startTimer.elapsedMicroseconds,
+      ),
+    );
+
+    // This is a scenario of an easy case - the file is small, less than
+    // 3KB, and we insert a prefix with a good selectivity. So, everything
+    // should be fast. We should just make sure to don't spend too much
+    // time analyzing, and do apply the filter.
+    // Total number of suggestions: 2322.
+    // Filtered to: 82.
+    result.add(
+      'smallFile-body',
+      BenchMarkResult(
+        'micros',
+        await _completionTiming(
+          test,
+          filePath: '$flutterPkgPath/lib/src/material/flutter_logo.dart',
+          uniquePrefix: 'Widget build(BuildContext context) {',
+          insertStringGenerator: () => 'M',
+          name: 'smallFile-body',
+        ),
+      ),
+    );
+
+    if (!quick) {
+      // This scenario is relatively easy - the file is small, less then 3KB.
+      // But we don't have any prefix to filter, so if we don't restrict the
+      // number of suggestions, we might spend too much time serializing into
+      // JSON in the server, and deserializing on the client.
+      // Total number of suggestions: 2322.
+      // Filtered to: 2322.
+      result.add(
+        'smallFile-body-withoutPrefix',
+        BenchMarkResult(
+          'micros',
+          await _completionTiming(
+            test,
+            filePath: '$flutterPkgPath/lib/src/material/flutter_logo.dart',
+            uniquePrefix: 'Widget build(BuildContext context) {',
+            insertStringGenerator: null,
+            name: 'smallFile-body-withoutPrefix',
+          ),
+        ),
+      );
+
+      // The key part of this scenario is that we work in a relatively large
+      // file, about 340KB. So, it is expensive to parse and resolve. And
+      // we simulate changing it by typing a prefix, as users often do.
+      // The target method body is small, so something could be optimized.
+      // Total number of suggestions: 4654.
+      // Filtered to: 182.
+      result.add(
+        'smallLibraryCycle-largeFile-smallBody',
+        BenchMarkResult(
+          'micros',
+          await _completionTiming(
+            test,
+            filePath: '$flutterPkgPath/test/material/text_field_test.dart',
+            uniquePrefix: 'getOpacity(WidgetTester tester, Finder finder) {',
+            insertStringGenerator: () => 'M',
+            name: 'smallLibraryCycle-largeFile-smallBody',
+          ),
+        ),
+      );
+
+      // In this scenario we change a file that is in a library cycle
+      // with 69 libraries. So, the implementation might discard information
+      // about all these libraries. We change a method body, so the API
+      // signature is the same, and we are able to reload these libraries
+      // from bytes. But this still costs something.
+      // There is also a spill-over from the previous test - we send a lot
+      // (about 5MB) of available declarations after each change. This makes
+      // completion response times very large.
+      // TODO(scheglov) Remove the previous sentence when improved.
+      // Total number of suggestions: 3429.
+      // Filtered to: 133.
+      result.add(
+        'mediumLibraryCycle-mediumFile-smallBody',
+        BenchMarkResult(
+          'micros',
+          await _completionTiming(
+            test,
+            filePath: '$flutterPkgPath/lib/src/material/app_bar.dart',
+            uniquePrefix: 'computeDryLayout(BoxConstraints constraints) {',
+            insertStringGenerator: () => 'M',
+            name: 'mediumLibraryCycle-mediumFile-smallBody',
+          ),
+        ),
+      );
+
+      // In this scenario is that we change a file that is in a library cycle
+      // with 69 libraries. Moreover, we change the API - the type of a
+      // formal parameter. So, potentially we need to relink the whole library
+      // cycle. This is expensive.
+      // Total number of suggestions: 1510.
+      // Filtered to: 0.
+      result.add(
+        'mediumLibraryCycle-mediumFile-api-parameterType',
+        BenchMarkResult(
+          'micros',
+          await _completionTiming(
+            test,
+            filePath: '$flutterPkgPath/lib/src/material/app_bar.dart',
+            uniquePrefix: 'computeDryLayout(BoxConstraints',
+            insertStringGenerator: _IncrementingStringGenerator(),
+            name: 'mediumLibraryCycle-mediumFile-api-parameterType',
+          ),
+        ),
+      );
+    }
+
+    await test.shutdown();
+
+    return result;
+  }
+
+  /// Perform completion in [filePath] at the end of the [uniquePrefix].
+  ///
+  /// If [insertStringGenerator] is not `null`, insert it, and complete after
+  /// it. So, we can simulate user typing to start completion.
+  Future<int> _completionTiming(
+    AbstractBenchmarkTest test, {
+    required String filePath,
+    required String uniquePrefix,
+    required String Function()? insertStringGenerator,
+    String? name,
+  }) async {
+    final fileContent = File(filePath).readAsStringSync();
+
+    final prefixOffset = fileContent.indexOf(uniquePrefix);
+    if (prefixOffset == -1) {
+      throw StateError('Cannot find: $uniquePrefix');
+    }
+    if (fileContent.contains(uniquePrefix, prefixOffset + 1)) {
+      throw StateError('Not unique: $uniquePrefix');
+    }
+
+    final prefixEnd = prefixOffset + uniquePrefix.length;
+
+    await test.openFile(filePath, fileContent);
+
+    Future<void> perform() async {
+      var completionOffset = prefixEnd;
+
+      if (insertStringGenerator != null) {
+        final insertString = insertStringGenerator();
+        completionOffset += insertString.length;
+        var newCode = fileContent.substring(0, prefixEnd) +
+            insertString +
+            fileContent.substring(prefixEnd);
+        await test.updateFile(
+          filePath,
+          newCode,
+        );
+      }
+
+      await test.complete(filePath, completionOffset);
+
+      if (insertStringGenerator != null) {
+        await test.updateFile(filePath, fileContent);
+      }
+    }
+
+    // Perform warm-up.
+    // The cold start does not matter.
+    // The sustained performance is much more important.
+    const kWarmUpCount = 50;
+    for (var i = 0; i < kWarmUpCount; i++) {
+      await perform();
+    }
+
+    const kRepeatCount = 10;
+    final timer = Stopwatch()..start();
+    for (var i = 0; i < kRepeatCount; i++) {
+      await perform();
+    }
+
+    await test.closeFile(filePath);
+
+    return timer.elapsedMicroseconds ~/ kRepeatCount;
+  }
+}
+
+class _IncrementingStringGenerator {
+  int _value = 0;
+
+  String call() {
+    return '${_value++}';
+  }
+}
diff --git a/pkg/analysis_server/benchmark/perf/memory_tests.dart b/pkg/analysis_server/benchmark/perf/memory_tests.dart
index b67b63c..528f672 100644
--- a/pkg/analysis_server/benchmark/perf/memory_tests.dart
+++ b/pkg/analysis_server/benchmark/perf/memory_tests.dart
@@ -79,6 +79,7 @@
   Future<void> setUp(List<String> roots) async {
     await _test.setUp();
     await _test.subscribeToStatusNotifications();
+    await _test.subscribeToAvailableSuggestions();
     await _test.sendAnalysisSetAnalysisRoots(roots, []);
   }
 
@@ -127,6 +128,16 @@
   /// After every test, the server is stopped.
   Future shutdown() async => await shutdownIfNeeded();
 
+  /// Enable using available suggestions during completion.
+  Future<void> subscribeToAvailableSuggestions() async {
+    await server.send(
+      'completion.setSubscriptions',
+      CompletionSetSubscriptionsParams(
+        [CompletionService.AVAILABLE_SUGGESTION_SETS],
+      ).toJson(),
+    );
+  }
+
   /// Enable [ServerService.STATUS] notifications so that [analysisFinished]
   /// can be used.
   Future subscribeToStatusNotifications() async {
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
index d2a5859..04235d9 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
@@ -100,11 +100,6 @@
 
   /// Compute analysis results for all units of the library.
   Map<FileState, UnitAnalysisResult> analyze() {
-    return analyzeSync();
-  }
-
-  /// Compute analysis results for all units of the library.
-  Map<FileState, UnitAnalysisResult> analyzeSync() {
     timerLibraryAnalyzer.start();
     Map<FileState, CompilationUnitImpl> units = {};
 
diff --git a/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart b/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart
index 2b2efd4..1fa7152 100644
--- a/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart
@@ -100,7 +100,7 @@
   TypeSystemImpl get _typeSystem => _libraryElement.typeSystem;
 
   /// Compute analysis results for all units of the library.
-  Map<FileState, UnitAnalysisResult> analyzeSync({
+  Map<FileState, UnitAnalysisResult> analyze({
     required String? completionPath,
     required int? completionOffset,
     required OperationPerformanceImpl performance,
diff --git a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
index d52742b..daf5dbd 100644
--- a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
+++ b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
@@ -526,7 +526,7 @@
 
         try {
           results = performance!.run('analyze', (performance) {
-            return libraryAnalyzer.analyzeSync(
+            return libraryAnalyzer.analyze(
               completionPath: completionOffset != null ? completionPath : null,
               completionOffset: completionOffset,
               performance: performance,
diff --git a/pkg/scrape/example/superclass_parameters.dart b/pkg/scrape/example/superclass_parameters.dart
new file mode 100644
index 0000000..de5923e
--- /dev/null
+++ b/pkg/scrape/example/superclass_parameters.dart
@@ -0,0 +1,317 @@
+// Copyright (c) 2021, 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:analyzer/dart/ast/ast.dart';
+import 'package:scrape/scrape.dart';
+
+enum ArgumentMatch {
+  noArguments,
+  none,
+  all,
+  some,
+  prefix,
+  suffix,
+  middle,
+  noncontiguous
+}
+
+extension on ArgumentMatch {
+  String get description {
+    switch (this) {
+      case ArgumentMatch.noArguments:
+        return 'No arguments to match';
+      case ArgumentMatch.none:
+        return 'Matched none';
+      case ArgumentMatch.all:
+        return 'Matched all';
+      case ArgumentMatch.some:
+        return 'Matched some';
+      case ArgumentMatch.prefix:
+        return 'Matched prefix';
+      case ArgumentMatch.suffix:
+        return 'Matched suffix';
+      case ArgumentMatch.middle:
+        return 'Matched middle';
+      case ArgumentMatch.noncontiguous:
+        return 'Matched noncontiguous';
+    }
+  }
+}
+
+void main(List<String> arguments) {
+  Scrape()
+    ..addHistogram('Potential use')
+    ..addHistogram('Individual arguments')
+    ..addHistogram('Named arguments')
+    ..addHistogram('Positional arguments')
+    ..addHistogram('Argument pattern')
+    ..addHistogram('Append super args')
+    ..addHistogram('Prepend super args')
+    ..addHistogram('Insert super args')
+    ..addHistogram('Do not merge super args')
+    ..addHistogram('No explicit super(), call unnamed')
+    ..addHistogram('No explicit super(), call same name')
+    ..addVisitor(() => SuperclassParameterVisitor())
+    ..runCommandLine(arguments);
+}
+
+class SuperclassParameterVisitor extends ScrapeVisitor {
+  @override
+  void visitConstructorDeclaration(ConstructorDeclaration node) {
+    // Whether the constructor might benefit from the feature at all.
+    var initializer = _findSuper(node);
+    if (initializer == null) {
+      record('Potential use', 'No: No initializer');
+      return;
+    }
+
+    if (initializer.argumentList.arguments.isEmpty) {
+      record('Potential use', 'No: Empty super() argument list');
+      return;
+    }
+
+    record('Potential use', 'Yes');
+
+    // If we get here, we have a superclass constructor call with arguments.
+    // See if any of them could use the feature.
+    var positionalParamNames = node.parameters.parameters
+        .where((param) => param.isPositional)
+        .map((param) => param.identifier!.name)
+        .toList();
+
+    var namedParamNames = node.parameters.parameters
+        .where((param) => param.isNamed)
+        .map((param) => param.identifier!.name)
+        .toSet();
+
+    var matchedNamedArguments = 0;
+    var unmatchedNamedArguments = 0;
+
+    var lastPositionalParam = -1;
+    var matchedIndexes = <int>[];
+    var unmatchedPositionalArguments = 0;
+    var positionalArgCount = 0;
+    for (var i = 0; i < initializer.argumentList.arguments.length; i++) {
+      var argument = initializer.argumentList.arguments[i];
+
+      if (argument is NamedExpression) {
+        var expression = argument.expression;
+        if (expression is! SimpleIdentifier) {
+          record('Individual arguments',
+              'Named argument expression is not identifier');
+          unmatchedNamedArguments++;
+        } else if (argument.name.label.name != expression.name) {
+          record('Individual arguments',
+              'Named argument name does not match expression name');
+          unmatchedNamedArguments++;
+        } else if (!namedParamNames.contains(expression.name)) {
+          record('Individual arguments',
+              'Named argument does not match a parameter');
+          unmatchedNamedArguments++;
+        } else {
+          record('Individual arguments', 'Argument matches a parameter');
+          matchedNamedArguments++;
+        }
+      } else {
+        positionalArgCount++;
+        if (argument is! SimpleIdentifier) {
+          record('Individual arguments',
+              'Positional argument expression is not identifier');
+          unmatchedPositionalArguments++;
+        } else {
+          // Start searching after the last matched positional parameter because
+          // we don't allow reordering them. If two arguments are out of order,
+          // that doesn't mean we can't use "super." at all, just that we can
+          // only use it for *one* of those arguments.
+          var index =
+              positionalParamNames.indexOf(argument.name, lastPositionalParam);
+          if (index == -1) {
+            record('Individual arguments',
+                'Positional argument does not match a parameter');
+          } else {
+            record('Individual arguments', 'Argument matches a parameter');
+            lastPositionalParam = index;
+            matchedIndexes.add(i);
+          }
+        }
+      }
+    }
+
+    // Characterize the positional argument list.
+    ArgumentMatch positionalMatch;
+    if (unmatchedPositionalArguments == 0) {
+      if (matchedIndexes.isEmpty) {
+        positionalMatch = ArgumentMatch.noArguments;
+      } else {
+        positionalMatch = ArgumentMatch.all;
+      }
+    } else if (matchedIndexes.isEmpty) {
+      positionalMatch = ArgumentMatch.none;
+    } else {
+      // If there is any unmatched argument before a matched one, then the
+      // matched arguments are not all at the beginning.
+      var matchedArePrefix = true;
+      for (var i = 1; i < positionalArgCount; i++) {
+        if (!matchedIndexes.contains(i - 1) && matchedIndexes.contains(i)) {
+          matchedArePrefix = false;
+          break;
+        }
+      }
+
+      // If there is any unmatched argument after a matched one, then the
+      // matched arguments are not all at the end.
+      var matchedAreSuffix = true;
+      for (var i = 0; i < positionalArgCount - 1; i++) {
+        if (!matchedIndexes.contains(i + 1) && matchedIndexes.contains(i)) {
+          matchedAreSuffix = false;
+          break;
+        }
+      }
+
+      // If any index between the first and last matched arg is not matched,
+      // then the arguments are not contiguous.
+      var matchedAreContiguous = true;
+      if (matchedIndexes.isNotEmpty) {
+        for (var i = matchedIndexes.first; i <= matchedIndexes.last; i++) {
+          if (!matchedIndexes.contains(i)) {
+            matchedAreContiguous = false;
+            break;
+          }
+        }
+      }
+
+      if (!matchedAreContiguous) {
+        positionalMatch = ArgumentMatch.noncontiguous;
+      } else if (matchedArePrefix) {
+        positionalMatch = ArgumentMatch.prefix;
+      } else if (matchedAreSuffix) {
+        positionalMatch = ArgumentMatch.suffix;
+      } else {
+        positionalMatch = ArgumentMatch.middle;
+      }
+    }
+
+    record('Positional arguments', positionalMatch.description);
+
+    // Characterize the named argument list.
+    ArgumentMatch namedMatch;
+    if (matchedNamedArguments == 0) {
+      if (unmatchedNamedArguments == 0) {
+        namedMatch = ArgumentMatch.noArguments;
+      } else {
+        namedMatch = ArgumentMatch.none;
+      }
+    } else {
+      if (unmatchedNamedArguments == 0) {
+        namedMatch = ArgumentMatch.all;
+      } else {
+        namedMatch = ArgumentMatch.some;
+      }
+    }
+
+    record('Named arguments', namedMatch.description);
+
+    var pattern = [
+      for (var i = 0; i < positionalArgCount; i++)
+        matchedIndexes.contains(i) ? 's' : '_',
+      for (var i = 0; i < matchedNamedArguments; i++) ':s',
+      for (var i = 0; i < unmatchedNamedArguments; i++) ':_',
+    ].join(',');
+    record('Argument pattern', '($pattern)');
+
+    // If none of the arguments could be 'super.', then none of the proposals
+    // apply.
+    if (matchedIndexes.isEmpty && matchedNamedArguments == 0) return;
+
+    var append = true;
+    var prepend = true;
+    var insert = true;
+    var noMerge = true;
+    var allParams = true;
+
+    switch (positionalMatch) {
+      case ArgumentMatch.noArguments:
+      case ArgumentMatch.all:
+        // OK.
+        break;
+
+      case ArgumentMatch.none:
+        allParams = false;
+        break;
+
+      case ArgumentMatch.some:
+        throw Exception('Should not get some for positional args.');
+
+      case ArgumentMatch.prefix:
+        append = false;
+        noMerge = false;
+        allParams = false;
+        break;
+
+      case ArgumentMatch.suffix:
+        prepend = false;
+        noMerge = false;
+        allParams = false;
+        break;
+
+      case ArgumentMatch.middle:
+        append = false;
+        prepend = false;
+        noMerge = false;
+        allParams = false;
+        break;
+
+      case ArgumentMatch.noncontiguous:
+        append = false;
+        prepend = false;
+        insert = false;
+        noMerge = false;
+        allParams = false;
+        break;
+    }
+
+    switch (namedMatch) {
+      case ArgumentMatch.noArguments:
+      case ArgumentMatch.all:
+        // OK.
+        break;
+
+      case ArgumentMatch.none:
+      case ArgumentMatch.some:
+        allParams = false;
+        break;
+
+      default:
+        throw Exception('Unexpected match.');
+    }
+
+    record('Append super args', append ? 'Yes' : 'No');
+    record('Prepend super args', prepend ? 'Yes' : 'No');
+    record('Insert super args', insert ? 'Yes' : 'No');
+    record('Do not merge super args', noMerge ? 'Yes' : 'No');
+
+    var subName = _constructorName(node.name);
+    var superName = _constructorName(initializer.constructorName);
+
+    record('No explicit super(), call same name',
+        (allParams && superName == subName) ? 'Yes' : 'No');
+
+    record('No explicit super(), call unnamed',
+        (allParams && superName == '(unnamed)') ? 'Yes' : 'No');
+  }
+
+  String _constructorName(SimpleIdentifier? name) {
+    if (name == null) return '(unnamed)';
+    return name.name;
+  }
+
+  SuperConstructorInvocation? _findSuper(ConstructorDeclaration node) {
+    for (var initializer in node.initializers) {
+      if (initializer is SuperConstructorInvocation) {
+        return initializer;
+      }
+    }
+
+    return null;
+  }
+}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/compile_protos.sh b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/compile_protos.sh
index 8031891..d4f6355 100755
--- a/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/compile_protos.sh
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/protobuf_handler/compile_protos.sh
@@ -16,4 +16,4 @@
 protoc --dart_out=$GENERATED_DIR -I$DIR/protos $DIR/protos/*.proto
 rm $GENERATED_DIR/*.pbenum.dart $GENERATED_DIR/*.pbjson.dart $GENERATED_DIR/*.pbserver.dart
 
-dartfmt -w $DIR/lib/generated
+dart format $DIR/lib/generated
diff --git a/pkg/vm_service/tool/generate.dart b/pkg/vm_service/tool/generate.dart
index b9ef51f..79d74eb 100644
--- a/pkg/vm_service/tool/generate.dart
+++ b/pkg/vm_service/tool/generate.dart
@@ -46,9 +46,9 @@
   dart.api.parse(nodes);
   dart.api.generate(generator);
   outputFile.writeAsStringSync(generator.toString());
-  ProcessResult result = Process.runSync('dartfmt', ['-w', outDirPath]);
+  ProcessResult result = Process.runSync('dart', ['format', outDirPath]);
   if (result.exitCode != 0) {
-    print('dartfmt: ${result.stdout}\n${result.stderr}');
+    print('dart format: ${result.stdout}\n${result.stderr}');
     throw result.exitCode;
   }
 
@@ -103,9 +103,9 @@
   dart.api.parse(nodes);
   dart.api.generateAsserts(generator);
   outputFile.writeAsStringSync(generator.toString());
-  ProcessResult result = Process.runSync('dartfmt', ['-w', outDirPath]);
+  ProcessResult result = Process.runSync('dart', ['format', outDirPath]);
   if (result.exitCode != 0) {
-    print('dartfmt: ${result.stdout}\n${result.stderr}');
+    print('dart format: ${result.stdout}\n${result.stderr}');
     throw result.exitCode;
   }
 
diff --git a/runtime/observatory/tool/ensure_dartfmt.sh b/runtime/observatory/tool/ensure_dartfmt.sh
index 6aa50e9..0579deb 100755
--- a/runtime/observatory/tool/ensure_dartfmt.sh
+++ b/runtime/observatory/tool/ensure_dartfmt.sh
@@ -3,13 +3,13 @@
 dart_files=$(find lib web -name "*.dart")
 [ -z "$dart_files" ] && exit 0
 
-unformatted=$(dartfmt -n $dart_files)
+unformatted=$(dart format -o none $dart_files)
 [ -z "$unformatted" ] && exit 0
 
-# Some files are not dartfmt'd. Print message and fail.
-echo >&2 "dart files must be formatted with dartfmt. Please run:"
+# Some files are not dart formatted. Print message and fail.
+echo >&2 "dart files must be formatted with dart format. Please run:"
 for fn in $unformatted; do
-  echo >&2 "  dartfmt -w $PWD/$fn"
+  echo >&2 "  dart format $PWD/$fn"
 done
 
 exit 1
diff --git a/runtime/observatory_2/tool/ensure_dartfmt.sh b/runtime/observatory_2/tool/ensure_dartfmt.sh
index 6aa50e9..0579deb 100755
--- a/runtime/observatory_2/tool/ensure_dartfmt.sh
+++ b/runtime/observatory_2/tool/ensure_dartfmt.sh
@@ -3,13 +3,13 @@
 dart_files=$(find lib web -name "*.dart")
 [ -z "$dart_files" ] && exit 0
 
-unformatted=$(dartfmt -n $dart_files)
+unformatted=$(dart format -o none $dart_files)
 [ -z "$unformatted" ] && exit 0
 
-# Some files are not dartfmt'd. Print message and fail.
-echo >&2 "dart files must be formatted with dartfmt. Please run:"
+# Some files are not dart formatted. Print message and fail.
+echo >&2 "dart files must be formatted with dart format. Please run:"
 for fn in $unformatted; do
-  echo >&2 "  dartfmt -w $PWD/$fn"
+  echo >&2 "  dart format $PWD/$fn"
 done
 
 exit 1
diff --git a/runtime/tests/concurrency/generate_stress_test.dart b/runtime/tests/concurrency/generate_stress_test.dart
index 3870ef2..a22bc34 100644
--- a/runtime/tests/concurrency/generate_stress_test.dart
+++ b/runtime/tests/concurrency/generate_stress_test.dart
@@ -17,7 +17,7 @@
 final Map testMap = json.decode(File(stressTestListJson).readAsStringSync());
 final testFiles = testMap['non-nnbd'].cast<String>();
 final testFilesNnbd = testMap['nnbd'].cast<String>();
-final dartfmt = 'tools/sdks/dart-sdk/bin/dartfmt';
+final dart = 'tools/sdks/dart-sdk/bin/dart';
 
 main(List<String> args) async {
   File(generatedNnbdTest)
@@ -188,7 +188,7 @@
 
 Future<String> format(String generatedSource) async {
   try {
-    final result = await Process.start(dartfmt, []);
+    final result = await Process.start(dart, ['format']);
     result.stdin.writeln(generatedSource);
 
     final results = await Future.wait([
@@ -200,13 +200,14 @@
 
     final exitCode = results[3] as int;
     if (exitCode != 0) {
-      print('Note: Failed to format source code. Dartfmt exited non-0.');
+      print('Note: Failed to format source code. Dart format exited non-0.');
       return generatedSource;
     }
     final stdout = results[1] as String;
     final stderr = results[2] as String;
     if (stderr.trim().length != 0) {
-      print('Note: Failed to format source code. Dartfmt had stderr: $stderr');
+      print('Note: Failed to format source code. Dart format had stderr: '
+          '$stderr');
       return generatedSource;
     }
     return stdout;
diff --git a/runtime/tools/dartfuzz/gen_type_table.dart b/runtime/tools/dartfuzz/gen_type_table.dart
index 0f2599a..6bd642e 100644
--- a/runtime/tools/dartfuzz/gen_type_table.dart
+++ b/runtime/tools/dartfuzz/gen_type_table.dart
@@ -8,7 +8,7 @@
 //   dart gen_type_table.dart > dartfuzz_type_table.dart
 //
 // Reformat:
-//   tools/sdks/dart-sdk/bin/dartfmt -w \
+//   tools/sdks/dart-sdk/bin/dart format \
 //   runtime/tools/dartfuzz/dartfuzz_type_table.dart
 //
 // Then send out modified dartfuzz_type_table.dart for review together
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler.cc b/runtime/vm/compiler/backend/flow_graph_compiler.cc
index 274a04f..9355b01 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler.cc
@@ -1621,8 +1621,15 @@
       blocked_registers[loc.reg()] = true;
     } else if (loc.IsFpuRegister()) {
       // Check that a register is not specified twice in the summary.
-      ASSERT(!blocked_fpu_registers[loc.fpu_reg()]);
-      blocked_fpu_registers[loc.fpu_reg()] = true;
+      const FpuRegister fpu_reg = loc.fpu_reg();
+      if ((fpu_reg < 0) || (fpu_reg >= kNumberOfFpuRegisters)) {
+        // Debug prints for https://github.com/dart-lang/sdk/issues/47314.
+        OS::PrintErr("input(%" Pd ") fpu_reg = %d\n", i, fpu_reg);
+        OS::PrintErr("instr = %s\n", instr->ToCString());
+        UNREACHABLE();
+      }
+      ASSERT(!blocked_fpu_registers[fpu_reg]);
+      blocked_fpu_registers[fpu_reg] = true;
     }
   }
 
@@ -1634,8 +1641,15 @@
       blocked_registers[loc.reg()] = true;
     } else if (loc.IsFpuRegister()) {
       // Check that a register is not specified twice in the summary.
-      ASSERT(!blocked_fpu_registers[loc.fpu_reg()]);
-      blocked_fpu_registers[loc.fpu_reg()] = true;
+      const FpuRegister fpu_reg = loc.fpu_reg();
+      if ((fpu_reg < 0) || (fpu_reg >= kNumberOfFpuRegisters)) {
+        // Debug prints for https://github.com/dart-lang/sdk/issues/47314.
+        OS::PrintErr("temp(%" Pd ") fpu_reg = %d\n", i, fpu_reg);
+        OS::PrintErr("instr = %s\n", instr->ToCString());
+        UNREACHABLE();
+      }
+      ASSERT(!blocked_fpu_registers[fpu_reg]);
+      blocked_fpu_registers[fpu_reg] = true;
     }
   }
 
diff --git a/tools/VERSION b/tools/VERSION
index bd4c19d..39d9d47 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 15
 PATCH 0
-PRERELEASE 173
+PRERELEASE 174
 PRERELEASE_PATCH 0
\ No newline at end of file