Skip to content
This repository was archived by the owner on Nov 20, 2024. It is now read-only.

Commit 0c8ba72

Browse files
authored
Implement null_safety rule (#2773)
* Implement enable_null_safety rule * year * Update tests * Use `reflective_test_loader` * `all.dart`
1 parent f0b633c commit 0c8ba72

File tree

6 files changed

+138
-0
lines changed

6 files changed

+138
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
- `prefer_const_constructors`
3939
- new lint: `implicit_call_tearoffs`
4040
- new lint: `unnecessary_library_directive`
41+
- new lint: `enable_null_safety`
4142

4243
# 1.28.0
4344

example/all.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ linter:
7373
- empty_catches
7474
- empty_constructor_bodies
7575
- empty_statements
76+
- enable_null_safety
7677
- eol_at_end_of_file
7778
- exhaustive_cases
7879
- file_names

lib/src/rules.dart

+2
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ import 'rules/do_not_use_environment.dart';
7575
import 'rules/empty_catches.dart';
7676
import 'rules/empty_constructor_bodies.dart';
7777
import 'rules/empty_statements.dart';
78+
import 'rules/enable_null_safety.dart';
7879
import 'rules/eol_at_end_of_file.dart';
7980
import 'rules/exhaustive_cases.dart';
8081
import 'rules/file_names.dart';
@@ -297,6 +298,7 @@ void registerLintRules({bool inTestMode = false}) {
297298
..register(EmptyCatches())
298299
..register(EmptyConstructorBodies())
299300
..register(EmptyStatements())
301+
..register(EnableNullSafety())
300302
..register(EolAtEndOfFile())
301303
..register(ExhaustiveCases())
302304
..register(FileNames())

lib/src/rules/enable_null_safety.dart

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:analyzer/dart/ast/ast.dart';
6+
import 'package:analyzer/dart/ast/token.dart';
7+
import 'package:analyzer/dart/ast/visitor.dart';
8+
9+
import '../analyzer.dart';
10+
11+
const _desc = r'Do use sound null safety.';
12+
13+
const _details = r'''
14+
**DO** use sound null safety, by not specifying a dart version lower than `2.12`.
15+
16+
**BAD:**
17+
```dart
18+
// @dart=2.8
19+
a() {
20+
}
21+
```
22+
23+
**GOOD:**
24+
```dart
25+
b() {
26+
}
27+
''';
28+
29+
class EnableNullSafety extends LintRule implements NodeLintRule {
30+
EnableNullSafety()
31+
: super(
32+
name: 'enable_null_safety',
33+
description: _desc,
34+
details: _details,
35+
group: Group.style);
36+
37+
@override
38+
void registerNodeProcessors(
39+
NodeLintRegistry registry, LinterContext context) {
40+
var visitor = _Visitor(this, context);
41+
registry.addCompilationUnit(this, visitor);
42+
}
43+
}
44+
45+
class _Visitor extends SimpleAstVisitor<void> {
46+
// to be kept in sync with LanguageVersionOverrideVerifier (added groups for the version)
47+
static final regExp = RegExp(r'^\s*//\s*@dart\s*=\s*(\d+)\.(\d+)');
48+
final LintRule rule;
49+
final LinterContext context;
50+
51+
_Visitor(this.rule, this.context);
52+
53+
@override
54+
void visitCompilationUnit(CompilationUnit node) {
55+
var beginToken = node.beginToken;
56+
if (beginToken.type == TokenType.SCRIPT_TAG) {
57+
beginToken = beginToken.next!;
58+
}
59+
CommentToken? comment = beginToken.precedingComments;
60+
while (comment != null) {
61+
var match = regExp.firstMatch(comment.lexeme);
62+
if (match != null && match.groupCount == 2) {
63+
var major = int.parse(match.group(1)!);
64+
var minor = int.parse(match.group(2)!);
65+
if (major == 1 || (major == 2 && minor < 12)) {
66+
rule.reportLintForToken(comment);
67+
}
68+
}
69+
70+
var next = comment.next;
71+
comment = next is CommentToken ? next : null;
72+
}
73+
}
74+
}

test/rules/all.dart

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import 'dangling_library_doc_comments_test.dart'
3434
as dangling_library_doc_comments;
3535
import 'deprecated_consistency_test.dart' as deprecated_consistency;
3636
import 'discarded_futures_test.dart' as discarded_futures;
37+
import 'enable_null_safety_test.dart' as enable_null_safety;
3738
import 'file_names_test.dart' as file_names;
3839
import 'flutter_style_todos_test.dart' as flutter_style_todos;
3940
import 'hash_and_equals_test.dart' as hash_and_equals;
@@ -114,6 +115,7 @@ void main() {
114115
dangling_library_doc_comments.main();
115116
deprecated_consistency.main();
116117
discarded_futures.main();
118+
enable_null_safety.main();
117119
file_names.main();
118120
flutter_style_todos.main();
119121
hash_and_equals.main();
+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:test_reflective_loader/test_reflective_loader.dart';
6+
7+
import '../rule_test_support.dart';
8+
9+
main() {
10+
defineReflectiveSuite(() {
11+
defineReflectiveTests(EnableNullSafetyTest);
12+
});
13+
}
14+
15+
@reflectiveTest
16+
class EnableNullSafetyTest extends LintRuleTest {
17+
@override
18+
String get lintRule => 'enable_null_safety';
19+
20+
test_2_11() async {
21+
await assertDiagnostics(r'''
22+
// @dart=2.11
23+
f() {
24+
}
25+
''', [
26+
lint(0, 13),
27+
]);
28+
}
29+
30+
test_2_12() async {
31+
await assertNoDiagnostics(r'''
32+
// @dart=2.12
33+
f() {
34+
}
35+
''');
36+
}
37+
38+
test_2_8() async {
39+
await assertDiagnostics(r'''
40+
// @dart=2.8
41+
f() {
42+
}
43+
''', [
44+
lint(0, 12),
45+
]);
46+
}
47+
48+
test_2_8_shebang() async {
49+
await assertDiagnostics(r'''
50+
#!/usr/bin/dart
51+
// @dart=2.8
52+
f() {
53+
}
54+
''', [
55+
lint(16, 12),
56+
]);
57+
}
58+
}

0 commit comments

Comments
 (0)