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

Commit

Permalink
Implement null_safety rule (#2773)
Browse files Browse the repository at this point in the history
* Implement enable_null_safety rule

* year

* Update tests

* Use `reflective_test_loader`

* `all.dart`
  • Loading branch information
asashour authored Nov 9, 2022
1 parent f0b633c commit 0c8ba72
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
- `prefer_const_constructors`
- new lint: `implicit_call_tearoffs`
- new lint: `unnecessary_library_directive`
- new lint: `enable_null_safety`

# 1.28.0

Expand Down
1 change: 1 addition & 0 deletions example/all.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ linter:
- empty_catches
- empty_constructor_bodies
- empty_statements
- enable_null_safety
- eol_at_end_of_file
- exhaustive_cases
- file_names
Expand Down
2 changes: 2 additions & 0 deletions lib/src/rules.dart
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ import 'rules/do_not_use_environment.dart';
import 'rules/empty_catches.dart';
import 'rules/empty_constructor_bodies.dart';
import 'rules/empty_statements.dart';
import 'rules/enable_null_safety.dart';
import 'rules/eol_at_end_of_file.dart';
import 'rules/exhaustive_cases.dart';
import 'rules/file_names.dart';
Expand Down Expand Up @@ -297,6 +298,7 @@ void registerLintRules({bool inTestMode = false}) {
..register(EmptyCatches())
..register(EmptyConstructorBodies())
..register(EmptyStatements())
..register(EnableNullSafety())
..register(EolAtEndOfFile())
..register(ExhaustiveCases())
..register(FileNames())
Expand Down
74 changes: 74 additions & 0 deletions lib/src/rules/enable_null_safety.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright (c) 2022, 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 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/visitor.dart';

import '../analyzer.dart';

const _desc = r'Do use sound null safety.';

const _details = r'''
**DO** use sound null safety, by not specifying a dart version lower than `2.12`.
**BAD:**
```dart
// @dart=2.8
a() {
}
```
**GOOD:**
```dart
b() {
}
''';

class EnableNullSafety extends LintRule implements NodeLintRule {
EnableNullSafety()
: super(
name: 'enable_null_safety',
description: _desc,
details: _details,
group: Group.style);

@override
void registerNodeProcessors(
NodeLintRegistry registry, LinterContext context) {
var visitor = _Visitor(this, context);
registry.addCompilationUnit(this, visitor);
}
}

class _Visitor extends SimpleAstVisitor<void> {
// to be kept in sync with LanguageVersionOverrideVerifier (added groups for the version)
static final regExp = RegExp(r'^\s*//\s*@dart\s*=\s*(\d+)\.(\d+)');
final LintRule rule;
final LinterContext context;

_Visitor(this.rule, this.context);

@override
void visitCompilationUnit(CompilationUnit node) {
var beginToken = node.beginToken;
if (beginToken.type == TokenType.SCRIPT_TAG) {
beginToken = beginToken.next!;
}
CommentToken? comment = beginToken.precedingComments;
while (comment != null) {
var match = regExp.firstMatch(comment.lexeme);
if (match != null && match.groupCount == 2) {
var major = int.parse(match.group(1)!);
var minor = int.parse(match.group(2)!);
if (major == 1 || (major == 2 && minor < 12)) {
rule.reportLintForToken(comment);
}
}

var next = comment.next;
comment = next is CommentToken ? next : null;
}
}
}
2 changes: 2 additions & 0 deletions test/rules/all.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import 'dangling_library_doc_comments_test.dart'
as dangling_library_doc_comments;
import 'deprecated_consistency_test.dart' as deprecated_consistency;
import 'discarded_futures_test.dart' as discarded_futures;
import 'enable_null_safety_test.dart' as enable_null_safety;
import 'file_names_test.dart' as file_names;
import 'flutter_style_todos_test.dart' as flutter_style_todos;
import 'hash_and_equals_test.dart' as hash_and_equals;
Expand Down Expand Up @@ -114,6 +115,7 @@ void main() {
dangling_library_doc_comments.main();
deprecated_consistency.main();
discarded_futures.main();
enable_null_safety.main();
file_names.main();
flutter_style_todos.main();
hash_and_equals.main();
Expand Down
58 changes: 58 additions & 0 deletions test/rules/enable_null_safety_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright (c) 2022, 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 'package:test_reflective_loader/test_reflective_loader.dart';

import '../rule_test_support.dart';

main() {
defineReflectiveSuite(() {
defineReflectiveTests(EnableNullSafetyTest);
});
}

@reflectiveTest
class EnableNullSafetyTest extends LintRuleTest {
@override
String get lintRule => 'enable_null_safety';

test_2_11() async {
await assertDiagnostics(r'''
// @dart=2.11
f() {
}
''', [
lint(0, 13),
]);
}

test_2_12() async {
await assertNoDiagnostics(r'''
// @dart=2.12
f() {
}
''');
}

test_2_8() async {
await assertDiagnostics(r'''
// @dart=2.8
f() {
}
''', [
lint(0, 12),
]);
}

test_2_8_shebang() async {
await assertDiagnostics(r'''
#!/usr/bin/dart
// @dart=2.8
f() {
}
''', [
lint(16, 12),
]);
}
}

0 comments on commit 0c8ba72

Please sign in to comment.