Skip to content

Commit 0d17f37

Browse files
committed
fix(lint): make the analyzer actually enforce the declared lint rules
`mise-tasks/analyze.sh` ran `flutter analyze --no-fatal-infos`, so only the seven rules promoted to `warning` in `analysis_options.yaml` could fail. Everything else — including `prefer_const_constructors` and `prefer_single_quotes`, which AGENTS.md advertised as enforced — was advisory, and 9 live violations had already accumulated. Drop the flag so every declared rule is fatal, and turn on the three strict analyzer modes plus four lint rules the code already satisfied, so that property is held rather than merely observed: - `strict-inference`, `strict-raw-types` (alongside existing `strict-casts`) - `avoid_dynamic_calls`, `unawaited_futures`, `use_super_parameters`, `prefer_relative_imports` Fixing the resulting 50 findings is mechanical and behaviour-preserving: explicit type arguments on collection literals and `PopupMenuButton`, `Future<void>.delayed`, null-aware map elements, `unawaited(...)` on fire-and-forget futures, and `const` where it was already implied. Note the issue measured only `lib/` — all 50 remaining findings were in `test/`, and `lib/` needed just the six one-line fixes it predicted. Fixes #524
1 parent 93391b3 commit 0d17f37

25 files changed

Lines changed: 90 additions & 76 deletions

.claude/skills/build-and-env/SKILL.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ different version than CI. Always `./bin/mise run <task>` or
4343
task triggers `flutter pub get` automatically.
4444
2. `generate``dart run build_runner build --delete-conflicting-outputs`,
4545
producing `.mocks.dart` files consumed by `analyze`/`test`.
46-
3. `flutter analyze --no-fatal-infos` and `flutter test`.
46+
3. `flutter analyze` and `flutter test`.
4747

4848
Installation of `flutter=3.44.0`, `node=22`, `shellcheck=0.9.0`, `shfmt=3.8.0`
4949
(`mise.toml:18-22`) happens transparently the first time any task needs them —
@@ -219,7 +219,7 @@ Currently every task under `analyze`, `test`, `coverage`, `dev`,
219219
|---|---|---|
220220
| `flutter pub get` | automatic (`[deps.flutter] auto=true`) | none |
221221
| `dart run build_runner build --delete-conflicting-outputs` | `./bin/mise run generate` | none |
222-
| `flutter analyze --no-fatal-infos` | `./bin/mise run analyze` | none |
222+
| `flutter analyze` | `./bin/mise run analyze` | none |
223223
| `flutter test --coverage` | `./bin/mise run coverage` | none |
224224
| `flutter test` | `./bin/mise run test` | none |
225225
| `flutter build web --release --base-href "/" --source-maps` + 5 `--dart-define`s | `MISE_ENV=dev ./bin/mise run build:web:prod` | **mise's `build:web:prod` (`mise-tasks/build/web/prod.sh`) does NOT pass `--source-maps`** — CI adds it, builds, then strips the `.map` file into a separate artifact before uploading the web build. Run the `flutter build web ... --source-maps` command by hand (see AGENTS.md "Debugging Flutter Web Crashes") when you need a local source map. |

.claude/skills/diagnostics-and-tooling/SKILL.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ like the bug" — grep the log, decode the stack, read the coverage line.
1919

2020
`./bin/mise run test [path]` and `./bin/mise run analyze [path]` are thin
2121
wrappers (`mise-tasks/test.sh`, `mise-tasks/analyze.sh`) around `flutter test`
22-
/ `flutter analyze --no-fatal-infos`. Both:
22+
/ `flutter analyze`. Both:
2323

2424
1. Print a log path FIRST, before running anything: `TEST_LOG=/tmp/test-XXXXXX.log`
2525
or `ANALYZE_LOG=/tmp/analyze-XXXXXX.log` (mktemp-generated).
@@ -62,8 +62,8 @@ ANALYZE_LOG=/tmp/my-analyze.log ./bin/mise run analyze lib/screens/
6262
N.Ns)` (good — verified live against `lib/utils/` and `lib/constants/`), or
6363
one line per finding, bullet-separated:
6464
` info • <message> • <file>:<line>:<col> • <lint_rule_name>`
65-
(severity is `info`, `warning`, or `error`; `--no-fatal-infos` means `info`
66-
findings don't fail the task, `warning`/`error` do).
65+
(severity is `info`, `warning`, or `error`; since #524 the task runs
66+
without `--no-fatal-infos`, so **every** severity fails it).
6767

6868
### Env override reference
6969

@@ -315,9 +315,8 @@ severities, not just the pass/fail:
315315
grep -n 'warning\|error' "$ANALYZE_LOG" # path printed by the task
316316
```
317317

318-
A clean run with zero `warning`/`error` lines (infos are non-fatal, per
319-
`--no-fatal-infos`) is the closest thing to a "not too complex" signal this
320-
repo has.
318+
A clean run with zero findings at any severity is the closest thing to a
319+
"not too complex" signal this repo has.
321320

322321
---
323322

.claude/skills/validation-and-qa/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ to paint that delegation line green.
542542
|---|---|---|
543543
| Coverage (project) | ≥70%, 1% threshold | `codecov.yml` `coverage.status.project.default` |
544544
| Coverage (patch) | ≥70%, 1% threshold | `codecov.yml` `coverage.status.patch.default` |
545-
| Analyzer | clean at `flutter analyze --no-fatal-infos` | CI `analyze` job / `./bin/mise run analyze` |
545+
| Analyzer | clean at `flutter analyze`infos are fatal too (#524) | CI `analyze` job / `./bin/mise run analyze` |
546546
| Dart format | `dart format --output=none --set-exit-if-changed .` | CI `fmt` job (`dart:format:check`) |
547547
| Prettier (JS/TS) | `prettier --check` | CI `fmt` job |
548548
| Shell scripts | `shfmt -d` clean | CI `fmt` job |

AGENTS.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,9 @@ Container(
200200
)
201201
```
202202

203-
**Linter rules enforced** (among others): `prefer_const_constructors`, `prefer_const_declarations`, `prefer_final_locals`, `prefer_final_fields`, `avoid_print`, `prefer_single_quotes`, `sort_child_properties_last`, `use_key_in_widget_constructors`.
203+
**Linter rules enforced**`analyze` runs `flutter analyze` *without* `--no-fatal-infos`, so **every** rule in `analysis_options.yaml` fails the build, at any severity. There are no advisory lints. Among them: `prefer_const_constructors`, `prefer_const_declarations`, `prefer_final_locals`, `prefer_final_fields`, `avoid_print`, `prefer_single_quotes`, `sort_child_properties_last`, `use_key_in_widget_constructors`, `use_super_parameters`, `unawaited_futures`, `avoid_dynamic_calls`, `prefer_relative_imports`.
204+
205+
The analyzer also runs in `strict-casts`, `strict-inference` and `strict-raw-types` mode. In practice that means: no bare `[]` / `{}` literal whose element type can't be inferred (write `<Map<String, dynamic>>[]`), no raw generic type (`PopupMenuButton``PopupMenuButton<String>`), and every `Future` either `await`ed or wrapped in `unawaited(...)` — in tests too. This applies to `test/` exactly as it does to `lib/`.
204206

205207
### Patterns
206208

analysis_options.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ include: package:flutter_lints/flutter.yaml
33
analyzer:
44
language:
55
strict-casts: true
6+
strict-inference: true
7+
strict-raw-types: true
68
errors:
79
dead_code: error
810
unused_element: warning
@@ -26,3 +28,10 @@ linter:
2628
cascade_invocations: true
2729
require_trailing_commas: true
2830
unnecessary_lambdas: true
31+
use_super_parameters: true
32+
# Async correctness — every future is awaited or explicitly unawaited()
33+
unawaited_futures: true
34+
# Type safety
35+
avoid_dynamic_calls: true
36+
# Imports
37+
prefer_relative_imports: true

lib/screens/about_screen.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ class _AboutScreenState extends State<AboutScreen> {
459459
}
460460

461461
void _showThemeSelector(BuildContext context, BeerProvider provider) {
462-
showModalBottomSheet(
462+
showModalBottomSheet<void>(
463463
context: context,
464464
builder: (context) => ThemeSelectorSheet(provider: provider),
465465
);

lib/screens/drink_detail_screen.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ class _DrinkDetailScreenState extends State<DrinkDetailScreen>
9797
Future<void> _logTasting(BeerProvider provider, Drink drink) async {
9898
final messenger = _messengerKey.currentState;
9999
unawaited(HapticFeedback.mediumImpact());
100-
_pulseController.forward(from: 0);
100+
unawaited(_pulseController.forward(from: 0));
101101

102102
// addTasting returns the exact timestamp it logged, so Undo removes that
103103
// precise pour rather than guessing at the newest event.

lib/widgets/drink_filter_sheets.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ void showVisibilityFilter(BuildContext context) =>
2424
_showSheet(context, (_) => const VisibilityFilterSheet());
2525

2626
void _showSheet(BuildContext context, WidgetBuilder builder) {
27-
showModalBottomSheet(
27+
showModalBottomSheet<void>(
2828
context: context,
2929
isScrollControlled: true,
3030
builder: builder,

lib/widgets/festival_menu_sheets.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ void showFestivalBrowser(BuildContext context) {
1818
} catch (_) {
1919
// GoRouterState unavailable (e.g., in tests)
2020
}
21-
showModalBottomSheet(
21+
showModalBottomSheet<void>(
2222
context: context,
2323
isScrollControlled: true,
2424
builder: (context) =>
@@ -29,7 +29,7 @@ void showFestivalBrowser(BuildContext context) {
2929
/// Shows the settings modal with theme selector
3030
void showSettingsSheet(BuildContext context) {
3131
final provider = context.read<BeerProvider>();
32-
showModalBottomSheet(
32+
showModalBottomSheet<void>(
3333
context: context,
3434
builder: (context) => SettingsSheet(provider: provider),
3535
);
@@ -477,7 +477,7 @@ class SettingsSheet extends StatelessWidget {
477477
}
478478

479479
void _showThemeSelector(BuildContext context, BeerProvider provider) {
480-
showModalBottomSheet(
480+
showModalBottomSheet<void>(
481481
context: context,
482482
builder: (context) => ThemeSelectorSheet(provider: provider),
483483
);

mise-tasks/analyze.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
set -uo pipefail
66
ANALYZE_LOG="${ANALYZE_LOG:-$(mktemp /tmp/analyze-XXXXXX.log)}"
77
echo "ANALYZE_LOG=$ANALYZE_LOG"
8-
flutter analyze --no-fatal-infos "$@" 2>&1 |
8+
flutter analyze "$@" 2>&1 |
99
grep -v -E "Woah! You appear|superuser privileges" |
1010
tee "$ANALYZE_LOG"
1111
EXIT_CODE=${PIPESTATUS[0]}

0 commit comments

Comments
 (0)