自定义Lint

Catalogue   

Flutter默认提供了许多的Lint规则,但有时候我们可能有特殊的需求。如果按照官方的方式,你需要了解:

也可以参考Dart-自定义Lint之路(一)-创建Analyzer Plugin系列文章来实现自定义Lint。

但建议使用custom_lint这个库,有以下好处:

  1. 经过封装,使用更加简单
  2. 方便debug,调试起来很方便

新增检查emit规则

在使用bloc的过程中,如果emit前使用了异步操作,则可能会存在bloc已经销毁了还要emit的情况,一般这种情况需要手动判断一下isClosed。但经常会忘记这个判断,所以我们可以通过自定义Lint来检查这种情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126

import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/error/listener.dart';
import 'package:custom_lint_builder/custom_lint_builder.dart';

const _code = LintCode(
name: 'use_emit_synchronously',
problemMessage: "Please use isClosed to check if the bloc is closed.",
);

/// 异步使用bloc的emit前,需要判断isClosed,否则会报错
class UseEmitSynchronously extends DartLintRule {
UseEmitSynchronously() : super(code: _code);

@override
Future<void> run(
CustomLintResolver resolver,
ErrorReporter reporter,
CustomLintContext context,
) async {
final result = await resolver.getResolvedUnitResult();
final visitor = _Visitor(reporter);
result.unit.accept(visitor);
}

@override
List<Fix> getFixes() {
return [_AddIsClosedCheck()];
}
}

class _Visitor extends RecursiveAstVisitor<void> {
_Visitor(this.reporter);

final ErrorReporter reporter;

/// 方法调用
@override
void visitMethodInvocation(MethodInvocation node) {
super.visitMethodInvocation(node);
if (node.methodName.name == 'emit') {
AstNode? functionNode = node.thisOrAncestorMatching((ancestor) =>
ancestor is FunctionDeclaration || ancestor is MethodDeclaration);

if (functionNode != null) {
BlockFunctionBody? body;
if (functionNode is FunctionDeclaration) {
body = functionNode.functionExpression.body as BlockFunctionBody?;
} else if (functionNode is MethodDeclaration) {
body = functionNode.body as BlockFunctionBody?;
}

if (body != null) {
var hasAwait = _hasAwaitBeforeAndNoCheckEmit(body, node);
if (hasAwait) {
reporter.reportErrorForNode(_code, node, []);
}
}
}
}
}

bool _hasAwaitBeforeAndNoCheckEmit(
BlockFunctionBody body, MethodInvocation emitNode) {
var awaitVisitor = _AwaitVisitor(emitNode);
body.accept(awaitVisitor);
return awaitVisitor.foundAwait &&
!awaitVisitor.foundIsClosedCheckBeforeEmit;
}
}

class _AwaitVisitor extends RecursiveAstVisitor<void> {
_AwaitVisitor(this.emitNode);

final MethodInvocation emitNode;
bool foundAwait = false;
bool emitEncountered = false;
bool foundIsClosedCheckBeforeEmit = false;

@override
void visitAwaitExpression(AwaitExpression node) {
if (!emitEncountered) {
foundAwait = true;
}
super.visitAwaitExpression(node);
}

@override
void visitIfStatement(IfStatement node) {
if (node.expression.toSource().contains('isClosed')) {
foundIsClosedCheckBeforeEmit = true;
}
super.visitIfStatement(node);
}

@override
void visitMethodInvocation(MethodInvocation node) {
if (node == emitNode) {
emitEncountered = true;
}
super.visitMethodInvocation(node);
}
}

/// 用于修复的操作
class _AddIsClosedCheck extends DartFix {
@override
void run(
CustomLintResolver resolver,
ChangeReporter reporter,
CustomLintContext context,
AnalysisError analysisError,
List<AnalysisError> others,
) {
context.registry.addClassDeclaration(
(node) {
final changeBuilder = reporter.createChangeBuilder(
message: 'Add isClosed check', priority: 1);
},
);
}
}


参考