blob: 20b8972ea86c77d85654904da85c11c2a872b746 [file] [log] [blame]
// Copyright (c) 2020, 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:math';
import 'package:_fe_analyzer_shared/src/scanner/token.dart';
import 'package:analysis_server/src/services/correction/assist.dart';
import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer_plugin/utilities/assist/assist.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
class AddNotNullAssert extends CorrectionProducer {
@override
AssistKind get assistKind => DartAssistKind.ADD_NOT_NULL_ASSERT;
@override
Future<void> compute(ChangeBuilder builder) async {
final identifier = node;
if (identifier is SimpleIdentifier) {
if (identifier.parent is FormalParameter) {
final exp = identifier.parent.thisOrAncestorMatching(
(node) => node is FunctionExpression || node is MethodDeclaration);
FunctionBody body;
if (exp is FunctionExpression) {
body = exp.body;
} else if (exp is MethodDeclaration) {
body = exp.body;
}
if (body is BlockFunctionBody) {
var blockBody = body;
// Check for an obvious pre-existing assertion.
for (var statement in blockBody.block.statements) {
if (statement is AssertStatement) {
final condition = statement.condition;
if (condition is BinaryExpression) {
final leftOperand = condition.leftOperand;
if (leftOperand is SimpleIdentifier) {
if (leftOperand.staticElement == identifier.staticElement &&
condition.operator.type == TokenType.BANG_EQ &&
condition.rightOperand is NullLiteral) {
return;
}
}
}
}
}
await builder.addDartFileEdit(file, (builder) {
final id = identifier.name;
final prefix = utils.getNodePrefix(exp);
final indent = utils.getIndent(1);
// todo (pq): follow-ups:
// 1. if the end token is on the same line as the body
// we should add an `eol` before the assert as well.
// 2. also, consider asking the block for the list of statements and
// adding the statement to the beginning of the list, special casing
// when there are no statements (or when there's a single statement
// and the whole block is on the same line).
var offset = min(utils.getLineNext(blockBody.beginToken.offset),
blockBody.endToken.offset);
builder.addSimpleInsertion(
offset, '$prefix${indent}assert($id != null);$eol');
});
}
}
}
}
/// Return an instance of this class. Used as a tear-off in `AssistProcessor`.
static AddNotNullAssert newInstance() => AddNotNullAssert();
}