Skip to content

Commit

Permalink
Version 3.5.0-26.0.dev
Browse files Browse the repository at this point in the history
Merge df707ac into dev
  • Loading branch information
Dart CI committed Apr 5, 2024
2 parents c99c14a + df707ac commit 67ad2d6
Show file tree
Hide file tree
Showing 16 changed files with 226 additions and 222 deletions.
2 changes: 1 addition & 1 deletion DEPS
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ vars = {
"vector_math_rev": "7e705f734e94917e9a5347578e6e496f8db38ac6",
"watcher_rev": "1bd2f20d0d924c8422aa2b9afdb165bff4f053c0",
"web_rev": "e773de957b289d001c90c6b830e91634e305667d",
"web_socket_channel_rev": "19d82db86acb7309dd08c40a2af3285232751e83",
"web_socket_channel_rev": "ced3a37193f89d5ee95792f342eeb15d3d55d8c1",
"webdev_rev": "4067462c8d605266a23c3725948a0314102c95f9",
"webdriver_rev": "c80e01e6ce121e55c31e33a31e5d3950023e6bc9",
"webkit_inspection_protocol_rev": "153fea4fe5ac45bebf0c2e76bb3d76b0f1fcdaae",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,90 +4,18 @@

import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
import 'package:analysis_server/src/services/correction/fix.dart';
import 'package:analysis_server/src/services/correction/util.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/ignore_comments/ignore_info.dart';
import 'package:analyzer/src/workspace/blaze.dart';
import 'package:analyzer_plugin/src/utilities/extensions/resolved_unit_result.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
import 'package:yaml/yaml.dart';
import 'package:yaml_edit/yaml_edit.dart' show YamlEditor;

abstract class AbstractIgnoreDiagnostic extends ResolvedCorrectionProducer {
AnalysisError get error => diagnostic as AnalysisError;

@override
List<Object>? get fixArguments => [_code];

String get _code => error.errorCode.name.toLowerCase();

Future<void> _computeEdit(
ChangeBuilder builder,
InsertionLocation insertDesc,
RegExp existingIgnorePattern,
String ignoreCommentType,
) async {
final lineInfo = unit.lineInfo;

await builder.addDartFileEdit(file, (builder) {
final offset = insertDesc.offset;

// Work out the offset of the start of the line so we can insert the new
// line there.
final location = lineInfo.getLocation(offset);
final zeroBasedLineNumber = location.lineNumber - 1;
final lineOffset = lineInfo.getOffsetOfLine(zeroBasedLineNumber);

if (zeroBasedLineNumber > 0) {
final previousLineOffset =
lineInfo.getOffsetOfLine(zeroBasedLineNumber - 1);

// If the line above already has an ignore comment, we need to append to
// it like "ignore: foo, bar".
final previousLineText = utils
.getText(previousLineOffset, lineOffset - previousLineOffset)
.trimRight();
if (previousLineText.trim().startsWith(existingIgnorePattern)) {
final offset = previousLineOffset + previousLineText.length;
builder.addSimpleInsertion(offset, ', $_code');
return;
}
}

final indent = utils.getLinePrefix(offset);
final prefix = insertDesc.prefix;
final comment = '// $ignoreCommentType: $_code';
final suffix = insertDesc.suffix;
builder.addSimpleInsertion(
lineOffset, '$prefix$indent$comment$eol$suffix');
});
}

/// Returns `true` if any of the following is `true`:
/// - [error.code] is present in the `cannot-ignore` list.
/// - [error.code] is already ignored in the `errors` list.
bool _isCodeUnignorable() {
var cannotIgnore =
analysisOptions.unignorableNames.contains(error.errorCode.name);

if (cannotIgnore) {
return true;
}

// This will prevent showing a `fix` when the `error` is already ignored in
// `analysis_options.yaml`.
//
// Note: both `ignore` and `false` severity are set to `null` when parsed.
// See `ErrorConfig` in `pkg/analyzer/source/error_processor.dart`.
var explicitlyIgnored = analysisOptions.errorProcessors.any((element) =>
element.severity == null && element.code == error.errorCode.name);

return explicitlyIgnored;
}
}

class IgnoreDiagnosticInAnalysisOptionsFile extends AbstractIgnoreDiagnostic {
class IgnoreDiagnosticInAnalysisOptionsFile extends _BaseIgnoreDiagnostic {
@override
FixKind get fixKind => DartFixKind.IGNORE_ERROR_ANALYSIS_FILE;

Expand All @@ -101,7 +29,7 @@ class IgnoreDiagnosticInAnalysisOptionsFile extends AbstractIgnoreDiagnostic {
return;
}

if (_isCodeUnignorable()) return;
if (_isCodeUnignorable) return;

final analysisOptionsFile = analysisOptions.file;

Expand Down Expand Up @@ -144,12 +72,12 @@ class IgnoreDiagnosticInAnalysisOptionsFile extends AbstractIgnoreDiagnostic {
try {
editor.update(path, value);
} on YamlException {
// If the `analysis_options.yaml` does not have a valid format,
// a `YamlException` is thrown (e.g. a label without a value).
// In such case, do not suggest a fix.
// If the `analysis_options.yaml` does not have a valid format, a
// `YamlException` is thrown (e.g. a label without a value). In such
// case, do not suggest a fix.
//
// TODO(osaxma): check if the `analysis_options.yaml` is a valid
// before calling the builder to avoid unnecessary processing.
// TODO(osaxma): check if the `analysis_options.yaml` is a valid before
// calling the builder to avoid unnecessary processing.
return;
}

Expand All @@ -168,40 +96,155 @@ class IgnoreDiagnosticInAnalysisOptionsFile extends AbstractIgnoreDiagnostic {
}
}

class IgnoreDiagnosticInFile extends AbstractIgnoreDiagnostic {
class IgnoreDiagnosticInFile extends _DartIgnoreDiagnostic {
@override
String get commentPrefix => 'ignore_for_file';

@override
FixKind get fixKind => DartFixKind.IGNORE_ERROR_FILE;

@override
Future<void> compute(ChangeBuilder builder) async {
if (_isCodeUnignorable()) return;

final insertDesc = utils.getInsertionLocationIgnoreForFile();
await _computeEdit(
builder,
insertDesc,
IgnoreInfo.ignoreForFileMatcher,
'ignore_for_file',
);
if (_isCodeUnignorable) return;

await builder.addDartFileEdit(file, (builder) {
var source = unitResult.content;

// Look for the last blank line in any leading comments (to insert after
// all header comments but not after any "comment-attached" code). If an
// existing `ignore_for_file` comment is found while looking, then insert
// after that.

var lineCount = unitResult.lineInfo.lineCount;
if (lineCount == 1) {
insertAt(builder, 0, insertEmptyLineAfter: true);
return;
}

int? lastBlankLineOffset;
late int lineStart;
for (var lineNumber = 0; lineNumber < lineCount - 1; lineNumber++) {
lineStart = unitResult.lineInfo.getOffsetOfLine(lineNumber);
var nextLineStart = unitResult.lineInfo.getOffsetOfLine(lineNumber + 1);
var line = source.substring(lineStart, nextLineStart).trim();

if (line.startsWith('// $commentPrefix:')) {
// Found an existing ignore; insert at the end of this line.
builder.addSimpleInsertion(nextLineStart - eol.length, ', $_code');
return;
}

if (line.isEmpty) {
// Track last blank line, as we will insert there.
lastBlankLineOffset = lineStart;
continue;
}

if (line.startsWith('#!') || line.startsWith('//')) {
// Skip comment/hash-bang.
continue;
}

// We found some code.
break;
}

if (lastBlankLineOffset != null) {
// If we found a blank line, insert right after that.
insertAt(builder, lastBlankLineOffset, insertEmptyLineBefore: true);
} else {
// Otherwise, insert right before the first line of code.
insertAt(builder, lineStart, insertEmptyLineAfter: true);
}
});
}
}

class IgnoreDiagnosticOnLine extends AbstractIgnoreDiagnostic {
class IgnoreDiagnosticOnLine extends _DartIgnoreDiagnostic {
@override
String get commentPrefix => 'ignore';

@override
FixKind get fixKind => DartFixKind.IGNORE_ERROR_LINE;

@override
Future<void> compute(ChangeBuilder builder) async {
if (_isCodeUnignorable()) return;

final diagnostic = this.diagnostic!; // Enforced by _isCodeUnignorable
final insertDesc = InsertionLocation(
prefix: '', offset: diagnostic.problemMessage.offset, suffix: '');
await _computeEdit(
builder,
insertDesc,
IgnoreInfo.ignoreMatcher,
'ignore',
);
if (_isCodeUnignorable) return;

await builder.addDartFileEdit(file, (builder) {
var offset = error.problemMessage.offset;
var lineNumber = unitResult.lineInfo.getLocation(offset).lineNumber - 1;

if (lineNumber == 0) {
// The error is on the first line; no chance of a previous line already
// containing an ignore comment.
insertAt(builder, 0);
return;
}

var previousLineStart =
unitResult.lineInfo.getOffsetOfLine(lineNumber - 1);
var lineStart = unitResult.lineInfo.getOffsetOfLine(lineNumber);
var line =
unitResult.content.substring(previousLineStart, lineStart).trim();

if (line.startsWith(IgnoreInfo.ignoreMatcher)) {
builder.addSimpleInsertion(lineStart - eol.length, ', $_code');
} else {
insertAt(builder, lineStart);
}
});
}
}

abstract class _BaseIgnoreDiagnostic extends ResolvedCorrectionProducer {
AnalysisError get error => diagnostic as AnalysisError;

@override
List<Object>? get fixArguments => [_code];

String get _code => error.errorCode.name.toLowerCase();

/// Returns `true` if any of the following is `true`:
/// - `error.code` is present in the `cannot-ignore` list.
/// - `error.code` is already ignored in the `errors` list.
bool get _isCodeUnignorable {
var cannotIgnore =
analysisOptions.unignorableNames.contains(error.errorCode.name);

if (cannotIgnore) {
return true;
}

// This will prevent showing a `fix` when the `error` is already ignored in
// `analysis_options.yaml`.
//
// Note: both `ignore` and `false` severity are set to `null` when parsed.
// See `ErrorConfig` in `pkg/analyzer/source/error_processor.dart`.
return analysisOptions.errorProcessors.any((element) =>
element.severity == null && element.code == error.errorCode.name);
}
}

abstract class _DartIgnoreDiagnostic extends _BaseIgnoreDiagnostic {
/// The ignore-comment prefix (either 'ignore' or 'ignore_for_file').
String get commentPrefix;

/// Inserts a properly indented ignore-comment at [offset].
///
/// Additionally, [eol] is inserted before the comment if
/// [insertEmptyLineBefore], and [eol] is inserted after the comment if
/// [insertEmptyLineAfter].
void insertAt(
DartFileEditBuilder builder,
int offset, {
bool insertEmptyLineBefore = false,
bool insertEmptyLineAfter = false,
}) {
var prefix = insertEmptyLineBefore ? eol : '';
var indent = unitResult.linePrefix(offset);
final comment = '// $commentPrefix: $_code';
final suffix = insertEmptyLineAfter ? eol : '';
builder.addSimpleInsertion(offset, '$prefix$indent$comment$eol$suffix');
}
}
70 changes: 0 additions & 70 deletions pkg/analysis_server/lib/src/services/correction/util.dart
Original file line number Diff line number Diff line change
Expand Up @@ -339,20 +339,6 @@ String getLibrarySourceUri(
return what.toString();
}

/// Returns the line prefix from the given source, i.e. basically just a
/// whitespace prefix of the given [String].
String getLinePrefix(String line) {
var index = 0;
while (index < line.length) {
var c = line.codeUnitAt(index);
if (!isWhitespace(c)) {
break;
}
index++;
}
return line.substring(0, index);
}

/// Return the [LocalVariableElement] if given [node] is a reference to a local
/// variable, or `null` in the other case.
LocalVariableElement? getLocalVariableElement(SimpleIdentifier node) {
Expand Down Expand Up @@ -574,62 +560,6 @@ final class CorrectionUtils {
/// Returns the [AstNode] that encloses the given offset.
AstNode? findNode(int offset) => NodeLocator(offset).searchWithin(unit);

/// Returns a description of the place in which to insert an `ignore_for_file`
/// comment.
///
/// When an existing `ignore_for_file` comment is found, this returns the
/// start of the following line, although calling code may choose to fold
/// into the previous line.
InsertionLocation getInsertionLocationIgnoreForFile() {
var offset = 0;
var insertEmptyLineBefore = false;
var insertEmptyLineAfter = false;
var source = _buffer;

// Look for the last blank line in any leading comments (to insert after all
// header comments but not after any comment "attached" code). If an
// existing ignore_for_file comment is found while looking, then insert
// after that.

int? lastBlankLineOffset;
var insertOffset = 0;
while (offset < source.length - 1) {
var nextLineOffset = getLineNext(offset);
var line = source.substring(offset, nextLineOffset).trim();

if (line.startsWith('// ignore_for_file:')) {
// Found existing ignore, insert after this.
insertOffset = nextLineOffset;
break;
} else if (line.isEmpty) {
// Track last blank line, as we will insert there.
lastBlankLineOffset = offset;
offset = nextLineOffset;
} else if (line.startsWith('#!') || line.startsWith('//')) {
// Skip comment/hash-bang.
offset = nextLineOffset;
} else {
// We found some code.
// If we found a blank line, insert it after that.
if (lastBlankLineOffset != null) {
insertOffset = lastBlankLineOffset;
insertEmptyLineBefore = true;
} else {
// Otherwise, insert it before the first line of code.
insertOffset = offset;
insertEmptyLineAfter = true;
}
break;
}
}

return InsertionLocation(
prefix: insertEmptyLineBefore ? endOfLine : '',
offset: insertOffset,
suffix: insertEmptyLineAfter ? endOfLine : '',
);
}

/// Skips whitespace characters and single EOL on the right from [index].
///
/// If [index] the end of a statement or method, then in the most cases it is
Expand Down
Loading

0 comments on commit 67ad2d6

Please sign in to comment.