Skip to content

Commit

Permalink
Version 3.3.0-123.0.dev
Browse files Browse the repository at this point in the history
Merge 63118a1 into dev
  • Loading branch information
Dart CI committed Nov 11, 2023
2 parents 6415489 + 63118a1 commit 1a1b9cb
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,63 +28,96 @@ class ConvertToFlutterStyleTodo extends ResolvedCorrectionProducer {
var diagnosticOffset = diagnostic?.problemMessage.offset;
if (diagnosticOffset == null) return;

Token? comment;

// Find the token that follows the reported diagnostic.
Token? token = node.beginToken;
while (token != null && token != node.endToken) {
if (token.offset > diagnosticOffset) break;

token = token.next;
}
if (token == null) return;

// Identify the right comment.
Token? comment = token.precedingComments;
while (comment != null) {
if (comment.offset >= diagnosticOffset) break;
comment = comment.next;
// First, check for a doc comment.
if (token is CommentToken) {
while (token != null) {
if (token.offset == diagnosticOffset) {
comment = token;
break;
}

token = token.next;
}
} else {
// Then look for the token that owns the preceding comment.
while (token != null && token != node.endToken) {
if (token.offset > diagnosticOffset) break;

token = token.next;
}
if (token == null) return;

comment = token.precedingComments;
while (comment != null) {
if (comment.offset == diagnosticOffset) break;
comment = comment.next;
}
}
if (comment == null) return;

var content = comment.lexeme;

// Fix leading spaces.
// Convert doc comments.
if (content.startsWith('///')) {
content = content.substring(1);
}

// Fix unwanted leading spaces.
var todoIndex = content.indexOf('TODO');
if (todoIndex == -1) {
todoIndex = content.indexOf('todo');
}
if (todoIndex == -1) return;

if (todoIndex != 3) {
content = content.replaceRange(2, todoIndex, ' ');
if (todoIndex > 3) {
// Eat white space.
if (content.substring(3, todoIndex).trim().isEmpty) {
content = content.replaceRange(3, todoIndex, '');
todoIndex = 3;
}
}

// Try adding a missing leading space before `TODO`.
// Try adding a missing leading space.
if (!content.startsWith('// ')) {
content = content.replaceFirst('//', '// ');
}

// Try removing an unwanted space after `TODO`.
if (content.length > 7 && content[7] == ' ') {
if (todoIndex == 3 && content.length > 7 && content[7] == ' ') {
content = content.replaceRange(7, 8, '');
}

// Try adding a colon.
var index = content.indexOf(')') + 1;
if (content.length > index && !content.startsWith(':', index)) {
content = content.replaceFirst(')', '):');
if (todoIndex == 3) {
var colonIndex = content.indexOf(')') + 1;
if (content.length > colonIndex && !content.startsWith(':', colonIndex)) {
content = content.replaceFirst(')', '):');
}
}

// Try fixing lower case.
// Try fixing a lower case `todo`.
if (content.startsWith('// todo')) {
content = content.replaceRange(3, 7, 'TODO');
}

// Wrap any stray "TODO"s in message contents in ticks.
content =
content.replaceAllMapped(RegExp('TODO', caseSensitive: false), (match) {
var todoText = content.substring(match.start, match.end);
return match.start > 4 ? '`$todoText`' : todoText;
});

// TODO(pq): consider adding missing user info.
// Possibly inserting '(${Platform.environment['USER'] ?? Platform.environment['USERNAME']}')
// (assuming the environment variable is set).

// If the generated content doesn't match flutter style, don't apply it.
if (!content.startsWith(FlutterStyleTodos.todoExpectedRegExp)) return;
if (FlutterStyleTodos.invalidTodo(content)) return;

await builder.addDartFileEdit(file, (builder) {
builder.addReplacement(range.token(comment as Token), (builder) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,41 @@ class ConvertToFlutterStyleTodoTest extends FixProcessorLintTest {
@override
String get lintCode => LintNames.flutter_style_todos;

Future<void> test_commentContent() async {
await resolveTestCode('''
// Here's a TODO and a todo and a TODO.
void f() { }
''');
await assertHasFix('''
// Here's a `TODO` and a `todo` and a `TODO`.
void f() { }
''', errorFilter: (e) => e.errorCode != TodoCode.TODO);
}

Future<void> test_docComment() async {
await resolveTestCode('''
/// Docs.
/// TODO(user) msg.
void f() { }
''');
await assertHasFix('''
/// Docs.
// TODO(user): msg.
void f() { }
''', errorFilter: (e) => e.errorCode != TodoCode.TODO);
}

Future<void> test_docCommentSolo() async {
await resolveTestCode('''
/// TODO(user) msg.
void f() { }
''');
await assertHasFix('''
// TODO(user): msg.
void f() { }
''', errorFilter: (e) => e.errorCode != TodoCode.TODO);
}

Future<void> test_extraLeadingSpace() async {
await resolveTestCode('''
// TODO(user) msg.
Expand All @@ -72,6 +107,14 @@ void f() { }
''');
}

Future<void> test_missingClosingParen() async {
await resolveTestCode('''
// TODO(user msg.
void f() { }
''');
await assertNoFix(errorFilter: (e) => e.errorCode != TodoCode.TODO);
}

Future<void> test_missingColon() async {
await resolveTestCode('''
// TODO(user) msg.
Expand Down Expand Up @@ -117,15 +160,6 @@ void f() {}
''', errorFilter: (e) => e.errorCode != TodoCode.TODO);
}

Future<void> test_todoInContent() async {
await resolveTestCode('''
// Here's a TODO
void f() { }
''');

await assertNoFix(errorFilter: (e) => e.errorCode != TodoCode.TODO);
}

Future<void> test_unwantedSpaceBeforeUser() async {
await resolveTestCode('''
// TODO (user): msg.
Expand Down
15 changes: 9 additions & 6 deletions pkg/linter/lib/src/rules/flutter_style_todos.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ class FlutterStyleTodos extends LintRule {
'flutter_style_todos', "To-do comment doesn't follow the Flutter style.",
correctionMessage: 'Try following the Flutter style for to-do comments.');

/// A regular expression that matches a correctly formatted `TODO`.
static final RegExp todoExpectedRegExp =
static final _todoRegExp = RegExp(r'//+(.* )?TODO\b', caseSensitive: false);

static final RegExp _todoExpectedRegExp =
RegExp(r'// TODO\([a-zA-Z0-9][-a-zA-Z0-9]*\): ');

FlutterStyleTodos()
Expand All @@ -51,11 +52,14 @@ class FlutterStyleTodos extends LintRule {
var visitor = _Visitor(this);
registry.addCompilationUnit(this, visitor);
}

/// Return `true` if the given [content] is invalid and should trigger a lint.
static bool invalidTodo(String content) =>
content.startsWith(_todoRegExp) &&
!content.startsWith(_todoExpectedRegExp);
}

class _Visitor extends SimpleAstVisitor<void> {
static final _todoRegExp = RegExp(r'//+(.* )?TODO\b', caseSensitive: false);

final LintRule rule;

_Visitor(this.rule);
Expand All @@ -80,8 +84,7 @@ class _Visitor extends SimpleAstVisitor<void> {

void _checkComment(Token node) {
var content = node.lexeme;
if (content.startsWith(_todoRegExp) &&
!content.startsWith(FlutterStyleTodos.todoExpectedRegExp)) {
if (FlutterStyleTodos.invalidTodo(content)) {
rule.reportLintForToken(node);
}
}
Expand Down
2 changes: 1 addition & 1 deletion tools/VERSION
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ CHANNEL dev
MAJOR 3
MINOR 3
PATCH 0
PRERELEASE 122
PRERELEASE 123
PRERELEASE_PATCH 0

0 comments on commit 1a1b9cb

Please sign in to comment.