Migrate pkg/status_file to null safety.

Change-Id: Ic6627a64a3ee72a6f14aadf59ee87e21e64e3664
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/209781
Auto-Submit: Bob Nystrom <rnystrom@google.com>
Commit-Queue: Bob Nystrom <rnystrom@google.com>
Reviewed-by: William Hesse <whesse@google.com>
This commit is contained in:
Robert Nystrom 2021-08-12 20:33:30 +00:00 committed by commit-bot@chromium.org
parent c010c1d54f
commit 9e4cbbbe78
17 changed files with 174 additions and 166 deletions

View file

@ -11,7 +11,7 @@
"constraint, update this by running tools/generate_package_config.dart."
],
"configVersion": 2,
"generated": "2021-08-05T11:33:04.746536",
"generated": "2021-08-10T10:51:47.272341",
"generator": "tools/generate_package_config.dart",
"packages": [
{
@ -659,7 +659,7 @@
"name": "status_file",
"rootUri": "../pkg/status_file",
"packageUri": "lib/",
"languageVersion": "2.3"
"languageVersion": "2.12"
},
{
"name": "stream_channel",

View file

@ -56,10 +56,11 @@ void main(List<String> arguments) {
}
void lintStdIn({bool checkForDisjunctions = false}) {
List<String> strings = <String>[];
String readString;
var strings = <String>[];
try {
while (null != (readString = stdin.readLineSync())) {
while (true) {
var readString = stdin.readLineSync();
if (readString == null) break;
strings.add(readString);
}
} on StdinException {
@ -126,7 +127,7 @@ bool lintStatusFile(StatusFile statusFile,
print("");
return true;
}
if (statusFile.path != null && statusFile.path.isNotEmpty) {
if (statusFile.path.isNotEmpty) {
print("${statusFile.path}");
}
var errors = lintingErrors.toList();

View file

@ -33,8 +33,9 @@ import 'dart:io';
import 'package:args/args.dart';
import 'package:status_file/canonical_status_file.dart';
import 'package:status_file/expectation.dart';
import 'package:status_file/src/expression.dart';
StatusEntry filterExpectations(
StatusEntry? filterExpectations(
StatusEntry entry, List<Expectation> expectationsToKeep) {
List<Expectation> remaining = entry.expectations
.where(
@ -45,14 +46,14 @@ StatusEntry filterExpectations(
: StatusEntry(entry.path, entry.lineNumber, remaining, entry.comment);
}
Map<String, Map<int, String>> issues;
late Map<String, Map<int, String>> issues;
String getIssueState(String project, int issue) {
Map projectIssues = issues[project];
var projectIssues = issues[project];
if (projectIssues == null) {
throw "Cannot find project $project, not one of {${issues.keys.join(",")}}";
}
String state = projectIssues[issue] ?? "";
var state = projectIssues[issue] ?? "";
return "\t$state";
}
@ -61,7 +62,7 @@ String getIssueState(String project, int issue) {
// sorted by issue number then timestamp ascending.
//
// The first line is expected to contain the field names and is skipped.
void parseIssueFile() async {
Future<void> parseIssueFile() async {
issues = {};
String issuesLog = await File("issues.log").readAsString();
List<String> lines = issuesLog.split("\n");
@ -91,13 +92,13 @@ List<RegExp> sdkIssuePatterns = [
];
String getIssueText(String comment, bool resolveState) {
int issue;
String prefix;
String project;
for (RegExp pattern in co19IssuePatterns) {
Match match = pattern.firstMatch(comment);
int? issue;
late String prefix;
late String project;
for (var pattern in co19IssuePatterns) {
var match = pattern.firstMatch(comment);
if (match != null) {
issue = int.tryParse(match[1]);
issue = int.tryParse(match[1]!);
if (issue != null) {
prefix = "https://github.com/dart-lang/co19/issues/";
project = "dart-lang/co19";
@ -106,10 +107,10 @@ String getIssueText(String comment, bool resolveState) {
}
}
if (issue == null) {
for (RegExp pattern in sdkIssuePatterns) {
Match match = pattern.firstMatch(comment);
for (var pattern in sdkIssuePatterns) {
var match = pattern.firstMatch(comment);
if (match != null) {
issue = int.tryParse(match[1]);
issue = int.tryParse(match[1]!);
if (issue != null) {
prefix = "https://dartbug.com/";
project = "dart-lang/sdk";
@ -119,7 +120,7 @@ String getIssueText(String comment, bool resolveState) {
}
}
if (issue != null) {
String state = resolveState ? getIssueState(project, issue) : "";
var state = resolveState ? getIssueState(project, issue) : "";
return "$prefix$issue$state";
} else {
return "";
@ -143,7 +144,7 @@ Future<StatusFile> removeNonEssentialEntries(
entries.add(entry);
hasStatusEntries = true;
} else if (entry is StatusEntry) {
StatusEntry newEntry = entry;
StatusEntry? newEntry = entry;
if (entry.comment == null) {
newEntry = filterExpectations(entry, expectationsToKeep);
} else if (removeComments) {
@ -155,8 +156,9 @@ Future<StatusFile> removeNonEssentialEntries(
String expectations = entry.expectations.toString();
// Remove '[' and ']'.
expectations = expectations.substring(1, expectations.length - 1);
String conditionPrefix =
section.condition != null ? "${section.condition}" : "";
String conditionPrefix = section.condition != Expression.always
? "${section.condition}"
: "";
String issueText = await getIssueText(comment, resolveIssueState);
String statusLine = "$conditionPrefix\t$testName\t$expectations"
"\t$comment\t$issueText";
@ -171,16 +173,18 @@ Future<StatusFile> removeNonEssentialEntries(
throw "Unknown entry type ${entry.runtimeType}";
}
}
bool isDefaultSection = section.condition == null;
var isDefaultSection = section.condition == Expression.always;
if (hasStatusEntries ||
(isDefaultSection && section.sectionHeaderComments.isNotEmpty)) {
StatusSection newSection =
var newSection =
StatusSection(section.condition, -1, section.sectionHeaderComments);
newSection.entries.addAll(entries);
sections.add(newSection);
}
}
StatusFile newStatusFile = StatusFile(statusFile.path);
var newStatusFile = StatusFile(statusFile.path);
newStatusFile.sections.addAll(sections);
return newStatusFile;
}

View file

@ -95,7 +95,7 @@ class StatusFile {
/// Checks if [currentLine] is a comment and returns the first regular
/// expression match, or null otherwise.
Match commentEntryMatch(int currentLine) {
Match? commentEntryMatch(int currentLine) {
if (currentLine < 1 || currentLine > lines.length) {
return null;
}
@ -104,7 +104,7 @@ class StatusFile {
/// Finds a section header on [currentLine] if the line is in range of
/// [lines].
Match sectionHeaderMatch(int currentLine) {
Match? sectionHeaderMatch(int currentLine) {
if (currentLine < 1 || currentLine > lines.length) {
return null;
}
@ -178,14 +178,14 @@ class StatusFile {
// The current section whose rules are being parsed. Initialized to an
// implicit section that matches everything.
StatusSection section =
new StatusSection(null, -1, implicitSectionHeaderComments);
new StatusSection(Expression.always, -1, implicitSectionHeaderComments);
section.entries.addAll(entries);
sections.add(section);
for (; _lineCount <= lines.length; _lineCount++) {
var line = lines[_lineCount - 1];
fail(String message, [List<String> errors]) {
fail(String message, [List<String>? errors]) {
throw new SyntaxError(_shortPath, _lineCount, line, message, errors);
}
@ -199,7 +199,7 @@ class StatusFile {
var match = _sectionPattern.firstMatch(line);
if (match != null) {
try {
var condition = Expression.parse(match[1].trim());
var condition = Expression.parse(match[1]!.trim());
section =
new StatusSection(condition, _lineCount, sectionHeaderComments);
sections.add(section);
@ -214,10 +214,10 @@ class StatusFile {
// If it is in a new entry we should add to the current section.
match = _entryPattern.firstMatch(line);
if (match != null) {
var path = match[1].trim();
var path = match[1]!.trim();
var expectations = <Expectation>[];
// split expectations
match[2].split(",").forEach((name) {
match[2]!.split(",").forEach((name) {
try {
expectations.add(Expectation.find(name.trim()));
} on ArgumentError {
@ -229,7 +229,7 @@ class StatusFile {
.add(new StatusEntry(path, _lineCount, expectations, null));
} else {
section.entries.add(new StatusEntry(
path, _lineCount, expectations, new Comment(match[3])));
path, _lineCount, expectations, new Comment(match[3]!)));
}
continue;
}
@ -264,8 +264,6 @@ class StatusFile {
/// Throws a [SyntaxError] on the first found error.
void validate(Environment environment) {
for (var section in sections) {
if (section.condition == null) continue;
var errors = <String>[];
section.condition.validate(environment, errors);
@ -299,8 +297,8 @@ class StatusFile {
class StatusSection {
/// The expression that determines when this section is applied.
///
/// May be `null` for paths that appear before any section header in the file.
/// In that case, the section always applies.
/// Will be [Expression.always] for paths that appear before any section
/// header in the file. In that case, the section always applies.
final Expression condition;
/// The one-based line number where the section appears in the file.
@ -311,8 +309,7 @@ class StatusSection {
final List<Entry> sectionHeaderComments;
/// Returns true if this section should apply in the given [environment].
bool isEnabled(Environment environment) =>
condition == null || condition.evaluate(environment);
bool isEnabled(Environment environment) => condition.evaluate(environment);
bool isEmpty() => !entries.any((entry) => entry is StatusEntry);
@ -322,8 +319,8 @@ class StatusSection {
String toString() {
var buffer = new StringBuffer();
sectionHeaderComments.forEach(buffer.writeln);
if (condition != null) {
buffer.writeln("[ ${condition} ]");
if (condition != Expression.always) {
buffer.writeln("[ $condition ]");
}
entries.forEach(buffer.writeln);
return buffer.toString();
@ -336,10 +333,10 @@ class Comment {
Comment(this._comment);
/// Returns the issue number embedded in [comment] or `null` if there is none.
int issueNumber(String comment) {
int? issueNumber(String comment) {
var match = _issuePattern.firstMatch(comment);
if (match == null) return null;
return int.parse(match[1]);
return int.parse(match[1]!);
}
@override
@ -377,7 +374,7 @@ class CommentEntry extends Entry {
class StatusEntry extends Entry {
final String path;
final List<Expectation> expectations;
final Comment comment;
final Comment? comment;
StatusEntry(this.path, lineNumber, this.expectations, this.comment)
: super(lineNumber);

View file

@ -12,5 +12,5 @@ abstract class Environment {
void validate(String name, String value, List<String> errors);
/// Looks up the value of the variable with [name].
String lookUp(String name);
String? lookUp(String name);
}

View file

@ -206,17 +206,17 @@ class Expectation {
}
final String _name;
final Expectation _group;
final Expectation? _group;
/// Whether this expectation is a test outcome. If not, it's a "meta marker".
final bool isOutcome;
Expectation._(this._name, {Expectation group, bool isMeta: false})
Expectation._(this._name, {Expectation? group, bool isMeta: false})
: _group = group,
isOutcome = !isMeta;
bool canBeOutcomeOf(Expectation expectation) {
var outcome = this;
Expectation? outcome = this;
if (outcome == ignore) return true;
while (outcome != null) {

View file

@ -102,14 +102,14 @@ LogicExpression _minimizeByComplementation(LogicExpression expression) {
});
var combinedMinSets = _combineMinSets(
clauses.map((e) => [new LogicExpression.and(e)]).toList(), []);
List<List<Expression>> minCover = _findMinCover(combinedMinSets, []);
List<List<LogicExpression>> minCover = _findMinCover(combinedMinSets, []);
var finalOperands = minCover.map((minSet) => _reduceMinSet(minSet)).toList();
return new LogicExpression.or(finalOperands).normalize();
}
/// Computes all assignments of literals that make the [expression] evaluate to
/// true.
List<Expression> _satisfiableMinTerms(Expression expression) {
List<Expression>? _satisfiableMinTerms(Expression expression) {
var variables = _getVariables(expression);
bool hasNotSatisfiableAssignment = false;
List<Expression> satisfiableTerms = <Expression>[];
@ -142,7 +142,7 @@ List<Expression> _satisfiableMinTerms(Expression expression) {
/// which the [variables] was found.
class TruthTableEnvironment extends Environment {
final List<Expression> variables;
int configuration;
int configuration = -1;
TruthTableEnvironment(this.variables);
@ -172,10 +172,10 @@ class TruthTableEnvironment extends Environment {
/// Combines [minSets] recursively as long as possible. Prime implicants (those
/// that cannot be reduced further) are kept track of in [primeImplicants]. When
/// finished the function returns all combined min sets.
List<List<Expression>> _combineMinSets(
List<List<Expression>> minSets, List<List<Expression>> primeImplicants) {
List<List<LogicExpression>> _combineMinSets(List<List<LogicExpression>> minSets,
List<List<LogicExpression>> primeImplicants) {
List<List<LogicExpression>> combined = <List<LogicExpression>>[];
var addedInThisIteration = new Set<List<Expression>>();
var addedInThisIteration = new Set<List<LogicExpression>>();
for (var i = 0; i < minSets.length; i++) {
var minSet = minSets[i];
var combinedMinSet = false;
@ -262,8 +262,9 @@ LogicExpression _reduceMinSet(List<LogicExpression> minSet) {
/// minimum set cover is NP-hard, and we are not trying to be really cleaver
/// here. The implicants that cover only a single truth assignment can be
/// directly added to [cover].
List<List<Expression>> _findMinCover(
List<List<Expression>> primaryImplicants, List<List<Expression>> cover) {
List<List<LogicExpression>> _findMinCover(
List<List<LogicExpression>> primaryImplicants,
List<List<LogicExpression>> cover) {
var minCover = primaryImplicants.toList()..addAll(cover);
if (cover.isEmpty) {
var allImplicants = primaryImplicants.toList();
@ -332,9 +333,9 @@ List<Expression> _difference(List<Expression> As, List<Expression> Bs) {
/// Finds the first occurrence of [expressionToFind] in [expressions] or
/// returns null.
Expression _findFirst<Expression>(
Expression? _findFirst<Expression>(
expressionToFind, List<Expression> expressions) {
return expressions.firstWhere(
return expressions.cast<Expression?>().firstWhere(
(otherExpression) => expressionToFind.compareTo(otherExpression) == 0,
orElse: () => null);
}

View file

@ -9,6 +9,11 @@ import '../environment.dart';
/// A parsed Boolean expression AST.
abstract class Expression implements Comparable<Expression> {
/// An expression that always evaluates to true.
///
/// Used for the section at the top of a status file with no expression.
static const Expression always = _AlwaysExpression();
/// Parses Boolean expressions in a .status file for Dart.
///
/// The grammar is:
@ -26,6 +31,8 @@ abstract class Expression implements Comparable<Expression> {
static Expression parse(String expression) =>
new _ExpressionParser(expression).parse();
const Expression();
/// Validates that this expression does not contain any invalid uses of
/// variables.
///
@ -111,6 +118,29 @@ class Variable {
}
}
/// An expression that always evaluates to true.
///
/// Used for the implicit section at the top of a status file that always
/// matches.
class _AlwaysExpression extends Expression {
const _AlwaysExpression();
@override
int _compareToMyType(covariant _AlwaysExpression other) => 0;
@override
int get _typeComparison => 0;
@override
bool evaluate(Environment environment) => true;
@override
Expression normalize() => this;
@override
void validate(Environment environment, List<String> errors) {}
}
/// Tests whether a given variable is or is not equal some literal value, as in:
/// ```
/// $variable == someValue
@ -156,7 +186,7 @@ class ComparisonExpression extends Expression {
// Comparisons come before variables so that "$compiler == ..." and
// "$runtime == ..." appear on the left in status expressions.
int get _typeComparison => 0;
int get _typeComparison => 1;
String toString() => "\$${left.name} ${negate ? '!=' : '=='} $right";
}
@ -203,7 +233,7 @@ class VariableExpression extends Expression {
return _compareBool(negate, other.negate);
}
int get _typeComparison => 1;
int get _typeComparison => 2;
String toString() => "${negate ? "!" : ""}\$${variable.name}";
}
@ -237,7 +267,7 @@ class LogicExpression extends Expression {
}
}
Expression normalize() {
LogicExpression normalize() {
// Normalize the order of the clauses. Since there is no short-circuiting,
// a || b means the same as b || a. Picking a standard order lets us
// identify and collapse identical expressions that only differ by clause
@ -281,7 +311,7 @@ class LogicExpression extends Expression {
return 0;
}
int get _typeComparison => 2;
int get _typeComparison => 3;
String toString() {
String parenthesize(Expression operand) {
@ -365,7 +395,7 @@ class _ExpressionParser {
"Expected identifier in expression, got ${_scanner.current}");
}
var left = new Variable(_scanner.current);
var left = new Variable(_scanner.current!);
_scanner.advance();
if (!negate &&
@ -378,7 +408,7 @@ class _ExpressionParser {
"Expected value in expression, got ${_scanner.current}");
}
var right = _scanner.advance();
var right = _scanner.advance()!;
return new ComparisonExpression(left, right, isNotEquals);
} else {
return new VariableExpression(left, negate: negate);
@ -401,7 +431,7 @@ class _Scanner {
/// The token strings being iterated.
final Iterator<String> tokenIterator;
String current;
String? current;
_Scanner(String expression) : tokenIterator = tokenize(expression).iterator {
advance();
@ -414,7 +444,7 @@ class _Scanner {
return _tokenPattern
.allMatches(expression)
.map((match) => match[0])
.map((match) => match[0]!)
.toList();
}
@ -424,7 +454,7 @@ class _Scanner {
// All non-identifier tokens are one or two characters,
// so a longer token must be an identifier.
bool get isIdentifier =>
current.length > 2 || _identifierPattern.hasMatch(current);
current!.length > 2 || _identifierPattern.hasMatch(current!);
/// If the current token is [token], consumes it and returns `true`.
bool match(String token) {
@ -435,7 +465,7 @@ class _Scanner {
}
/// Consumes the current token and returns it.
String advance() {
String? advance() {
var previous = current;
current = tokenIterator.moveNext() ? tokenIterator.current : null;
return previous;

View file

@ -45,7 +45,7 @@ final _issuePattern = new RegExp(r"[Ii]ssue (\d+)");
class StatusFile {
final String path;
final List<StatusSection> sections = [];
final List<String> _comments = [];
final List<String?> _comments = [];
int _lineCount = 0;
@ -58,13 +58,10 @@ class StatusFile {
var lines = new File(path).readAsLinesSync();
_comments.length = lines.length + 1;
// The current section whose rules are being parsed.
StatusSection section;
for (var line in lines) {
_lineCount++;
fail(String message, [List<String> errors]) {
fail(String message, [List<String>? errors]) {
throw new SyntaxError(_shortPath, _lineCount, line, message, errors);
}
@ -86,9 +83,8 @@ class StatusFile {
var match = _sectionPattern.firstMatch(source);
if (match != null) {
try {
var condition = Expression.parse(match[1].trim());
section = new StatusSection(condition, _lineCount);
sections.add(section);
var condition = Expression.parse(match[1]!.trim());
sections.add(new StatusSection(condition, _lineCount));
} on FormatException {
fail("Status expression syntax error");
}
@ -98,10 +94,10 @@ class StatusFile {
// Otherwise, it should be a new entry under the current section.
match = _entryPattern.firstMatch(source);
if (match != null) {
var path = match[1].trim();
var path = match[1]!.trim();
// TODO(whesse): Handle test names ending in a wildcard (*).
var expectations = <Expectation>[];
for (var name in match[2].split(",")) {
for (var name in match[2]!.split(",")) {
name = name.trim();
try {
expectations.add(Expectation.find(name));
@ -114,12 +110,11 @@ class StatusFile {
// If we haven't found a section header yet, create an implicit section
// that matches everything.
if (section == null) {
section = new StatusSection(null, -1);
sections.add(section);
if (sections.isEmpty) {
sections.add(new StatusSection(Expression.always, -1));
}
section.entries
sections.last.entries
.add(new StatusEntry(path, _lineCount, expectations, issue));
continue;
}
@ -138,8 +133,6 @@ class StatusFile {
// TODO(rnystrom): It would be more useful if it reported all of the errors
// instead of stopping on the first.
for (var section in sections) {
if (section.condition == null) continue;
var errors = <String>[];
section.condition.validate(environment, errors);
@ -158,11 +151,11 @@ class StatusFile {
}
/// Returns the issue number embedded in [comment] or `null` if there is none.
int _issueNumber(String comment) {
int? _issueNumber(String comment) {
var match = _issuePattern.firstMatch(comment);
if (match == null) return null;
return int.parse(match[1]);
return int.parse(match[1]!);
}
String toString() {
@ -192,7 +185,7 @@ class StatusFile {
var lastLine = 0;
var needBlankLine = false;
void writeLine(String text, int line) {
void writeLine(String? text, int line) {
var comment = _comments[line];
if (text == null && comment == null) {
// There's no comment on this line, so it's blank.
@ -216,17 +209,15 @@ class StatusFile {
}
void writeText(String text, int line) {
if (line != null) {
while (++lastLine < line) {
writeLine(null, lastLine);
}
while (++lastLine < line) {
writeLine(null, lastLine);
}
writeLine(text, line);
}
for (var section in sections) {
if (section.condition != null) {
if (section.condition != Expression.always) {
writeText("[ ${section.condition} ]", section.lineNumber);
}
@ -254,8 +245,8 @@ class StatusFile {
class StatusSection {
/// The expression that determines when this section is applied.
///
/// May be `null` for paths that appear before any section header in the file.
/// In that case, the section always applies.
/// Will be [Expression.always] for paths that appear before any section
/// header in the file. In that case, the section always applies.
final Expression condition;
/// The one-based line number where the entry appears in the file.
@ -264,8 +255,7 @@ class StatusSection {
final List<StatusEntry> entries = [];
/// Returns true if this section should apply in the given [environment].
bool isEnabled(Environment environment) =>
condition == null || condition.evaluate(environment);
bool isEnabled(Environment environment) => condition.evaluate(environment);
StatusSection(this.condition, this.lineNumber);
}
@ -278,7 +268,7 @@ class StatusEntry {
final int lineNumber;
final List<Expectation> expectations;
final int issue;
final int? issue;
StatusEntry(this.path, this.lineNumber, this.expectations, this.issue);
}
@ -289,7 +279,7 @@ class SyntaxError implements Exception {
final int lineNumber;
final String line;
final String message;
final List<String> errors;
final List<String>? errors;
SyntaxError(this.file, this.lineNumber, this.line, this.message, this.errors);
@ -298,10 +288,8 @@ class SyntaxError implements Exception {
buffer.writeln('$message in "$file" line $lineNumber:');
buffer.writeln(line);
if (errors != null) {
for (var error in errors) {
buffer.writeln("- ${error.replaceAll('\n', '\n ')}");
}
for (var error in errors ?? const []) {
buffer.writeln("- ${error.replaceAll('\n', '\n ')}");
}
return buffer.toString().trimRight();

View file

@ -115,10 +115,9 @@ Iterable<LintingError> lintAlphabeticalOrderingOfPaths(StatusSection section) {
/// Checks that each section expression have been normalized.
Iterable<LintingError> lintNormalizedSection(StatusSection section) {
if (section.condition == null) return const [];
var nonNormalized = section.condition.toString();
var normalized = section.condition.normalize().toString();
if (section.condition.toString() != normalized) {
if (nonNormalized != normalized) {
return [
new LintingError(
section.lineNumber,
@ -184,11 +183,11 @@ Iterable<LintingError> lintSectionHeaderOrdering(List<StatusSection> sections) {
if (witness != null) {
return [
new LintingError(
witness.second.lineNumber,
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}.")
"'${witness.first!.condition}' on line ${witness.first!.lineNumber} "
"should come before '${witness.second!.condition}' at line "
"${witness.second!.lineNumber}.")
];
}
return [];
@ -198,14 +197,12 @@ Iterable<LintingError> lintSectionHeaderOrdering(List<StatusSection> sections) {
Iterable<LintingError> lintSectionHeaderDuplicates(
List<StatusSection> sections) {
var errors = <LintingError>[];
var sorted = sections.where((section) => section.condition != null).toList()
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 != null &&
previousSection.condition != null &&
section.condition.compareTo(previousSection.condition) == 0) {
if (section.condition.compareTo(previousSection.condition) == 0) {
errors.add(new LintingError(
section.lineNumber,
"The condition "
@ -216,7 +213,7 @@ Iterable<LintingError> lintSectionHeaderDuplicates(
return errors;
}
ListNotEqualWitness<T> _findNotEqualWitness<T>(List<T> first, List<T> second) {
ListNotEqualWitness<T>? _findNotEqualWitness<T>(List<T> first, List<T> second) {
if (first.isEmpty && second.isEmpty) {
return null;
}
@ -233,7 +230,7 @@ ListNotEqualWitness<T> _findNotEqualWitness<T>(List<T> first, List<T> second) {
}
class ListNotEqualWitness<T> {
final T first;
final T second;
final T? first;
final T? second;
ListNotEqualWitness(this.first, this.second);
}

View file

@ -2,6 +2,8 @@
// 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:status_file/src/expression.dart';
import 'canonical_status_file.dart';
import 'dart:convert';
@ -52,10 +54,9 @@ StatusFile _sortSectionsAndCombine(StatusFile statusFile) {
List<StatusSection> newSections = [];
// Copy over all sections and normalize all the expressions.
oldStatusFile.sections.forEach((section) {
if (section.condition != null && section.isEmpty()) {
return;
}
if (section.condition != null) {
if (section.condition != Expression.always) {
if (section.isEmpty()) return;
newSections.add(new StatusSection(section.condition.normalize(),
section.lineNumber, section.sectionHeaderComments)
..entries.addAll(section.entries));
@ -63,23 +64,17 @@ StatusFile _sortSectionsAndCombine(StatusFile statusFile) {
newSections.add(section);
}
});
// Sort the headers
newSections.sort((a, b) {
if (a.condition == null) {
return -1;
} else if (b.condition == null) {
return 1;
}
return a.condition.compareTo(b.condition);
});
newSections.sort((a, b) => a.condition.compareTo(b.condition));
// See if we can combine section headers by simple comparison.
StatusFile newStatusFile = new StatusFile(statusFile.path);
var newStatusFile = new StatusFile(statusFile.path);
newStatusFile.sections.add(newSections[0]);
for (var i = 1; i < newSections.length; i++) {
var previousSection = newSections[i - 1];
var currentSection = newSections[i];
if (previousSection.condition != null &&
previousSection.condition.compareTo(currentSection.condition) == 0) {
if (previousSection.condition.compareTo(currentSection.condition) == 0) {
newStatusFile.sections.last.entries.addAll(currentSection.entries);
} else {
newStatusFile.sections.add(currentSection);

View file

@ -2,7 +2,7 @@ name: status_file
# This package is not intended for consumption on pub.dev. DO NOT publish.
publish_to: none
environment:
sdk: "^2.3.0"
sdk: "^2.12.0"
dependencies:
path: "^1.4.0"
args: "^1.4.4"

View file

@ -100,7 +100,7 @@ void testCheckForDisjunctions_notAllowedDisjunction() {
vm/tests: Skip # this comment is valid
""",
"Error at line 1: Expression contains '||'. Please split the expression "
"into multiple separate sections.",
"into multiple separate sections.",
disjunctions: true);
}
@ -118,7 +118,7 @@ vm/tests: Skip # this should come after a_test
a_test: Pass
""",
"Error at line 1: Test paths are not alphabetically ordered in "
"section. a_test should come before vm/tests.");
"section. a_test should come before vm/tests.");
}
void testCheckForAlphabeticalOrderingOfPaths_okOrdering() {
@ -139,7 +139,7 @@ bc_test: Pass
xyz_test: Skip
""",
"Error at line 1: The status entry 'a_test: Pass' is duplicated on lines "
"2 and 3.");
"2 and 3.");
}
void testCheckForCorrectOrderingInSections_invalidRuntimeBeforeCompiler() {
@ -148,7 +148,7 @@ void testCheckForCorrectOrderingInSections_invalidRuntimeBeforeCompiler() {
a_test: Pass
""",
r"Error at line 1: Condition expression should be '$compiler == dart2js "
r"&& $runtime == ff' but was '$runtime == ff && $compiler == dart2js'.");
r"&& $runtime == ff' but was '$runtime == ff && $compiler == dart2js'.");
}
void testCheckForCorrectOrderingInSections_invalidRuntimeBeforeMode() {
@ -157,7 +157,7 @@ void testCheckForCorrectOrderingInSections_invalidRuntimeBeforeMode() {
a_test: Pass
""",
r"Error at line 1: Condition expression should be '$mode == debug && "
r"$runtime == ff' but was '$runtime == ff && $mode == debug'.");
r"$runtime == ff' but was '$runtime == ff && $mode == debug'.");
}
void testCheckForCorrectOrderingInSections_invalidSystemBeforeMode() {
@ -166,7 +166,7 @@ void testCheckForCorrectOrderingInSections_invalidSystemBeforeMode() {
a_test: Pass
""",
r"Error at line 1: Condition expression should be '$mode == debug && "
r"$system == win' but was '$system == win && $mode == debug'.");
r"$system == win' but was '$system == win && $mode == debug'.");
}
void testCheckForCorrectOrderingInSections_invalidStrongBeforeKernel() {
@ -175,7 +175,7 @@ void testCheckForCorrectOrderingInSections_invalidStrongBeforeKernel() {
a_test: Pass
""",
r"Error at line 1: Condition expression should be '!$kernel && !$strong' "
r"but was '!$strong && !$kernel'.");
r"but was '!$strong && !$kernel'.");
}
void testCheckForCorrectOrderingInSections_invalidOrdering() {
@ -184,8 +184,8 @@ void testCheckForCorrectOrderingInSections_invalidOrdering() {
a_test: Pass
""",
r"Error at line 1: Condition expression should be '$builder_tag == "
r"strong && $compiler == dart2js && !$browser' but was "
r"'$compiler == dart2js && $builder_tag == strong && !$browser'.");
r"strong && $compiler == dart2js && !$browser' but was "
r"'$compiler == dart2js && $builder_tag == strong && !$browser'.");
}
void testCheckForCorrectOrderingInSections_okOrdering() {
@ -203,8 +203,8 @@ a_test: Pass
a_test: Pass
""",
r"Error at line 1: Section expressions are not correctly ordered in file."
r" '$compiler == dart2js' on line 4 should come before '$runtime == ff' "
r"at line 1.");
r" '$compiler == dart2js' on line 4 should come before '$runtime == ff' "
r"at line 1.");
}
void checkLintNormalizedSection_invalidAlphabeticalOrderingVariableArguments() {
@ -216,8 +216,8 @@ a_test: Pass
a_test: Pass
""",
r"Error at line 1: Section expressions are not correctly ordered in file."
r" '$runtime == chrome' on line 4 should come before '$runtime == ff' at "
r"line 1.");
r" '$runtime == chrome' on line 4 should come before '$runtime == ff' at "
r"line 1.");
}
void checkLintNormalizedSection_invalidOrderingWithNotEqual() {
@ -233,8 +233,8 @@ a_test: Pass
a_test: Pass
""",
r"Error at line 4: Section expressions are not correctly ordered in file."
r" '$runtime == ff' on line 7 should come before '$runtime != ff' at "
r"line 4.");
r" '$runtime == ff' on line 7 should come before '$runtime != ff' at "
r"line 4.");
}
void checkLintNormalizedSection_invalidOrderingWithNegation() {
@ -251,7 +251,7 @@ a_test: Pass
""",
r"Error at line 4: Section expressions are not correctly ordered in file."
r" '$checked' on line 7 should come before '!$checked' at line 4.");
r" '$checked' on line 7 should come before '!$checked' at line 4.");
}
void checkLintNormalizedSection_correctOrdering() {
@ -285,5 +285,5 @@ a_test: Pass
a_test: Pass
""",
r"Error at line 4: The condition '!$browser' is duplicated on lines 1 "
r"and 4.");
r"and 4.");
}

View file

@ -69,8 +69,8 @@ void checkSemanticallyEqual(StatusFile original, StatusFile normalized,
"$entriesInNormalized. Those two numbers are not the same.");
}
for (var section in original.sections) {
section.entries.where((entry) => entry is StatusEntry).forEach((entry) =>
findInStatusFile(normalized, entry, section.condition?.normalize(),
section.entries.whereType<StatusEntry>().forEach((entry) =>
findInStatusFile(normalized, entry, section.condition.normalize(),
warnOnDuplicateHeader: warnOnDuplicateHeader));
}
}
@ -87,12 +87,7 @@ void findInStatusFile(
{bool warnOnDuplicateHeader = false}) {
int foundEntryPosition = -1;
for (var section in statusFile.sections) {
if (section.condition == null && condition != null ||
section.condition != null && condition == null) {
continue;
}
if (section.condition != null &&
section.condition.normalize().compareTo(condition) != 0) {
if (section.condition.normalize().compareTo(condition) != 0) {
continue;
}
var matchingEntries = section.entries

View file

@ -19,8 +19,8 @@ void main() {
if (!entry.path.endsWith(".status")) continue;
try {
new StatusFile.read(entry.path);
} catch (err) {
Expect.fail("Could not parse '${entry.path}'.\n$err");
} catch (err, stack) {
Expect.fail("Could not parse '${entry.path}'.\n$err\n$stack");
}
}
}

View file

@ -55,13 +55,13 @@ void testDnf() {
// https://en.wikipedia.org/wiki/Quine%E2%80%93McCluskey_algorithm
shouldDnfTo(
r"$a && !$b && !$c && !$d || $a && !$b && !$c && $d || "
r"$a && !$b && $c && !$d || $a && !$b && $c && $d",
r"$a && !$b && $c && !$d || $a && !$b && $c && $d",
r"$a && !$b");
shouldDnfTo(
r"!$a && $b && !$c && !$d || $a && !$b && !$c && !$d || "
r"$a && !$b && $c && !$d || $a && !$b && $c && $d || $a && $b && !$c && !$d ||"
r" $a && $b && $c && $d || $a && !$b && !$c && $d || $a && $b && $c && !$d",
r"$a && !$b && $c && !$d || $a && !$b && $c && $d || $a && $b && !$c && !$d ||"
r" $a && $b && $c && $d || $a && !$b && !$c && $d || $a && $b && $c && !$d",
r"$a && !$b || $a && $c || $b && !$c && !$d");
// Test that an expression is converted to dnf and minified correctly.

View file

@ -17,7 +17,7 @@ class TestEnvironment implements Environment {
}
/// Looks up the value of the variable with [name].
String lookUp(String name) => _values[name];
String? lookUp(String name) => _values[name];
operator []=(String key, String value) => _values[key] = value;
}
@ -33,8 +33,8 @@ main() {
}
void testExpression() {
var expression = Expression
.parse(r" $mode == debug && ($arch == chromium || $arch == dartc) ");
var expression = Expression.parse(
r" $mode == debug && ($arch == chromium || $arch == dartc) ");
Expect.equals(r"$mode == debug && ($arch == chromium || $arch == dartc)",
expression.toString());