前言

早在1979年,在贝尔实验室发表的UNIX第七版中出现了 lint,这是一个用于 C 语言静态分析的小工具,静态分析允许我们在执行代码之前发现问题, 以此来帮助开发者写出更可靠更规范的程序。

2021 了,不会还没用上吧?/狗头

Linter

Linter 衍生于 lint,关于 lint,我们来看看维基百科的解释:

在计算机科学中,lint 是一种工具程序的名称,它用来标记源代码中,某些可疑的、不具结构性(可能造成 bug )的段落。它是一种静态程序分析工具,最早适用于 C 语言,在 UNIX 平台上开发出来。后来它成为通用术语,可用于描述在任何一种计算机程序语言中,用来标记源代码中有疑义段落的工具。

基本上每种语言都有自己的 Linter,例如:Python - flake8,JavaScript - jshint,CSS - csslint,Ruby - ruby-wc。

Dart 也不例外。Dart 本身就集成了 linter 工具,通过执行命令 dart analyze 来触发分析。对于 Flutter 来说,flutter analyze 会用来分析文件。

Lint Rules

每一个 linter 必须要有相应的规则才能分析文件,Dart 为我们提供了一系列的规则(Lint Rules),分成三个组(每组只举例了几种,其他可以参考 Linter for Dart ):

errors - 可能会出现的编译错误

always_use_package_imports

Bad:

import 'baz.dart';

import 'src/bag.dart'

import '../lib/baz.dart';
复制代码

Good:

import 'package:foo/bar.dart';

import 'package:foo/baz.dart';

import 'package:foo/src/baz.dart';
复制代码

avoid_print

Bad:

void f(int x) {
  print('debug: $x');
  ...
}
复制代码

style - 代码风格问题

always_declare_return_types

Bad:

main() { }

_bar() => _Foo();

class _Foo {
  _foo() => 42;
}
复制代码

Good:

void main() { }

_Foo _bar() => _Foo();

class _Foo {
  int _foo() => 42;
}

typedef predicate = bool Function(Object o);
复制代码

avoid_returning_null_for_void

Bad:

void f1() {
  return null;
}

Future<void> f2() async {
  return null;
}
复制代码

Good:

void f1() {
  return;
}

Future<void> f2() async {
  return;
}
复制代码

pub - pub 相关规则

package_names

Bad:

JustLikeThis
12Jsl
import
复制代码

Good:

just_like_this
复制代码

常用的 Lint Rules Group

由于有些 rule 可能会相互冲突,所以一般我们只使用他的子集,例如:

应用以及自定义配置

上面介绍了分析的命令和规则,那我们怎么应用到自己的项目中去呢?

只需要我们在根目录创建一个 analysis_options.yaml 文件,在文件里面配置一些相应的规则,这样执行 flutter analyze 的时候就会按照我们指定的规则去分析文件。不同的 package 可以有不同的 analysis_options.yaml 文件

my_other_package 和 my_other_other_package 会通过 #1 分析代码,my_package 会通过 #2 分析代码。

可以看到,当前 package 没有 analysis_options.yaml 文件的时候,会在目录树中寻找其他的文件,如果最后都没找到,则会按照标准的规则去分析文件。

我们可以在 analysis_options.yaml 文件添加想要的 lint rule 或者自定义一些配置,常见的文件内容如下:

# 1
# url to bring in options from the specified URL — in this case, from a file in the lints package.
include: package:lints/recommended.yaml
# include: package:pedantic/analysis_options.1.8.0.yaml

# 2
# entry to customize static analysis — enabling stricter type checks, excluding files, ignoring specific rules, changing the severity of rules, or enabling experiments.
analyzer:
  # strong-mode:
  #   implicit-casts: false
  #   implicit-dynamic: false

  errors:
    todo: ignore
    invalid_assignment: warning
    missing_return: error
    dead_code: info

  exclude:
    - lib/src/checkout/data/ptp_checkout_repository.dart
    - lib/src/components/search_form/ui/search_form.dart

# 3
# entry to configure linter rules.
linter:
  rules:
    - avoid_empty_else
    # - avoid_function_literals_in_foreach_calls
    - avoid_print
    - avoid_relative_lib_imports
    - avoid_returning_null_for_future
    - prefer_const_constructors
复制代码

include

# url to bring in options from the specified URL — in this case, from a file in the lints package.
include: package:lints/recommended.yaml
# include: package:pedantic/analysis_options.1.8.0.yaml
复制代码

可以直接引入一些 lint rules,例如上面常用的 Lint Rules Group。

⚠️ 注意

当前文件重写 analyzer: 时,即使 analyzer: 下什么都不写,都会整体覆盖 include 的文件的 analyzer:

当前文件重写 rules: 会和 include 文件的 rules: 取并集,当某条具体 rule 存在多个文件时,则以当前文件的值为准。

analyzer

# entry to customize static analysis — enabling stricter type checks, excluding files, ignoring specific rules, changing the severity of rules, or enabling experiments.
analyzer:
  strong-mode:
    implicit-casts: false
    implicit-dynamic: false

  errors:
    todo: ignore
    invalid_assignment: warning
    missing_return: error
    dead_code: info

  exclude:
    - lib/src/checkout/data/ptp_checkout_repository.dart
    - lib/src/components/search_form/ui/search_form.dart
复制代码

strong-mode

Dart type system 更严格的类型检查:

# 隐式转换
implicit-casts: false
# 隐式 dynamic
implicit-dynamic: false
复制代码

errors

每一个analyzer error codelinter rule 都有默认的等级:

  • info

    An informational message that doesn’t cause analysis to fail. Example: dead_code

  • warning

    A warning that doesn’t cause analysis to fail unless the analyzer is configured to treat warnings as errors. Example: invalid_null_aware_operator

  • error

    An error that causes analysis to fail. Example: invalid_assignment

你可以修改他们的等级,甚至直接忽略:

errors:
  todo: ignore
  invalid_assignment: warning
  missing_return: error
  dead_code: info
复制代码

exclude

可以统一配置忽略某些文件:

exclude:
  - lib/src/file1.dart
  - lib/src/components/file2.dart
复制代码

!所有基于 analysis_server 分析文件的工具都将忽略 exclude 下所添加的文件。

也可以去某个文件里面单独配置:

// ignore_for_file: unused_import, unused_local_variable

// ignore: invalid_assignment
int x = '';

int x = ''; // ignore: invalid_assignment
复制代码

linter

添加一些规则

# entry to configure linter rules.
linter:
  rules:
    - avoid_empty_else
    # - avoid_function_literals_in_foreach_calls
    - avoid_print
    - avoid_relative_lib_imports
    - avoid_returning_null_for_future
    - prefer_const_constructors

# 另外一种写法,可用于重写 include 文件存在的规则
linter:
  rules:
    avoid_empty_else: true
    avoid_print: false
复制代码

其他规则可以参考 all lint rules

一些问题