- # Dart-自定义Lint之路(一)-创建Analyzer Plugin
- # Dart-自定义Lint之路(二)-运行和调试Analyzer Plugin
- # Dart-自定义Lint之路(三)-实现简单的Lint规则
上个文档中,已经实现了禁止xx为前缀的变量命名的lint规则。这种规则仅和名称有关,所以实现起来非常简单。而在Flutter代码编写时,会发现一些诸如list.length > 0被提示使用list.isNotEmpty来代替的lint规则,而你如果自定义的某个结构定义了length,然后也这么写,却并不会有提示,说明这条lint规则是针对于List、Set、Map类型的,也就说明lint规则的实现中,是能检测类型的。
那么这篇文章,介绍如何去实现一个基于类型判定的lint规则——禁止Widget嵌套过深。
预期效果
Container(
child: Container(
child: Container(
child: Container(),
),
),
);
当连续嵌套Widget超过3层时,第四层Widget进行报错提示。
类型检测
如何在Analyzer Plugin中检测类型?既然官方的lint可以做到,那么就直接去看官方lint中的实现:src/rules。里面关于类型判定的dart文件主要有5个:
-
analyzer.dart
-
ast.dart
-
dart_type_utilities.dart
-
flutter_utils.dart
-
utils.dart
这5个都是工具类性质的,所以可以直接拷贝到项目中直接使用。
flutter_utils.dart中就有数个方法判定是否是Widget、StatelessWidget、StatefulWidget等等。所以直接使用就可以了。
连续嵌套检测
搞定了Widget的类型判定,那么最后只需要检测Widget嵌套了多少层就可以了。
首先确定检查节点:
-
使用
InstanceCreationExpression的节点。因为Widget的嵌套,实际上是不停的进行实例创建。 -
类型得是Widget。不是Widget也没必要去查。
-
是叶子Widget,自身没有child或children。
接着我们在代码中找到这个节点,1和2非常好实现:
class WidgetDepthVisitor extends GeneralizingAstVisitor {
@override
visitInstanceCreationExpression(InstanceCreationExpression node) {
if (isWidgetType(node.staticType)) { //isWidgetType是flutter_utils中的顶层方法,直接使用即可
//TODO
}
return super.visitInstanceCreationExpression(node);
}
}
第3点,需要检查当前这个Widget的创建Ast语法树中,是否有child或者children参数:
///检查是否有child或children或body或home
bool hasChildOrChildren(ArgumentList node) {
for (var arg in node.arguments) {
var name = (arg as NamedExpression).name.label.name;
if (name == 'child' || name == 'body' || name == 'home') {
if (arg.childEntities.any((element) => element is NullLiteral)) {
return false;
}
return true;
} else if (name == 'children') {
ListLiteral? list;
try {
list = arg.childEntities
.firstWhere((element) => element is ListLiteral) as ListLiteral;
if (list.toSource() == '[]') {
return false;
}
} catch (_) {}
return true;
}
}
return false;
}
这样就能找到叶子Widget节点,然后就是回溯向上检测嵌套层数,实现逻辑也比较简单,就是连续不停向上检查node是否是InstanceCreationExpression然后又是Widget类型的。
int getContinuousWidgetDepths(AstNode? node) {
int depth = 0;
while (node != null && node.parent != null) {
if (node is InstanceCreationExpression) {
if (isWidgetType(node.staticType)) {
depth++;
}
}
node = node.parent;
}
return depth;
}
获取到Depths后,根据规则,超过3个,则加入issues中,后面交给Plugin进行报错处理即可:
@override
visitInstanceCreationExpression(InstanceCreationExpression node) {
if (isWidgetType(node.staticType)) {
//如果是Widget的类型,那么检查是否是叶子结点
bool leafNode = !hasChildOrChildren(node.argumentList);
if (leafNode) {
//如果是叶子Widget,那么检查嵌套的Widget深度
int widgetDepth = getContinuousWidgetDepths(node);
if (widgetDepth > 3) {
//mirrorLog.info('存在Widget深度超过3');
issues.add(Issue(node.offset, node.length));
}
}
}
return super.visitInstanceCreationExpression(node);
}