Skip to content

Commit

Permalink
Add fix for MUST_CALL_SUPER
Browse files Browse the repository at this point in the history
Fixes #33985

Change-Id: I5ff66bc01dde162979a21ebe8374398c2ffea783
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/240846
Reviewed-by: Brian Wilkerson <[email protected]>
Commit-Queue: Brian Wilkerson <[email protected]>
  • Loading branch information
asashour authored and Commit Bot committed Apr 23, 2022
1 parent c94b26e commit 1f27a7d
Show file tree
Hide file tree
Showing 9 changed files with 434 additions and 27 deletions.
Original file line number Diff line number Diff line change
@@ -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}');
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ class CreateConstructorForFinalFields extends CorrectionProducer {

Future<void> _withoutSuperParameters(
ChangeBuilder builder,
ClassMemberLocation targetLocation,
InsertionLocation targetLocation,
String className,
ClassElement keyClass,
List<VariableDeclarationList> variableLists) async {
Expand Down Expand Up @@ -117,7 +117,7 @@ class CreateConstructorForFinalFields extends CorrectionProducer {

Future<void> _withSuperParameters(
ChangeBuilder builder,
ClassMemberLocation targetLocation,
InsertionLocation targetLocation,
String className,
List<VariableDeclarationList> variableLists) async {
await builder.addDartFileEdit(file, (builder) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class _CreateConstructor extends CorrectionProducer {
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1340,8 +1340,7 @@ HintCode.MIXIN_ON_SEALED_CLASS:
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:
Expand Down
5 changes: 5 additions & 0 deletions pkg/analysis_server/lib/src/services/correction/fix.dart
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ class DartFixKind {
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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:analysis_server/src/services/correction/base_processor.dart';
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';
Expand Down Expand Up @@ -1234,6 +1235,9 @@ class FixProcessor extends BaseProcessor {
HintCode.MISSING_RETURN: [
AddAsync.missingReturn,
],
HintCode.MUST_CALL_SUPER: [
AddCallSuper.new,
],
HintCode.NULLABLE_TYPE_IN_CATCH_CLAUSE: [
RemoveQuestionMark.new,
],
Expand Down
67 changes: 45 additions & 22 deletions pkg/analysis_server/lib/src/services/correction/util.dart
Original file line number Diff line number Diff line change
Expand Up @@ -524,15 +524,6 @@ class CancelCorrectionException {
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;
Expand Down Expand Up @@ -979,7 +970,7 @@ class CorrectionUtils {
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;
Expand All @@ -991,10 +982,10 @@ class CorrectionUtils {
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);
Expand All @@ -1003,7 +994,7 @@ class CorrectionUtils {
.where((e) => e is FieldDeclaration || e is ConstructorDeclaration)
.lastOrNull;
if (targetMember != null) {
return ClassMemberLocation(
return InsertionLocation(
endOfLine + endOfLine + indent,
targetMember.end,
'',
Expand All @@ -1012,22 +1003,22 @@ class CorrectionUtils {

var semicolon = enumDeclaration.semicolon;
if (semicolon != null) {
return ClassMemberLocation(
return InsertionLocation(
endOfLine + endOfLine + indent,
semicolon.end,
'',
);
}

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);
Expand All @@ -1046,18 +1037,18 @@ class CorrectionUtils {
}
// 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);
Expand All @@ -1070,13 +1061,13 @@ class CorrectionUtils {
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,
Expand All @@ -1086,7 +1077,7 @@ class CorrectionUtils {
member is MethodDeclaration && member.isGetter);
}

ClassMemberLocation? prepareNewMethodLocation(
InsertionLocation? prepareNewMethodLocation(
CompilationUnitMember declaration) {
return prepareNewClassMemberLocation(
declaration,
Expand All @@ -1096,6 +1087,30 @@ class CorrectionUtils {
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(
Expand Down Expand Up @@ -1381,6 +1396,14 @@ class CorrectionUtils_InsertDesc {
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) {
Expand Down
Loading

0 comments on commit 1f27a7d

Please sign in to comment.