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