mcpField
L
lint-and-style-config

lint-and-style-config

github.com/zakariaf开发63147 次安装5 周前更新
代码仓库

关于 lint-and-style-config

面向 Dart 和 Flutter 项目的代码规范配置技能,指导维护 `analysis_options.yaml` 及相关检查门禁。重点是固定规则集版本、将静默类错误提升为失败,并确保 CI 执行构建。

主要功能

  1. 01固定 very_good_analysis 版本
  2. 02配置分析器与 lint 规则
  3. 03验证规则实际生效
  4. 04同步生成文件排除与覆盖率
  5. 05规范抑制项并要求 CI 构建

适用场景

  1. 01新增或调整 Dart lint 规则
  2. 02升级 SDK 或规则集版本
  3. 03排查分析器配置未生效
  4. 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.yaml
callback_future_hole.dart
references/
config-mechanics.md
rule-catalog.md
scripts/
lint-gates.sh
verify-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。

不容妥协的规则

  1. 空白格式只由 dart format 决定。 不要手动格式化,不要与它争辩;除非是附有说明注释的手工排版表格,否则不要使用 // dart format off。CI 会运行 dart format --set-exit-if-changed .。
  2. 以 very_good_analysis 为基础,并固定到带版本号的 include。 绝不要使用不带版本号的 analysis_options.yaml——pub upgrade 不得悄然改变错误的判定标准。VGA 已设置 strict-casts、strict-inference、strict-raw-types;不要在 language: 块中重复声明,否则冗余配置会逐渐偏离并产生冲突。
  3. info 也算失败。 CI 会运行 dart analyze --fatal-infos --fatal-warnings。未处理的 info 会不断累积成噪声,掩盖真正的问题。
  4. 只将会悄然引入问题的错误类别提升为 error——丢弃 Future、吞掉失败、await 后使用已失效的上下文、资源泄漏、dynamic 边界处的类型漏洞、未穷尽的枚举 switch、print。不要提升风格偏好。
  5. 只有基础规则集已启用某项规则时,errors: 中的提升才会生效。 errors: 只能重新分级已有诊断,不能启用当前关闭的规则。要启用 VGA 未包含的规则,必须先将其添加到 linter: rules: 下。
  6. 不要在 linter: rules: 中混用列表和映射形式。 在同一个块中混用 - rule 项和 rule: true 条目会导致解析错误,分析器失效,而构建却显示绿色。要禁用基础规则,请使用 errors: <rule>: ignore。
  7. CI 必须执行构建,而不能只做分析。 sealed switch 的穷尽性检查是编译错误,不是 lint;不能指望 errors: 覆盖能屏蔽它,任何执行到 dart compile 的代码路径仍会在此处失败——只做分析的门禁可能会对无法构建的代码放行。
  8. 排除生成代码,并在覆盖率过滤中使用完全一致的 glob。 否则生成文件会拉低覆盖率数值,使其失去意义。
  9. 屏蔽规则只能作用于单行,并须在同一行或上一行注明原因。 绝不要对已提升的规则使用 // ignore_for_file:;绝不要屏蔽架构或导入门禁。优先修复代码。
  10. 绝不要添加 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 — sealed Failure/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 引入项。

参考资料

评论

支持 Markdown