mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 00:09:49 +00:00
58392c017a
Change-Id: Iac4401d92f21fc1edd49e3abd9fc6c5474337cae Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/250769 Reviewed-by: Nate Bosch <nbosch@google.com> Commit-Queue: Devon Carew <devoncarew@google.com>
238 lines
7.5 KiB
Dart
238 lines
7.5 KiB
Dart
// Copyright (c) 2017, 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' as math;
|
|
|
|
import 'package:status_file/canonical_status_file.dart';
|
|
|
|
class LintingError {
|
|
final int lineNumber;
|
|
final String message;
|
|
LintingError(this.lineNumber, this.message);
|
|
|
|
@override
|
|
String toString() {
|
|
return "Error at line $lineNumber: $message";
|
|
}
|
|
}
|
|
|
|
/// Main function to check a status file for linting errors.
|
|
List<LintingError> lint(StatusFile file, {checkForDisjunctions = false}) {
|
|
var errors = <LintingError>[];
|
|
for (var section in file.sections) {
|
|
errors
|
|
..addAll(lintCommentLinesInSection(section))
|
|
..addAll(lintAlphabeticalOrderingOfPaths(section))
|
|
..addAll(lintNormalizedSection(section))
|
|
..addAll(lintSectionEntryDuplicates(section));
|
|
if (checkForDisjunctions) {
|
|
errors.addAll(lintDisjunctionsInHeader(section));
|
|
}
|
|
}
|
|
errors.addAll(lintSectionHeaderOrdering(file.sections));
|
|
errors.addAll(lintSectionHeaderDuplicates(file.sections));
|
|
return errors;
|
|
}
|
|
|
|
/// Checks for invalid comment lines in a section.
|
|
///
|
|
/// We do not allow the following:
|
|
///
|
|
/// [ ... ]
|
|
///
|
|
/// vm/test: Skip # description
|
|
/// # Some comment <-- invalid
|
|
/// ...
|
|
///
|
|
/// This function checks for such invalid comments.
|
|
Iterable<LintingError> lintCommentLinesInSection(StatusSection section) {
|
|
if (section.lineNumber == -1) {
|
|
// This is the default section, which also has the dart copyright notice.
|
|
// Allow comment entries in the beginning of the file, until the first
|
|
// status entry.
|
|
var seenStatusEntry = false;
|
|
var lintingErrors = <LintingError>[];
|
|
for (var entry in section.entries) {
|
|
seenStatusEntry = seenStatusEntry || entry is StatusEntry;
|
|
if (seenStatusEntry && entry is CommentEntry) {
|
|
lintingErrors.add(
|
|
LintingError(entry.lineNumber, "Comment is on a line by itself."));
|
|
}
|
|
}
|
|
return lintingErrors;
|
|
}
|
|
return section.entries.whereType<CommentEntry>().map((entry) =>
|
|
LintingError(entry.lineNumber, "Comment is on a line by itself."));
|
|
}
|
|
|
|
/// Checks for disjunctions in headers. Disjunctions should be separated out.
|
|
///
|
|
/// Example:
|
|
/// [ $mode == debug || $mode == release ]
|
|
///
|
|
/// should not be allowed. The clauses should be refactored into own sections:
|
|
/// [ $mode == debug ]
|
|
/// ...
|
|
///
|
|
///
|
|
/// [ $mode == release ]
|
|
/// ...
|
|
///
|
|
/// Removing disjunctions will turn some sections into two or more sections with
|
|
/// the same status entries, but these will be much easier to process with our
|
|
/// tools.
|
|
Iterable<LintingError> lintDisjunctionsInHeader(StatusSection section) {
|
|
if (section.condition.toString().contains("||")) {
|
|
return [
|
|
LintingError(
|
|
section.lineNumber,
|
|
"Expression contains '||'. Please split the expression into multiple "
|
|
"separate sections.")
|
|
];
|
|
}
|
|
return [];
|
|
}
|
|
|
|
/// Checks for correct ordering of test entries in sections. They should be
|
|
/// ordered alphabetically.
|
|
Iterable<LintingError> lintAlphabeticalOrderingOfPaths(StatusSection section) {
|
|
var entries = section.entries
|
|
.whereType<StatusEntry>()
|
|
.map((entry) => entry.path)
|
|
.toList();
|
|
var sortedList = entries.toList()..sort((a, b) => a.compareTo(b));
|
|
var witness = _findNotEqualWitness<String>(sortedList, entries);
|
|
if (witness != null) {
|
|
return [
|
|
LintingError(
|
|
section.lineNumber,
|
|
"Test paths are not alphabetically ordered in section. "
|
|
"${witness.first} should come before ${witness.second}.")
|
|
];
|
|
}
|
|
return [];
|
|
}
|
|
|
|
/// Checks that each section expression have been normalized.
|
|
Iterable<LintingError> lintNormalizedSection(StatusSection section) {
|
|
var nonNormalized = section.condition.toString();
|
|
var normalized = section.condition.normalize().toString();
|
|
if (nonNormalized != normalized) {
|
|
return [
|
|
LintingError(
|
|
section.lineNumber,
|
|
"Condition expression should be '$normalized' "
|
|
"but was '$nonNormalized'.")
|
|
];
|
|
}
|
|
return const [];
|
|
}
|
|
|
|
/// Checks for duplicate section entries in the body of a section.
|
|
Iterable<LintingError> lintSectionEntryDuplicates(StatusSection section) {
|
|
var errors = <LintingError>[];
|
|
List<StatusEntry> statusEntries =
|
|
section.entries.whereType<StatusEntry>().toList();
|
|
for (var i = 0; i < statusEntries.length; i++) {
|
|
var entry = statusEntries[i];
|
|
for (var j = i + 1; j < statusEntries.length; j++) {
|
|
var otherEntry = statusEntries[j];
|
|
if (entry.path == otherEntry.path &&
|
|
_findNotEqualWitness(entry.expectations, otherEntry.expectations) ==
|
|
null) {
|
|
errors.add(LintingError(
|
|
section.lineNumber,
|
|
"The status entry "
|
|
"'$entry' is duplicated on lines "
|
|
"${entry.lineNumber} and ${otherEntry.lineNumber}."));
|
|
}
|
|
}
|
|
}
|
|
return errors;
|
|
}
|
|
|
|
/// Checks for incorrect ordering of section headers. Section headers should be
|
|
/// alphabetically ordered, except, when negation is used, it should be
|
|
/// lexicographically close to the none-negated one, but still come after.
|
|
///
|
|
/// [ $compiler == dart2js ] < [ $strong ]
|
|
/// [ $mode == debug ] < [ $mode != debug ]
|
|
/// [ $strong ] < [ ! $strong ]
|
|
///
|
|
/// A larger example could be the following:
|
|
///
|
|
/// [ $mode != debug ]
|
|
/// [ !strong ]
|
|
/// [ $mode == debug ]
|
|
/// [ strong ]
|
|
/// [ $compiler == dart2js ]
|
|
///
|
|
/// which should should become:
|
|
///
|
|
/// [ $compiler == dart2js ]
|
|
/// [ $mode == debug ]
|
|
/// [ $mode != debug ]
|
|
/// [ strong ]
|
|
/// [ !strong ]
|
|
///
|
|
Iterable<LintingError> lintSectionHeaderOrdering(List<StatusSection> sections) {
|
|
var unsorted = sections.where((section) => section.lineNumber != -1).toList();
|
|
var sorted = unsorted.toList()
|
|
..sort((a, b) => a.condition.compareTo(b.condition));
|
|
var witness = _findNotEqualWitness<StatusSection>(sorted, unsorted);
|
|
if (witness != null) {
|
|
return [
|
|
LintingError(
|
|
witness.second!.lineNumber,
|
|
"Section expressions are not correctly ordered in file. "
|
|
"'${witness.first!.condition}' on line ${witness.first!.lineNumber} "
|
|
"should come before '${witness.second!.condition}' at line "
|
|
"${witness.second!.lineNumber}.")
|
|
];
|
|
}
|
|
return [];
|
|
}
|
|
|
|
/// Checks for duplicate section headers.
|
|
Iterable<LintingError> lintSectionHeaderDuplicates(
|
|
List<StatusSection> sections) {
|
|
var errors = <LintingError>[];
|
|
var sorted = sections.toList()
|
|
..sort((a, b) => a.condition.compareTo(b.condition));
|
|
for (var i = 1; i < sorted.length; i++) {
|
|
var section = sorted[i];
|
|
var previousSection = sorted[i - 1];
|
|
if (section.condition.compareTo(previousSection.condition) == 0) {
|
|
errors.add(LintingError(
|
|
section.lineNumber,
|
|
"The condition "
|
|
"'${section.condition}' is duplicated on lines "
|
|
"${previousSection.lineNumber} and ${section.lineNumber}."));
|
|
}
|
|
}
|
|
return errors;
|
|
}
|
|
|
|
ListNotEqualWitness<T>? _findNotEqualWitness<T>(List<T> first, List<T> second) {
|
|
if (first.isEmpty && second.isEmpty) {
|
|
return null;
|
|
}
|
|
for (var i = 0; i < math.max(first.length, second.length); i++) {
|
|
if (i >= second.length) {
|
|
return ListNotEqualWitness(first[i], null);
|
|
} else if (i >= first.length) {
|
|
return ListNotEqualWitness(null, second[i]);
|
|
} else if (first[i] != second[i]) {
|
|
return ListNotEqualWitness(first[i], second[i]);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
class ListNotEqualWitness<T> {
|
|
final T? first;
|
|
final T? second;
|
|
ListNotEqualWitness(this.first, this.second);
|
|
}
|