关于 lint-and-style-config
面向 Dart 和 Flutter 项目的代码规范配置技能,指导维护 `analysis_options.yaml` 及相关检查门禁。重点是固定规则集版本、将静默类错误提升为失败,并确保 CI 执行构建。
主要功能
- 01固定 very_good_analysis 版本
- 02配置分析器与 lint 规则
- 03验证规则实际生效
- 04同步生成文件排除与覆盖率
- 05规范抑制项并要求 CI 构建
适用场景
- 01新增或调整 Dart lint 规则
- 02升级 SDK 或规则集版本
- 03排查分析器配置未生效
- 04设置分析、格式化与构建门禁
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ zakariaf/ flutter-skills --skill lint-and-style-config安装后在 Claude Code 中输入/lint-and-style-config确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
examples/analysis_options.yamlcallback_future_hole.dartreferences/config-mechanics.mdrule-catalog.mdscripts/lint-gates.shverify-include-pin.sh详细介绍
Lint 与风格配置
风格不是个人喜好,而是由工具链强制执行。空白格式由 dart format 负责,正确性由分析器负责,少数会悄然引入问题的错误类别则会被提升为导致构建失败的错误。本规范适用于 analysis_options.yaml 本身,以及决定它是否真正发挥作用的机制。每当你编辑该文件、添加或屏蔽 lint,或升级 SDK/规则集时,都应遵循本规范。
根据当前任务阅读参考资料:
references/rule-catalog.md— 每项提升规则及其对应的错误类别、刻意降级的规则、有争议的public_member_api_docs取舍,以及复杂度限制表。references/config-mechanics.md— include 固定版本陷阱、errors:重新分级与linter:启用规则的区别、sealed switch 的分析与编译差异、覆盖率过滤同步、riverpod_lint 插件,以及屏蔽规则的规范。
将 examples/analysis_options.yaml 复制到包根目录,作为起始配置。提交 PR 前运行 scripts/verify-include-pin.sh 和 scripts/lint-gates.sh。
不容妥协的规则
- 空白格式只由
dart format决定。 不要手动格式化,不要与它争辩;除非是附有说明注释的手工排版表格,否则不要使用// dart format off。CI 会运行dart format --set-exit-if-changed .。 - 以
very_good_analysis为基础,并固定到带版本号的 include。 绝不要使用不带版本号的analysis_options.yaml——pub upgrade不得悄然改变错误的判定标准。VGA 已设置strict-casts、strict-inference、strict-raw-types;不要在language:块中重复声明,否则冗余配置会逐渐偏离并产生冲突。 - info 也算失败。 CI 会运行
dart analyze --fatal-infos --fatal-warnings。未处理的 info 会不断累积成噪声,掩盖真正的问题。 - 只将会悄然引入问题的错误类别提升为 error——丢弃 Future、吞掉失败、await 后使用已失效的上下文、资源泄漏、dynamic 边界处的类型漏洞、未穷尽的枚举 switch、
print。不要提升风格偏好。 - 只有基础规则集已启用某项规则时,
errors:中的提升才会生效。errors:只能重新分级已有诊断,不能启用当前关闭的规则。要启用 VGA 未包含的规则,必须先将其添加到linter: rules:下。 - 不要在
linter: rules:中混用列表和映射形式。 在同一个块中混用- rule项和rule: true条目会导致解析错误,分析器失效,而构建却显示绿色。要禁用基础规则,请使用errors: <rule>: ignore。 - CI 必须执行构建,而不能只做分析。 sealed switch 的穷尽性检查是编译错误,不是 lint;不能指望
errors:覆盖能屏蔽它,任何执行到dart compile的代码路径仍会在此处失败——只做分析的门禁可能会对无法构建的代码放行。 - 排除生成代码,并在覆盖率过滤中使用完全一致的 glob。 否则生成文件会拉低覆盖率数值,使其失去意义。
- 屏蔽规则只能作用于单行,并须在同一行或上一行注明原因。 绝不要对已提升的规则使用
// ignore_for_file:;绝不要屏蔽架构或导入门禁。优先修复代码。 - 绝不要添加
custom_lint。 riverpod_lint 3.x 使用一方插件系统运行,其诊断会在普通的flutter analyze中触发。
include,以及会悄然禁用所有规则的陷阱
include: package:very_good_analysis/analysis_options.10.3.0.yaml
如果该文件名在解析后的包中不存在,就会出现一条 warning • include_file_not_found,分析将以零条规则运行。什么都不检查的构建也会显示绿色。
升级 SDK/VGA 时,必须在同一个提交中更新 include 文件名;确认该文件存在(ls ~/.pub-cache/hosted/pub.dev/very_good_analysis-*/lib/),并在每次升级后确认一项已知违规仍会报错——绝不要只凭绿色结果判断。scripts/verify-include-pin.sh 会自动检查文件是否存在。
将静默错误提升为错误级别
analyzer:
errors:
unawaited_futures: error
discarded_futures: error
empty_catches: error
avoid_catches_without_on_clauses: error
only_throw_errors: error
throw_in_finally: error
use_build_context_synchronously: error
cancel_subscriptions: error
close_sinks: error # VGA ships this at `ignore`; re-promoted
avoid_dynamic_calls: error
always_declare_return_types: error
cast_nullable_to_non_nullable: error
exhaustive_cases: error
avoid_print: error
avoid_slow_async_io: error
这样一来,独立开发者平时会忽略的信息级波浪线都会导致构建失败。
按规则划分的错误类别,以及刻意设为 warning/ignore 的规则
(deprecated_member_use、
invalid_annotation_target、lines_longer_than_80_chars),请参阅
references/rule-catalog.md。
errors: 会重新分级;linter: rules: 才会启用规则
# Promoting works ONLY because VGA already enables these rules (close_sinks even
# at severity `ignore` counts as enabled — the override resurrects it).
analyzer:
errors:
close_sinks: error
# To enable a rule VGA does NOT ship, add it here FIRST, then optionally promote.
linter:
rules:
some_rule_vga_omits: true
用实际违规来验证——绝不要想当然地认为提升已生效。发布的配置
不需要 linter: rules: 块,因为它提升的每条规则都已由 VGA 启用。
Sealed 类型的穷尽性检查属于编译错误,而非 lint
sealed class SaveResult {}
class Saved extends SaveResult {}
class SaveFailed extends SaveResult {}
// Missing a case is `non_exhaustive_switch_statement` — a COMPILE error.
// (A switch *expression* raises `non_exhaustive_switch_expression` instead.)
String describe(SaveResult r) {
switch (r) {
case Saved():
return 'saved';
case SaveFailed():
return 'failed';
}
}
exhaustive_cases 仅涵盖枚举和静态常量集合。将故障和模式
类型建模为 sealed,这样新增分支会在每个调用点导致构建失败,而不是
落入 default 后悄然忽略。不能指望 errors: 覆盖项能抑制穷尽性诊断;
即使能让 dart analyze 显示通过,dart compile 仍会失败——因此 CI 必须执行构建,绝不能只做分析。请参阅 dart3-idioms-and-coding-standards。
排除项也要同步到覆盖率配置
analyzer:
exclude:
- "**/*.g.dart"
- "**/*.freezed.dart"
- "**/*.drift.dart"
- "**/generated_plugin_registrant.dart"
- "test/drift/generated/**" # drift schema snapshots — never reformat/fix
相同的 glob 也必须用于覆盖率过滤(lcov --remove 或报告工具的
排除项)。排除项与覆盖率过滤器不一致,会虚增数字。请参阅
testing-strategy 和 codegen-and-toolchain。
lint 无法捕获的漏洞
// WRONG — flagged by NOTHING. The arrow returns the Future (so discarded_futures
// is satisfied), but the target is VoidCallback, so Dart drops it silently.
onPressed: () => repository.save(note),
// RIGHT — a void-returning intent method the callback holds safely.
class NoteController {
void saveNow(Note note) => unawaited(_repo.save(note).catchError(_report));
}
// onPressed: () => controller.saveNow(note),
应对措施在于结构设计,而非纪律约束。分析器显示通过,并不能证明
某项操作确实执行了;最严重的故障对所有规则都不可见,需要手动检查。
请参阅 examples/callback_future_hole.dart 和 async-safety。
反模式
- 直接使用
include: package:very_good_analysis/analysis_options.yaml——升级 pub 包会悄然改变规则集;缺少固定版本的文件则会悄然禁用规则。固定版本并验证。 - 在
language:块中重复声明strict-casts/strict-raw-types——VGA 已设置这些选项;重复配置会逐渐偏离,最终产生冲突。 - 提升 VGA 未启用的规则——
errors:无法重新启用已关闭的规则,因此提升会悄然失效。 - 在同一个
linter: rules:块中混用- rule和rule: false——导致解析错误、分析器故障,但构建仍显示通过。 - CI 只做分析——即使 sealed switch 未穷尽、导致
dart compile失败,检查仍会通过。 - 排除项未同步到覆盖率配置——生成文件会虚增数字。
- 对已提升的规则使用
// ignore_for_file:,或对导入门禁使用任何抑制——下一次编辑就不受保护;被静默关闭的正确性门禁属于阻断问题。 - 添加
custom_lint——这是旧版插件系统;riverpod_lint 3.x 不需要它。 - 重新格式化 drift 架构快照——会破坏迁移测试基线。
完成标准
-
dart format --set-exit-if-changed .检查通过;dart analyze --fatal-infos --fatal-warnings通过;CI 执行构建(而非只做分析)。 - Include 指向固定版本的 VGA 文件,并能在 pub 缓存中解析(
scripts/verify-include-pin.sh通过)。 - 没有多余的
language:块;严格模式由 VGA 提供。 - 静默错误类别已提升为
error;每条提升的规则都确实由 VGA 启用。 -
linter: rules:中没有混用列表和映射形式。 - 存在生成文件排除项,且已同步到覆盖率过滤配置。
- 任何抑制都限定在具体行并注明原因;架构门禁上没有抑制;已提升的规则上没有
// ignore_for_file:。 - riverpod_lint 已配置
version:键;没有custom_lint。
相关技能
dartdoc-conventions— 支持public_member_api_docs: error,并指出需要编写文档的接口。async-safety— 本配置无法发现的回调中丢弃 Future 的问题。error-handling-typed-results— sealedFailure/Result主干,让穷尽性检查成为编译时保证。dart3-idioms-and-coding-standards— sealed 类型和第 7 条规则背后的穷尽式 switch 规范。naming-conventions— 分工后缀和分析器不会强制执行的 Effective Dart 命名规范。state-management-riverpod— riverpod_lint 所守护的 Riverpod 模式。codegen-and-toolchain/run-codegen— 这些排除项所覆盖的生成文件。ci-pipeline-and-gates— 这些门禁和“构建而非分析”规则的运行位置。testing-strategy— 覆盖率对齐,以及绿色分析器无法证明的内容。dependency-hygiene— 作为依赖项问题的版本固定 lint 引入项。
参考资料
- Effective Dart:风格 · 用法 · 文档
- Dart linter 规则
dart formatdart analyze/ analysis_optionsvery_good_analysis·flutter_lintsriverpod_lint· Riverpod:3.0 新变化