Version 2.18.0-48.0.dev

Merge commit '1f27a7dca9ce8f5064a7aece22bbe31458da3695' into 'dev'
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/add_call_super.dart b/pkg/analysis_server/lib/src/services/correction/dart/add_call_super.dart
new file mode 100644
index 0000000..8b6fb50
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/dart/add_call_super.dart
@@ -0,0 +1,98 @@
+// Copyright (c) 2022, 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/dart/abstract_producer.dart';
+import 'package:analysis_server/src/services/correction/fix.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
+import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
+import 'package:analyzer_plugin/utilities/range_factory.dart';
+import 'package:collection/collection.dart';
+
+class AddCallSuper extends CorrectionProducer {
+  var _addition = '';
+
+  @override
+  // Adding as the first statement is not predictably the correct action.
+  bool get canBeAppliedInBulk => false;
+
+  @override
+  // Adding as the first statement is not predictably the correct action.
+  bool get canBeAppliedToFile => false;
+
+  @override
+  List<Object> get fixArguments => [_addition];
+
+  @override
+  FixKind get fixKind => DartFixKind.ADD_CALL_SUPER;
+
+  @override
+  Future<void> compute(ChangeBuilder builder) async {
+    var node = this.node;
+    if (node is! SimpleIdentifier) return;
+    var methodDeclaration = node.thisOrAncestorOfType<MethodDeclaration>();
+    if (methodDeclaration == null) return;
+    var classElement = methodDeclaration
+        .thisOrAncestorOfType<ClassDeclaration>()
+        ?.declaredElement;
+    if (classElement == null) return;
+
+    var name = Name(classElement.library.source.uri, node.name);
+    var overridden = InheritanceManager3().getInherited2(classElement, name);
+    if (overridden == null) return;
+    var overriddenParameters = overridden.parameters.map((p) => p.name);
+
+    var body = methodDeclaration.body;
+    var parameters = methodDeclaration.parameters?.parameters;
+    var argumentList = parameters
+            ?.map((p) {
+              var name = p.identifier?.name;
+              if (overriddenParameters.contains(name)) {
+                return p.isNamed ? '$name: $name' : name;
+              }
+              return null;
+            })
+            .whereNotNull()
+            .join(', ') ??
+        '';
+
+    _addition = '${node.name}($argumentList)';
+
+    if (body is BlockFunctionBody) {
+      await _block(builder, body);
+    } else if (body is ExpressionFunctionBody) {
+      await _expression(builder, body);
+    }
+  }
+
+  Future<void> _block(ChangeBuilder builder, BlockFunctionBody body) async {
+    var location = utils.prepareNewStatementLocation(body.block, true);
+
+    await builder.addDartFileEdit(file, (builder) {
+      builder.addInsertion(location.offset, (builder) {
+        builder.write(location.prefix);
+        builder.write('super.$_addition;');
+        builder.write(location.suffix);
+      });
+    });
+  }
+
+  Future<void> _expression(
+      ChangeBuilder builder, ExpressionFunctionBody body) async {
+    var expression = body.expression;
+    var semicolon = body.semicolon;
+    var prefix = utils.getLinePrefix(expression.offset);
+    var prefixWithLine = eol + prefix + utils.getIndent(1);
+
+    await builder.addDartFileEdit(file, (builder) {
+      builder.addSimpleReplacement(
+          range.startStart(body.functionDefinition, expression),
+          '{${prefixWithLine}super.$_addition;${prefixWithLine}return ');
+
+      builder.addSimpleReplacement(
+          range.endEnd(expression, semicolon ?? expression), ';$eol$prefix}');
+    });
+  }
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/create_constructor_for_final_fields.dart b/pkg/analysis_server/lib/src/services/correction/dart/create_constructor_for_final_fields.dart
index 90f757c..98d9df5 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/create_constructor_for_final_fields.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/create_constructor_for_final_fields.dart
@@ -86,7 +86,7 @@
 
   Future<void> _withoutSuperParameters(
       ChangeBuilder builder,
-      ClassMemberLocation targetLocation,
+      InsertionLocation targetLocation,
       String className,
       ClassElement keyClass,
       List<VariableDeclarationList> variableLists) async {
@@ -117,7 +117,7 @@
 
   Future<void> _withSuperParameters(
       ChangeBuilder builder,
-      ClassMemberLocation targetLocation,
+      InsertionLocation targetLocation,
       String className,
       List<VariableDeclarationList> variableLists) async {
     await builder.addDartFileEdit(file, (builder) {
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/create_constructor_super.dart b/pkg/analysis_server/lib/src/services/correction/dart/create_constructor_super.dart
index 2c94494..5c5eccb 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/create_constructor_super.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/create_constructor_super.dart
@@ -47,7 +47,7 @@
   final ConstructorElement _constructor;
 
   /// An indication of where the new constructor should be added.
-  final ClassMemberLocation _targetLocation;
+  final InsertionLocation _targetLocation;
 
   /// The name of the class in which the constructor will be added.
   final String _targetClassName;
diff --git a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
index ae9517c..4c4210b 100644
--- a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
+++ b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
@@ -1340,8 +1340,7 @@
 HintCode.MUST_BE_IMMUTABLE:
   status: needsEvaluation
 HintCode.MUST_CALL_SUPER:
-  status: needsFix
-  issue: https://github.com/dart-lang/sdk/issues/33985
+  status: hasFix
 HintCode.NON_CONST_CALL_TO_LITERAL_CONSTRUCTOR:
   status: needsEvaluation
 HintCode.NON_CONST_CALL_TO_LITERAL_CONSTRUCTOR_USING_NEW:
diff --git a/pkg/analysis_server/lib/src/services/correction/fix.dart b/pkg/analysis_server/lib/src/services/correction/fix.dart
index cd161c2..9a729ee 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix.dart
@@ -93,6 +93,11 @@
     DartFixKindPriority.DEFAULT,
     'Add cast',
   );
+  static const ADD_CALL_SUPER = FixKind(
+    'dart.fix.add.callSuper',
+    DartFixKindPriority.DEFAULT,
+    "Add 'super.{0}'",
+  );
   static const ADD_CONST = FixKind(
     'dart.fix.add.const',
     DartFixKindPriority.DEFAULT,
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index ed60cb1..dcb96bf 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -8,6 +8,7 @@
 import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
 import 'package:analysis_server/src/services/correction/dart/add_async.dart';
 import 'package:analysis_server/src/services/correction/dart/add_await.dart';
+import 'package:analysis_server/src/services/correction/dart/add_call_super.dart';
 import 'package:analysis_server/src/services/correction/dart/add_const.dart';
 import 'package:analysis_server/src/services/correction/dart/add_diagnostic_property_reference.dart';
 import 'package:analysis_server/src/services/correction/dart/add_enum_constant.dart';
@@ -1234,6 +1235,9 @@
     HintCode.MISSING_RETURN: [
       AddAsync.missingReturn,
     ],
+    HintCode.MUST_CALL_SUPER: [
+      AddCallSuper.new,
+    ],
     HintCode.NULLABLE_TYPE_IN_CATCH_CLAUSE: [
       RemoveQuestionMark.new,
     ],
diff --git a/pkg/analysis_server/lib/src/services/correction/util.dart b/pkg/analysis_server/lib/src/services/correction/util.dart
index fd87a5d..be9d2c0 100644
--- a/pkg/analysis_server/lib/src/services/correction/util.dart
+++ b/pkg/analysis_server/lib/src/services/correction/util.dart
@@ -524,15 +524,6 @@
   CancelCorrectionException({this.exception});
 }
 
-/// Describes the location for a newly created [ClassMember].
-class ClassMemberLocation {
-  final String prefix;
-  final int offset;
-  final String suffix;
-
-  ClassMemberLocation(this.prefix, this.offset, this.suffix);
-}
-
 class CorrectionUtils {
   final CompilationUnit unit;
   final LibraryElement _library;
@@ -979,7 +970,7 @@
     return TokenUtils.getTokens(trimmedText, unit.featureSet).isEmpty;
   }
 
-  ClassMemberLocation newCaseClauseAtEndLocation(SwitchStatement statement) {
+  InsertionLocation newCaseClauseAtEndLocation(SwitchStatement statement) {
     var blockStartLine = getLineThis(statement.leftBracket.offset);
     var blockEndLine = getLineThis(statement.end);
     var offset = blockEndLine;
@@ -991,10 +982,10 @@
       offset = statement.leftBracket.end;
       suffix = getLinePrefix(statement.offset);
     }
-    return ClassMemberLocation(prefix, offset, suffix);
+    return InsertionLocation(prefix, offset, suffix);
   }
 
-  ClassMemberLocation? prepareEnumNewConstructorLocation(
+  InsertionLocation? prepareEnumNewConstructorLocation(
     EnumDeclaration enumDeclaration,
   ) {
     var indent = getIndent(1);
@@ -1003,7 +994,7 @@
         .where((e) => e is FieldDeclaration || e is ConstructorDeclaration)
         .lastOrNull;
     if (targetMember != null) {
-      return ClassMemberLocation(
+      return InsertionLocation(
         endOfLine + endOfLine + indent,
         targetMember.end,
         '',
@@ -1012,7 +1003,7 @@
 
     var semicolon = enumDeclaration.semicolon;
     if (semicolon != null) {
-      return ClassMemberLocation(
+      return InsertionLocation(
         endOfLine + endOfLine + indent,
         semicolon.end,
         '',
@@ -1020,14 +1011,14 @@
     }
 
     var lastConstant = enumDeclaration.constants.last;
-    return ClassMemberLocation(
+    return InsertionLocation(
       ';$endOfLine$endOfLine$indent',
       lastConstant.end,
       '',
     );
   }
 
-  ClassMemberLocation? prepareNewClassMemberLocation(
+  InsertionLocation? prepareNewClassMemberLocation(
       CompilationUnitMember declaration,
       bool Function(ClassMember existingMember) shouldSkip) {
     var indent = getIndent(1);
@@ -1046,18 +1037,18 @@
     }
     // After the last target member.
     if (targetMember != null) {
-      return ClassMemberLocation(
+      return InsertionLocation(
           endOfLine + endOfLine + indent, targetMember.end, '');
     }
     // At the beginning of the class.
     var suffix = members.isNotEmpty || isClassWithEmptyBody(declaration)
         ? endOfLine
         : '';
-    return ClassMemberLocation(
+    return InsertionLocation(
         endOfLine + indent, _getLeftBracket(declaration)!.end, suffix);
   }
 
-  ClassMemberLocation? prepareNewConstructorLocation(
+  InsertionLocation? prepareNewConstructorLocation(
       AnalysisSession session, ClassDeclaration classDeclaration) {
     final sortConstructorsFirst = session.analysisContext.analysisOptions
         .isLintEnabled(LintNames.sort_constructors_first);
@@ -1070,13 +1061,13 @@
     return prepareNewClassMemberLocation(classDeclaration, shouldSkip);
   }
 
-  ClassMemberLocation? prepareNewFieldLocation(
+  InsertionLocation? prepareNewFieldLocation(
       CompilationUnitMember declaration) {
     return prepareNewClassMemberLocation(
         declaration, (member) => member is FieldDeclaration);
   }
 
-  ClassMemberLocation? prepareNewGetterLocation(
+  InsertionLocation? prepareNewGetterLocation(
       CompilationUnitMember declaration) {
     return prepareNewClassMemberLocation(
         declaration,
@@ -1086,7 +1077,7 @@
             member is MethodDeclaration && member.isGetter);
   }
 
-  ClassMemberLocation? prepareNewMethodLocation(
+  InsertionLocation? prepareNewMethodLocation(
       CompilationUnitMember declaration) {
     return prepareNewClassMemberLocation(
         declaration,
@@ -1096,6 +1087,30 @@
             member is MethodDeclaration);
   }
 
+  /// Return the location of a new statement in the given [block], as the
+  /// first statement if [first] is `true`, or the last one if `false`.
+  InsertionLocation prepareNewStatementLocation(Block block, bool first) {
+    var statements = block.statements;
+    var empty = statements.isEmpty;
+    var last = empty || first ? block.leftBracket : statements.last;
+
+    var linePrefix = getLinePrefix(last.offset);
+    var indent = getIndent(1);
+    String prefix;
+    String suffix;
+    if (empty) {
+      prefix = endOfLine + linePrefix + indent;
+      suffix = endOfLine + linePrefix;
+    } else if (first) {
+      prefix = endOfLine + linePrefix + indent;
+      suffix = '';
+    } else {
+      prefix = endOfLine + linePrefix;
+      suffix = '';
+    }
+    return InsertionLocation(prefix, last.end, suffix);
+  }
+
   /// Returns the source with indentation changed from [oldIndent] to
   /// [newIndent], keeping indentation of lines relative to each other.
   String replaceSourceIndent(
@@ -1381,6 +1396,14 @@
   String suffix = '';
 }
 
+class InsertionLocation {
+  final String prefix;
+  final int offset;
+  final String suffix;
+
+  InsertionLocation(this.prefix, this.offset, this.suffix);
+}
+
 /// Utilities to work with [Token]s.
 class TokenUtils {
   static List<Token> getNodeTokens(AstNode node) {
diff --git a/pkg/analysis_server/test/src/services/correction/fix/add_call_super_test.dart b/pkg/analysis_server/test/src/services/correction/fix/add_call_super_test.dart
new file mode 100644
index 0000000..af4ed50
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/add_call_super_test.dart
@@ -0,0 +1,276 @@
+// Copyright (c) 2022, 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.dart';
+import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'fix_processor.dart';
+
+void main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(AddCallSuperTest);
+  });
+}
+
+@reflectiveTest
+class AddCallSuperTest extends FixProcessorTest {
+  @override
+  FixKind get kind => DartFixKind.ADD_CALL_SUPER;
+
+  @override
+  void setUp() {
+    super.setUp();
+    writeTestPackageConfig(meta: true);
+  }
+
+  Future<void> test_body() async {
+    await resolveTestCode('''
+import 'package:meta/meta.dart';
+
+class A {
+  @mustCallSuper
+  void a() {}
+}
+class B extends A {
+  @override
+  void a() {}
+}
+''');
+    await assertHasFix('''
+import 'package:meta/meta.dart';
+
+class A {
+  @mustCallSuper
+  void a() {}
+}
+class B extends A {
+  @override
+  void a() {
+    super.a();
+  }
+}
+''', matchFixMessage: "Add 'super.a()'");
+  }
+
+  Future<void> test_body_added_parameters() async {
+    await resolveTestCode('''
+import 'package:meta/meta.dart';
+
+class A {
+  @mustCallSuper
+  void m(int x) {}
+}
+class B extends A {
+  @override
+  void m(int x, [int? y]) {}
+}
+''');
+    await assertHasFix('''
+import 'package:meta/meta.dart';
+
+class A {
+  @mustCallSuper
+  void m(int x) {}
+}
+class B extends A {
+  @override
+  void m(int x, [int? y]) {
+    super.m(x);
+  }
+}
+''', matchFixMessage: "Add 'super.m(x)'");
+  }
+
+  Future<void> test_body_optional() async {
+    await resolveTestCode('''
+import 'package:meta/meta.dart';
+
+class A {
+  @mustCallSuper
+  int a(int x, {int y = 0}) {
+    return x = y;
+  }
+}
+class B extends A {
+  @override
+  int a(int x, {int y = 0}) {
+    return x = y;
+  }
+}
+''');
+    await assertHasFix('''
+import 'package:meta/meta.dart';
+
+class A {
+  @mustCallSuper
+  int a(int x, {int y = 0}) {
+    return x = y;
+  }
+}
+class B extends A {
+  @override
+  int a(int x, {int y = 0}) {
+    super.a(x, y: y);
+    return x = y;
+  }
+}
+''', matchFixMessage: "Add 'super.a(x, y: y)'");
+  }
+
+  Future<void> test_body_parameters() async {
+    await resolveTestCode('''
+import 'package:meta/meta.dart';
+
+class A {
+  @mustCallSuper
+  void a(int i) {}
+}
+class B extends A {
+  @override
+  void a(int i) {}
+}
+''');
+    await assertHasFix('''
+import 'package:meta/meta.dart';
+
+class A {
+  @mustCallSuper
+  void a(int i) {}
+}
+class B extends A {
+  @override
+  void a(int i) {
+    super.a(i);
+  }
+}
+''', matchFixMessage: "Add 'super.a(i)'");
+  }
+
+  Future<void> test_body_required() async {
+    await resolveTestCode('''
+import 'package:meta/meta.dart';
+
+class A {
+  @mustCallSuper
+  int a(int x, {required int y}) {
+    return x = y;
+  }
+}
+class B extends A {
+  @override
+  int a(int x, {required int y}) {
+    return x = y;
+  }
+}
+''');
+    await assertHasFix('''
+import 'package:meta/meta.dart';
+
+class A {
+  @mustCallSuper
+  int a(int x, {required int y}) {
+    return x = y;
+  }
+}
+class B extends A {
+  @override
+  int a(int x, {required int y}) {
+    super.a(x, y: y);
+    return x = y;
+  }
+}
+''', matchFixMessage: "Add 'super.a(x, y: y)'");
+  }
+
+  Future<void> test_expression_async() async {
+    await resolveTestCode('''
+import 'package:meta/meta.dart';
+
+class A {
+  @mustCallSuper
+  Future<int> m() async => 3;
+}
+class B extends A {
+  @override
+  Future<int> m() async => 3;
+}
+''');
+    await assertHasFix('''
+import 'package:meta/meta.dart';
+
+class A {
+  @mustCallSuper
+  Future<int> m() async => 3;
+}
+class B extends A {
+  @override
+  Future<int> m() async {
+    super.m();
+    return 3;
+  }
+}
+''', matchFixMessage: "Add 'super.m()'");
+  }
+
+  Future<void> test_expression_parameters() async {
+    await resolveTestCode('''
+import 'package:meta/meta.dart';
+
+class A {
+  @mustCallSuper
+  int a(int x, int y) => x + y;
+}
+class B extends A {
+  @override
+  int a(int x, int y) => x + y;
+}
+''');
+    await assertHasFix('''
+import 'package:meta/meta.dart';
+
+class A {
+  @mustCallSuper
+  int a(int x, int y) => x + y;
+}
+class B extends A {
+  @override
+  int a(int x, int y) {
+    super.a(x, y);
+    return x + y;
+  }
+}
+''', matchFixMessage: "Add 'super.a(x, y)'");
+  }
+
+  Future<void> test_expression_positional() async {
+    await resolveTestCode('''
+import 'package:meta/meta.dart';
+
+class A {
+  @mustCallSuper
+  int a(int x, [int y = 0]) => x + y;
+}
+class B extends A {
+  @override
+  int a(int x, [int y = 0]) => x + y;
+}
+''');
+    await assertHasFix('''
+import 'package:meta/meta.dart';
+
+class A {
+  @mustCallSuper
+  int a(int x, [int y = 0]) => x + y;
+}
+class B extends A {
+  @override
+  int a(int x, [int y = 0]) {
+    super.a(x, y);
+    return x + y;
+  }
+}
+''', matchFixMessage: "Add 'super.a(x, y)'");
+  }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/test_all.dart b/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
index efbc6aa..d383acf 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/test_all.dart
@@ -6,6 +6,7 @@
 
 import 'add_async_test.dart' as add_async;
 import 'add_await_test.dart' as add_await;
+import 'add_call_super_test.dart' as add_call_super;
 import 'add_const_test.dart' as add_const;
 import 'add_curly_braces_test.dart' as add_curly_braces;
 import 'add_diagnostic_property_reference_test.dart'
@@ -237,6 +238,7 @@
   defineReflectiveSuite(() {
     add_async.main();
     add_await.main();
+    add_call_super.main();
     add_const.main();
     add_curly_braces.main();
     add_diagnostic_property_reference.main();
diff --git a/pkg/analysis_server/tool/code_completion/completion_metrics.dart b/pkg/analysis_server/tool/code_completion/completion_metrics.dart
index f2d73e1..ade5fdc 100644
--- a/pkg/analysis_server/tool/code_completion/completion_metrics.dart
+++ b/pkg/analysis_server/tool/code_completion/completion_metrics.dart
@@ -743,7 +743,9 @@
     }
   }
 
-  int forEachExpectedCompletion(
+  /// Gathers various metrics for the completion [request] which resulted in
+  /// [suggestions], with [expectedCompletion] as the expected completion.
+  int gatherMetricsForSuggestions(
       DartCompletionRequest request,
       MetricsSuggestionListener listener,
       ExpectedCompletion expectedCompletion,
@@ -1349,6 +1351,9 @@
       }
     }
 
+    // Note that some routes sort suggestions before responding differently.
+    // The Cider and legacy handlers use [fuzzyFilterSort], which does not match
+    // [completionComparator].
     suggestions.sort(completionComparator);
     return suggestions;
   }
@@ -1479,7 +1484,7 @@
           );
           stopwatch.stop();
 
-          return forEachExpectedCompletion(
+          return gatherMetricsForSuggestions(
               request,
               listener,
               expectedCompletion,
diff --git a/tools/VERSION b/tools/VERSION
index b523152..9680b88 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 18
 PATCH 0
-PRERELEASE 47
+PRERELEASE 48
 PRERELEASE_PATCH 0
\ No newline at end of file