saropa_lints 14.4.2 copy "saropa_lints: ^14.4.2" to clipboard
saropa_lints: ^14.4.2 copied to clipboard

2134 custom lint rules with 254 quick fixes for Flutter and Dart. Static analysis for security, accessibility, and performance.

Changelog #

                                    ....
                             -+shdmNMMMMNmdhs+-
                          -odMMMNyo/-..``.++:+o+/-
                       /dMMMMMM/               `````
                      dMMMMMMMMNdhhhdddmmmNmmddhs+-
                      /MMMMMMMMMMMMMMMMMMMMMMMMMMMMMNh/
                    . :sdmNNNNMMMMMNNNMMMMMMMMMMMMMMMMm+
                    o     ..~~~::~+==+~:/+sdNMMMMMMMMMMMo
                    m                        .+NMMMMMMMMMN
                    m+                         :MMMMMMMMMm
                    /N:                        :MMMMMMMMM/
                     oNs.                    +NMMMMMMMMo
                      :dNy/.              ./smMMMMMMMMm:
                       /dMNmhyso+++oosydNNMMMMMMMMMd/
                          .odMMMMMMMMMMMMMMMMMMMMdo-
                             -+shdNNMMMMNNdhs+-
                                     ``

Made by Saropa. All rights reserved.

Learn more at https://saropa.com, or mailto://dev.tools@saropa.com

2100+ custom lint rules with 250+ quick fixes for Flutter and Dart — static analysis for security, accessibility, performance, and library-specific patterns. Includes a VS Code extension with Package Vibrancy scoring.

Packagepub.dev/packages/saropa_lints

Releasesgithub.com/saropa/saropa_lints/releases

VS Code Marketplacemarketplace.visualstudio.com/items?itemName=saropa.saropa-lints

Open VSX Registryopen-vsx.org/extension/saropa/saropa-lints


14.4.2 #

Static analysis fixes. log

Fixed #

  • Fix 41 static analysis issues flagged by pub.dev pana scoring (unnecessary null checks, unused imports, missing curly braces, dead code, redundant ignores, invalid overrides)

14.4.1 #

Ignore: Published build error - mixed code/versions. Ignore.


14.4.0 #

Introduces a new lint rule to catch invalid date initializations that would otherwise silently roll over into incorrect dates. Developers are now guided toward strict parsing methods to make date handling safer across Dart and Flutter projects. log

Added #

  • avoid_datetime_constructor — flags DateTime() and DateTime.utc() constructors, which silently roll over out-of-range values (e.g. month 13 becomes January of the next year). All-literal in-range calls are allowed. Quick fix available: replace with DateTime.tryParse(). No action required.
  • avoid_datetime_constructor_unvalidated — flags DateTime() calls whose result is consumed directly (returned, passed as argument, used in field initializer) without being assigned to a local variable where components can be validated. No action required.

14.3.13 #

Fix false positive in avoid_bluetooth_scan_without_timeout — the rule no longer fires on non-Bluetooth scan() calls. log

Fixed #

  • avoid_bluetooth_scan_without_timeout no longer flags scan().listen() on non-Bluetooth receivers (e.g. dedup scanners, port scanners). No action required.
  • require_bluetooth_state_check now recognizes additional Bluetooth package types (flutter_reactive_ble, bluetooth_low_energy, quick_blue, universal_ble). No action required.
  • avoid_bluetooth_scan_without_timeout skips files without scan-related strings via requiredPatterns pre-filter, reducing unnecessary AST traversal. No action required.

14.3.12 #

Re-release of v14.3.10 with a build fix — no rule or extension changes. log

Maintenance
  • Fix test compilation error that blocked the v14.3.10 publish pipeline. No action required.
  • Extract shared parseMethodBody test helper and add CI guard against childEntities usage on class-like declarations. No action required.

14.3.11 #

Skipped: Internal build only.


14.3.10 #

Resolves false positives across matrix scaling operations and resource disposal lints. Uniform scaling factors in matrix transformations are no longer incorrectly flagged as duplicate arguments, and cleanup rules now properly recognize cascade syntax when disposing of controllers, streams, and timers. log

Fixed #

  • Fix: no_equal_arguments false positive on Matrix4 uniform scalingscaleByDouble(s, s, 1, 1), scale(s, s, 1), and diagonal3Values(s, s, 1) no longer flag the repeated factor as a copy-paste error. The scale exemption is receiver-type-guarded to Matrix4 only, so myWidget.scale(x, x) still fires.
  • Fix: disposal rules false positive on cascade syntax — all disposal/cleanup rules (require_text_editing_controller_dispose, require_page_controller_dispose, stream/timer cancel rules, etc.) now recognize _field..dispose() and _field..close() cascade expressions as valid cleanup. Previously only _field.dispose() and _field?.dispose() were matched.
Maintenance
  • Fix cascade cleanup test helper to use ClassDeclaration.body.members instead of childEntities, which stopped exposing MethodDeclaration in analyzer 12.1.0.

14.3.9 #

Two new comprehensive rules help monitor native bridge performance by requiring the @MethodChannelInstrumented annotation on channel classes and ensuring those calls are wrapped in timing helpers like noteIfSlow. log

Added #

  • New rule: require_method_channel_instrumented — flags classes that call MethodChannel.invokeMethod / invokeListMethod / invokeMapMethod without a @MethodChannelInstrumented annotation, one diagnostic per class. Quick fix inserts the annotation. Comprehensive tier.
  • New rule: prefer_method_channel_note_if_slow — flags bare invoke-method calls inside @MethodChannelInstrumented classes that are not wrapped in noteIfSlow or an equivalent timing helper. Comprehensive tier.
Maintenance
  • Fix: rule packs UI in self-package — the Config Dashboard's "Enable all" and individual pack toggles produced misleading toasts ("already enabled" / "could not write") when the workspace is the saropa_lints package itself. The extension now detects the self-package via name: saropa_lints in pubspec, treats the implicit plugin load as configured, and creates a plugins: saropa_lints: block when no anchor exists for rule_packs writes.
  • CI: full clone for test job — the health_history_test needs git tags; shallow CI clones lacked them. Changed to fetch-depth: 0 (full clone) so tags and history are always available.
  • Security: fix 3 Dependabot alerts — upgraded shell-quote 1.8.4 → 1.10.0 (quadratic DoS in parse()), replaced abandoned npm-run-all with maintained npm-run-all2@8, and overrode brace-expansion to patched versions (exponential DoS). All dev-only dependencies.
  • Dependabot: grouped weekly schedule — added .github/dependabot.yml to batch all extension npm security updates into a single weekly PR (Mondays) instead of one PR per alert.
  • i18n engine: NLLB → Qwen — the extension's machine-translation pipeline now uses Qwen 3 via local Ollama as the primary engine, with Google Translate as the per-string fallback. NLLB is deprecated; existing NLLB-provenance translations are treated as low-quality and re-translated on the next --mode upgrade run. No user action required.
  • i18n: LLM control-token rejection — the translation cache validator now rejects cached strings containing leaked LLM control tokens (/no_think, <|endoftext|>, [INST], etc.). Contaminated entries auto-heal on the next translation run. GPU detection is deferred to first use so importing the engine no longer runs nvidia-smi.

14.3.8 #

Fixes an issue where the analysis server repeatedly restarted the plugin isolate, causing IDE diagnostic results to clear continuously. Automatically excludes common non-Dart output directories during initialization to prevent file-watcher feedback loops. Adds restart-rate telemetry, log rotation, and a configurable log_level setting to control plugin log verbosity. log

Fixed #

  • Plugin isolate restart storm — the analysis server respawned the plugin isolate hundreds of times per day (13,660 over 91 days on the contacts project), clearing all diagnostics from the Problems tab each time. Two causes addressed: (1) Plugin.start() now skips config loading when the working directory is not a Dart project (e.g. the VS Code install directory), eliminating the 0-rules phase and noisy log entries; (2) PluginLogger.setProjectRoot() now validates that the root contains pubspec.yaml before writing log files, preventing log writes into non-project directories that could trigger file-watcher restarts.
  • Init command: non-Dart directories now excluded from analyzerdart run saropa_lints:init and the headless config writer now ensure common non-Dart directories (reports/**, docs/**, bugs/**, plans/**, doc/**, output/**, tmp/**) are in the analyzer > exclude list. Without this, plugin log writes to reports/.saropa_lints/ could trigger the analysis server's file watcher and restart the plugin isolate in a feedback loop.
  • Plugin logger: restart-rate telemetry — after each isolate spawn, PluginLogger counts recent "session started" entries in the log file. When the rate exceeds 10 restarts in 10 minutes, a WARNING line is emitted with remediation advice. The log file itself is the durable counter since statics reset per isolate.
  • Init command: flow-style YAML guardensureNonDartExcludes now detects flow-style exclude: [...] under the analyzer: section and leaves it unchanged instead of inserting a duplicate exclude: key. Trailing comments after exclude: are also handled correctly.
  • Plugin logger: log rotationplugin.log is now capped at 512 KB; oldest content is discarded at each isolate start, bounding the cost of the restart-rate telemetry read and preventing unbounded disk growth. No action required.

Added #

  • Plugin logger: configurable log level — new log_level: key under plugins > saropa_lints in analysis_options.yaml controls which messages are written to plugin.log. Valid values: off, error, warning, info (default), debug. Messages below the configured level are still sent to the analysis server's developer log but skip the user-visible file. The init command writes log_level: info by default.
  • Plugin logger: convenience APIPluginLogger.debug(), .warning(), and .error() replace the level: named parameter pattern, making log call sites more concise. Unrecognized log_level values now emit a warning instead of silently falling back to info. Tab-indented configs are now parsed correctly.

14.3.7 #

Updates the Dio linting behavior to favor dependency injection and factory patterns over static singletons. The updated rule flags top-level and static Dio declarations while permitting instantiation inside methods, constructors, and callbacks, resolving an architectural contradiction with anti-singleton guidelines. log

Changed #

  • Breaking: Renamed require_dio_singleton to require_dio_factory — the rule now flags Dio() in static fields and top-level variables (the singleton anti-pattern) instead of recommending them. Dio() inside methods, constructors, closures, and DI callbacks is allowed. Resolves the architectural contradiction with avoid_singleton_pattern (#274). No action required if already using factory/DI patterns.
  • require_dio_factory config alias: Projects using require_dio_singleton in analysis_options.yaml continue working via configAliases — no config migration required on upgrade.

Added #

  • Hardened require_dio_factory detection: Added coverage for late static final Dio fields, static getters, nested closures, and mixin method bodies. No action required.
Maintenance
  • Closed Dependabot PR #271 bug (js-yaml 4.1.1 → 4.3.0): lock file already resolves to 4.3.0 via mocha; archived as fixed.
  • Publish audit now detects dangling bugs/*.md references in active documents (skips frozen plans/history/).

14.3.6 #

Removes the avoid_debug_print rule, which contradicted the existing prefer_debug_print and left no valid console output path. Also fixes false positives in avoid_redundant_null_check and avoid_redundant_await when types are nullable or resolve across package boundaries. A new --debug-rule flag on the scan CLI traces type resolution for any named rule, making it easier to diagnose false positives. log

Removed #

  • avoid_debug_print rule deleted. The rule contradicted prefer_debug_print — one said "use debugPrint," the other said "don't" — leaving no valid console output function for projects without a custom logging wrapper. prefer_debug_print remains and covers the print()debugPrint() upgrade path. No action required unless your config explicitly enabled avoid_debug_print; if so, remove the entry.
  • CommentOutDebugPrintFix quick fix deleted (was the only fix for the removed rule). No action required.

Added #

  • --debug-rule <name> flag for the scan CLI. Emits per-node type-resolution trace output (staticType, staticInvokeType, returnType) for the named rule during a scan. Use with --resolve for full type information. Designed for diagnosing false positives caused by type-resolution divergence in the analyzer plugin context. No action required.

Fixed #

  • avoid_redundant_null_check no longer fires on variables, parameters, fields, or getters declared with a nullable type (Type?). The rule cross-checks the element's declared type against the resolved staticType and guards against InvalidType from failed type resolution, preventing false positives in cross-package contexts.
  • avoid_redundant_await no longer fires on await of static methods returning Future<T>. The rule now guards against InvalidType (unresolvable types) and falls back to checking the invoked method signature's return type via staticInvokeType when staticType fails to resolve for cross-file static invocations.

14.3.5 #

This update improves the precision of our accessibility lints by isolating Flutter UI components from lower-level graphics classes. Projects utilizing external image processing libraries alongside Flutter will no longer experience irrelevant warnings. log

Added #

  • isFlutterWidgetNamed(Element?, String) shared utility for verifying a resolved element is a Flutter SDK widget by name and library origin, with TypeAliasElement unwrapping.

Fixed #

  • require_image_semantics, require_image_description, require_accessible_images no longer fire on non-Flutter classes named Image (e.g. package:image's pixel-buffer Image or dart:ui's Image). All three rules now verify the declaring library is package:flutter/ before reporting, with TypeAliasElement unwrapping for typedef'd widget references.

14.3.4 #

Adds a cross-tool data channel so sibling Saropa Suite tools can pull this project's daily health snapshot, adds a validated fresh_code risk flag to the Code Health report, and revives a batch of lint rules that never fired for anyone: seven that were missing their most common bad-code shape, and fourteen whole-file rules (desktop, BLoC, Riverpod, iOS, testing, navigation, i18n, and animation checks) that reported through an end-of-file step the analysis engine ignored. Also fixes a broken age signal that scored every function as maximally stale. No action required — the API is opt-in and the new flag and fixes take effect automatically. log

Added #

  • fresh_code flag in the Code Health (vibrancy) report. Functions with cyclomatic complexity above 10 whose body was written or rewritten within the last 90 days are now flagged, because validation against real bug-fix history showed recently rewritten complex code causes incidents far more often than old code. No action required — the flag appears in the CLI report and as a filterable pill in the extension's Code Health view.
  • (Extension) getDailySummary(date) on the extension's public API. Sibling Saropa Suite tools can now read this project's current health score, violation counts, and error-level trouble items for a given day via getExtension('saropa.saropa-lints').exports.getDailySummary('YYYY-MM-DD'), which resolves to a documented DailySummary (or undefined before any analysis has run). No action required — the summary is built lazily on call, reads only local analysis output, and transmits nothing.

Fixed #

  • prefer_notifier_over_state false positives eliminated. The rule matched StateProvider by scanning serialized source text, which could match unrelated identifiers containing that substring; it now checks the constructor/invocation name directly via the AST. The MethodInvocation branch is restricted to the known Riverpod factory methods (autoDispose, family) to prevent false positives from unrelated static methods. A fixture pins all three detection branches and a false-positive decoy. No action required.
  • Code Health age scores were stuck at zero. A broken decay formula scored every function with git history as maximally stale, so the age component contributed nothing to health rankings; ages now decay correctly from 100 (touched today) toward 0 over years. Overall scores rise slightly on recently maintained code — no action required.
  • prefer_list_contains now flags indexOf(x) != -1. The rule only recognized a bare 0 or -1 on the right of the comparison, but -1 is written as a negation, not a plain number, so the most common presence check — list.indexOf(x) != -1 — was never flagged. It now is. No action required.
  • avoid_map_keys_contains now flags map.keys.contains(k) on a plain variable. The rule previously matched only chained receivers (like this.map.keys.contains(k)) and missed the ordinary map.keys.contains(k) on a simple map variable — the usual shape. Its quick fix (map.containsKey(k)) now applies to those cases too. No action required.
  • avoid_unnecessary_collections now flags List.of([...])/Set.of(...)/Map.of(...). The rule missed these wrapped-literal constructors during full analysis because they are constructor calls, which analysis represents differently from the method-call shape the rule looked for. Both shapes are now flagged. No action required.
  • prefer_asmap_over_indexed_iteration now flags for (i = 0; i < list.length; i++). The rule required the loop bound to be a chained property read and missed the ordinary list.length on a plain list variable — the usual shape — so it effectively never fired. It now does. No action required.
  • require_key_for_collection now flags ListView.builder/GridView.builder during full analysis. These are constructor calls, which full analysis represents differently from the method-call shape the rule looked for, so keyless items in the most common list builders went unflagged; only a few less-common widgets were caught. All shapes are now flagged. No action required.
  • prefer_commenting_future_delayed now works during full analysis and stops flagging already-commented delays. Future.delayed is a constructor call (represented differently from a method call during full analysis), so the rule never fired for anyone; and it looked for the explanatory comment on the wrong token, so an await Future.delayed(...) with a comment above it was treated as uncommented. Both are fixed: the rule fires on uncommented delays and stays quiet when a comment precedes the statement. No action required.
  • avoid_sequential_awaits now fires. The rule registered for a callback the analysis engine silently ignores, so three or more independent sequential awaits (which could run together with Future.wait) were never flagged for anyone. It now registers correctly and reports. No action required.
  • Four more rules that never fired now work: prefer_single_exit_point, prefer_guard_clauses, require_getit_registration_order, and require_hive_adapter_registration_order. All registered through the same ignored callback as avoid_sequential_awaits, so none produced a diagnostic for anyone. All four now register correctly and report. No action required.
  • pass_correct_accepted_type now fires, and prefer_correct_identifier_length now checks parameter names. Both registered for a parameter callback the engine ignores: pass_correct_accepted_type never fired at all, and prefer_correct_identifier_length only checked variable names, silently skipping parameters. Both now register correctly. No action required.
  • Fourteen more whole-file rules that never fired now work. Each aggregated information across the whole file and then reported through an end-of-file callback the analysis engine silently ignores, so none produced a diagnostic for anyone. The revived rules are require_menu_bar_for_desktop, require_window_close_confirmation, require_error_state, avoid_circular_provider_deps, prefer_notifier_over_state, require_apple_sign_in, require_error_case_tests, avoid_test_coupling, require_test_cleanup, prefer_test_variant, require_route_transition_consistency, prefer_shell_route_for_persistent_ui, require_intl_locale_initialization, and prefer_implicit_animations. All now scan the file in a single pass and report correctly; require_intl_locale_initialization also stops missing usages that a duplicate registration had been discarding, and require_apple_sign_in now recognizes the standard GoogleSignIn().signIn() call shape (a constructor-call receiver) that its detection had been skipping. No action required.
Maintenance
  • Fixed the rule-liveness report (accuracy_report) so it exercises stylistic rules. No tier — not even pedantic — contains the stylistic rules, so the previous tier-scoped scan never enabled them and falsely reported stylistic rules with fixtures as silent; correcting it flipped 80 previously-false-silent rules to firing (the silent worklist dropped from 744 to 664). The report now defaults to all defined rules (--tier <name> narrows it), via a new optional explicit rule-set on the scan runner.
  • Repaired the collection and async rule fixtures so the liveness instrument exercises the rules that were correct but sitting on inadequate fixtures. Collection reached full coverage (all 27 rules fire). Async went from 13 silent to 4: eight fixtures made realistic (typed streams/futures, class-method context, matching heuristic identifiers, a real WebSocketChannel). The four remaining are two rules whose fixtures resolve to zero diagnostics under the full-corpus scan (cause not yet isolated with per-file tooling) and two expect_lint markers naming rules that were never implemented.
  • Added an integrity test that fails the build if any rule calls one of the three no-op registration stubs (addPostRunCallback, addFunctionBody, addFormalParameter), which silently discard their callback and were the root cause of the fourteen dead whole-file rules revived this release. The guard forces authors to the real registrations instead.
  • Repaired the liveness fixtures for the revived whole-file rules and added fixtures for three that had none (require_error_state, avoid_circular_provider_deps, prefer_notifier_over_state). Because these rules judge the whole file, a fixture that placed a BAD and a GOOD example together let the GOOD example mask the BAD; the compliant examples were moved to sibling *_good.dart files, path-gated fixtures were relocated, and mock classes (GoogleSignIn, CupertinoPageRoute, FadeTransitionRoute) were added so constructor-based rules resolve. All fourteen are confirmed firing (six in the full corpus scan, eight in isolated scans — the eight hit the pre-existing full-corpus-scan measurement limitation with the crowded test-fixture directory, already noted for the async cluster).
  • Fixed the Code Health unused flag producing ~50% false positives on multi-package repos. Nested pubspec.yaml files fragmented the analysis context, the resolved-usage pass silently degraded, and every @override method and bin/-only-called function was flagged dead. The fix scopes the analysis context to lib/, test/, bin/ (preventing fragmentation), includes bin/ files in the usage set (so CLI delegates get real caller counts), and adds a syntactic @override safety net that protects polymorphic methods even if resolution degrades. No action required.
  • Split the Issues tree provider's ~220-line tree-item renderer into a sibling module so the provider class carries only its stateful filter/index logic. Behavior-identical; the tree-item tests pin the render output.
  • Closed the oversized view-file breakdown plan and archived it to plan history — all ten tracked files are decomposed, and the two residual stateful controllers are accepted as cohesive final-state modules.
  • Ran the flight-risk scoring research gate (predictive-score validation) and recorded a negative result: on a 16-incident corpus mined from this repo's fix history, the candidate composite formula lost to the complexity-alone baseline, so the feature stays unbuilt and its plan stays open with the findings and re-attempt conditions documented.
  • Closed the sidebar-and-affordance inventory snapshot and archived it to plan history — every count had drifted from the manifest, and the one durable decision (the palette-only JSON-export tree providers are intentionally never registered as views) now lives as a comment at their construction site.
  • Fixed the loadHealthHistory test so it asserts real behavior instead of silently passing on empty results. The test had if (points.isEmpty) return, which meant a completely broken function still produced a green test. It now requires non-empty results (this repo has tags), asserts codeLoc > 0, the codeLoc <= loc invariant, and distinct tags when two points are returned.
  • Added HistoryPoint.toMarkdownRow(), HistoryPoint.markdownHeader, and HistoryPoint.toMarkdownTable() for rendering health trajectory as markdown tables.
  • Fixed the performance-rules fixture verification test: renamed require_window_close_confirmation_desktop_fixture.dart to match the rule-name convention, and added 8 fixture files that existed on disk but were missing from the verification list.
  • Replaced the hardcoded fixture list in the performance test with a directory scan, so new fixture files are verified automatically without manual list maintenance. Also renamed the stale require_window_close_confirmation_desktop_good.dart to drop the _desktop suffix.
  • Converted all 126 remaining test files from hardcoded fixture lists to the same Directory.listSync() auto-discovery pattern. Every fixture verification group now scans its directory on disk, so adding a fixture file is automatically tested — no manual list to maintain or drift out of sync. The android_rules_test retains one explicit test for a cross-directory fixture (require_android_manifest_entries in example/lib/platform/). Two files (roadmap_15_rules_test, migration_rules_test) were excluded because their fixture groups contain content-validation tests beyond simple existence checks.
  • Extracted fixture auto-discovery into a shared discoverFixtures() helper (test/helpers/fixture_discovery.dart) and migrated all 127 fixture-verification test files to use it. The helper returns an empty list when the directory is missing, so the guard test fails with a clear assertion instead of a FileSystemException aborting the group. Removes ~7 lines of duplicated listSync chain per file.
  • Added a fixture-vs-tiers integrity test (test/integrity/fixture_integrity_test.dart) that cross-references every *_fixture.dart on disk against getAllDefinedRules(). Catches stale or misspelled fixture files whose names don't match any registered rule. Group/category fixtures (covering multiple rules) are logged but not failed. Includes a regression floor at >2300 exact-match fixtures.

14.3.3 #

Adds a rule pack for device_calendar_plus, a maintained replacement for the abandoned device_calendar plugin with a different API (no relation to the existing device_calendar rule pack, which stays as-is). Also fixes the Package Dashboard's Opportunities detection so document files like README.md are never counted as an adoptable API, and adds an Opportunities section to the Package Detail sidebar with per-feature links to the package's source code and documentation. No action required — the new rules and the Opportunities fixes take effect automatically. log

Added #

  • New device_calendar_plus rule pack (3 rules). Flags data operations (create/update/delete/list) called with no permission check anywhere in the file; flags all-day events (isAllDay: true) given a UTC-converted date, which can shift the event to the wrong calendar day; and flags updateEvent calls that change no field, a no-op the package treats as silently harmless. No action required — rules run automatically wherever device_calendar_plus is imported.
  • (Extension) Opportunities section in the Package Detail sidebar. Each unadopted changelog feature now lists its introducing bullet plus, per named API, a link to search the package's source repository and a link to its online documentation, so you can review a feature without leaving the sidebar. No action required — the section appears automatically for any package with unadopted features.

Fixed #

  • (Extension) Opportunities detection no longer treats document files as adoptable APIs. A changelog bullet mentioning README.md, CHANGELOG.md, or pubspec.yaml was being extracted as if it were a dotted API reference (like ReelText.rich), so the Package Dashboard's Opportunities column and count could include filenames instead of real code. Extraction now excludes filename-shaped tokens. No action required — rescanning drops the false entries.
  • (Extension) Sidebar views now show an icon. The Banner, Editor Dashboards, Status, Settings, and Help views in the activity bar panel were missing an icon, so they rendered as unlabeled entries when moved to another panel. No action required — icons appear automatically.
Maintenance
  • Added false-positive-guard fixtures and additional UTC-shape cases for the device_calendar_plus all-day-event rule, closing a regression-coverage gap where the resolved-receiver-type check shipped in 14.3.3 had no fixture or test exercising it.
  • Fixed an inconsistent target/realTarget accessor in the device_calendar_plus UTC-taint helper's DateTime.parse branch (no behavior change — not a realistic cascade shape).

14.3.2 #

Cuts sustained editor CPU while you type. The analyzer plugin runs inside the Dart analysis server, which re-analyzes a file on nearly every keystroke; until now each pass re-ran the entire configured tier over code that was still in flux. During rapid editing the plugin now defers all of its rules until editing settles — the Dart analyzer still reports compile errors live. Full-fidelity batch analysis is unchanged. log

Fixed #

  • While a file is being rapidly edited in the editor, the analyzer plugin now defers all of its rules until editing settles, instead of re-running the configured tier on every keystroke-triggered pass over code still in flux — cutting sustained CPU during active development. Batch and CLI analysis (dart run saropa_lints scan, dart analyze) still run every rule at full fidelity, and the Dart analyzer keeps reporting compile errors live while you type. No action required; saropa_lints diagnostics reappear once editing pauses.

14.3.1 #

Fix for raised issue: https://github.com/saropa/saropa_lints/issues/269 log

Fixed #

  • The baseline generator (dart run saropa_lints:baseline) parsed dart analyze output with the wrong format matcher and so reported every project as clean, generating an empty baseline. It now reads the analyzer's diagnostic format correctly and captures real violations. Re-run the command to regenerate an accurate baseline.
  • The baseline generator no longer reports a false "clean codebase" success and exits 0 when the underlying analysis fails to run — for example when an analyzer plugin crashes and produces no output. It now detects the failed analysis, prints the error, and exits non-zero so CI cannot mistake a crash for a clean pass. No action required.

14.3.0 #

Stops the analyzer plugin from driving the Dart analysis server to a multi-GB out-of-memory hang on large projects. Because the plugin runs inside the analysis server, running rules there forces the editor to hold the project's resolved model in memory. A real-memory safety valve now pauses rule execution before the server saturates RAM, previously-inert cache eviction bounds the plugin's own footprint, and a project that has enabled no rules at all defaults to the essential set in-editor. Rules you have explicitly enabled always run as configured. log

Changed #

  • The in-editor analyzer plugin defaults to the essential rule set only when a project has configured no rules at all, so an unconfigured editor session stays light. Rules you explicitly enable (via dart run saropa_lints:init, a diagnostics: entry, or a severity override) always run in-editor exactly as configured — the memory default never silently drops an opted-in rule. An explicit SAROPA_TIER (or saropa_tier / runtime_tier) still caps as before; full coverage runs anytime out-of-process with dart run saropa_lints scan.

Fixed #

  • The plugin now pauses rule execution when the analysis-server process crosses a memory cap and resumes when it recovers, a backstop against the server saturating RAM and hanging the editor. Set SAROPA_LINTS_MAX_RSS_MB to tune the cap (0 disables it).
  • The native plugin now bounds and evicts its internal caches under memory pressure instead of retaining them for the entire analysis-server session. No action required.

14.2.4 #

Fixes a false positive in the hardcoded-API-URL rule so it no longer flags endpoints you have already moved into a named configuration constant — the exact fix the rule asks for. log

Fixed #

  • avoid_hardcoded_api_urls no longer fires on a URL that is already the value of a const/static const field, a const collection entry, or an environment-config enum default; it now flags only inline URLs at call sites. No action required — any // ignore: you added on a config file can be removed.

14.2.3 #

This is a maintenance release with no changes to lint rules or analysis behavior. It fixes the release process so the VS Code extension reliably reaches the Marketplace alongside Open VSX, and slims the published extension by dropping development-only files that were never used at runtime, so the download is smaller. log

Maintenance
  • Trimmed the VS Code extension package from 1200 files (17.7 MB) to the runtime set by excluding dev-only fixtures and outputs from .vscodeignore: UX test screenshots (test-ux/), the i18n translation-audit reports (reports/), and source maps (**/*.map). The bare *.md rule only matched the package root, so nested reports/*.md had been shipping; it is now **/*.md with the README and CHANGELOG re-included. Packaging only. No action required.
  • Fixed the publish script silently skipping the VS Code Marketplace when VSCE_PAT was unset, even though vsce held a valid stored vsce login credential. The Marketplace step now falls back to the stored credential (verified read-only with vsce verify-pat) instead of skipping, so a logged-in machine publishes to the Marketplace as well as Open VSX. Publish tooling only. No action required.

14.2.2 #

This release adds an Essential lint rule that catches a common Flutter layout crash: animating a widget's size directly inside a wrapping or flowing layout, which throws a render error on every frame once the animation starts. The rule points you to the safe alternatives so the problem is caught in the editor instead of on a device. log

Added #

  • New rule avoid_animated_size_in_wrap (Essential) flags AnimatedSize placed directly inside a Wrap or Flow. That combination throws "RenderAnimatedSize was mutated in its own performLayout" every frame once the size animates, because Wrap/Flow lay each child out within their own measurement pass while AnimatedSize re-dirties itself. Move the AnimatedSize into a Column/ListView, or put a bounded box (SizedBox/ConstrainedBox) between the two.
Maintenance
  • Excluded the regenerated Dart build output from VS Code's file watcher in .vscode/settings.json. VS Code does not skip build/ by default, so its 1.14 GB of gitignored output was crawled on every open, adding watcher and index load. Editor config only. No action required.
  • Quieted the publish flow's extension locale audit on a clean pass. With every locale fully covered it printed all ~80 lines of the per-locale table and coverage matrix as info, burying the result; a passing audit now prints only the "fully translated" confirmation and the report path. Gaps and low-quality lines still surface as warnings on a failing audit. Publish tooling only. No action required.
  • Collapsed git's per-file "CRLF will be replaced by LF" warnings during the commit step into a single "Normalized N files (CRLF -> LF)" line. A locale regen touches dozens of JSON files, each emitting one such stderr warning; they are expected (core.autocrlf is set right after) so they are now counted rather than dumped, while any unexpected stderr still prints. Publish tooling only. No action required.
  • Made temp-dir cleanup in project_vibrancy_cli_test.dart tolerate the transient Windows file lock. On Windows the analyzer briefly keeps file handles open after a scan, so the teardown's immediate deleteSync intermittently failed with PathAccessException (errno 32) and flaked the suite; cleanup now retries briefly and ignores a residual lock. Test harness only. No action required.

14.2.1 #

This release introduces a dedicated "Upgrade Opportunities" dashboard to help you discover unused features in your dependencies and instantly generate contextual upgrade prompts for AI assistants. It also adds a new lint rule to prevent runtime SQL crashes with Drift acronym columns, alongside smarter hardcoded API URL detection. Finally, the extension interface is polished with correctly translated tooltips, collapsible changelog histories, and cleaner table layouts. log

Added #

  • New rule require_named_for_acronym_drift_columns (Professional) flags Drift column getters with an acronym that omit .named(). Drift's snake_case converter inserts an underscore before every uppercase letter, so contactSaropaUUID becomes the SQL column contact_saropa_u_u_i_d — not the contact_saropa_uuid a human predicts — and raw SQL written against the expected name crashes with "no such column" at runtime. The rule is report-only because pinning a column that already shipped renames it and needs a migration; add .named('snake_case') on new acronym columns to keep source and schema in sync. No action required.

Fixed #

  • avoid_hardcoded_api_urls now flags hardcoded URLs on an api. host, not just /api paths. The detection pattern previously required /api in the URL path, so the most common shape — an api. subdomain with an ordinary path such as https://api.example.com/users — slipped through and the rule missed its own documented bad example. URLs with neither an api. host nor an /api path still pass, so ordinary links are unaffected. No action required. log

Added (Extension) #

  • New "Upgrade Opportunities" dashboard — a focused view of the dependencies you have under-adopted. Separate from the dense Package Dashboard table, it lists only packages that have changelog features your code does not yet use, ranked by relevance, and for each shows the package, description, README logo, the unused features, the exact project files that import it (click to jump to the line), and a one-click "Copy upgrade prompt for AI". Open it from the Saropa Lints sidebar ("Upgrade Opportunities", shown once a scan finds any) or the command palette ("Open Upgrade Opportunities"). No action required.
  • Package Vibrancy detail pane adds a "Copy upgrade prompt for AI" button. It assembles a ready-to-paste prompt from the package's changelog — the new features classified as adoption candidates, plus your project's own call sites for that package — so you can hand an AI everything it needs to suggest where the new features fit, instead of pasting a raw changelog. The button appears whenever a package has adoptable features; open a package and click it to copy. No action required.
  • Adoption opportunities now surface for up-to-date packages, not just outdated ones. A caret constraint quietly carries a package across releases whose new features you may never have adopted — being on the latest version does not mean you use everything it offers. The scan now mines each package's full changelog history and cross-references it against the symbols your code actually uses, so a fully up-to-date package with unused capabilities still flags features worth reviewing. No action required.
  • Package Vibrancy table gains a sortable "Opportunities" column to find the needles across many packages. Each package shows the count of changelog features it offers that your code does not yet use (the unused feature names are in the cell tooltip); sort by the column to bring the most under-adopted packages to the top. The column hides itself when nothing is unadopted. No action required.
  • Toolbar adds "Copy opportunities for AI" to triage the whole project in one paste. It bundles the AI upgrade prompts of the highest-relevance under-adopted packages into a single clipboard copy, so one AI round can review the project instead of opening each package. The button appears only when at least one package has an adoptable feature. No action required.
  • The Package Dashboard sidebar row shows a count of packages with features worth adopting. The "Package Dashboard" entry in the Saropa Lints sidebar (Editor dashboards section) now reads "… · N to adopt" after a scan, so under-used dependencies are visible without opening the dashboard. The count refreshes with each scan and clears when nothing is unadopted. No action required.

Fixed (Extension) #

  • The "Unused features" tooltip now reads cleanly in 20 non-English locales. Machine translation had appended hallucinated text after the {features} placeholder (stray sentences and leaked _PH0_ sentinel fragments), so the tooltip displayed garbage in languages such as German, Spanish, Japanese, and Russian. Each locale's value was rewritten to the plain "

Changed (Extension) #

  • Package Vibrancy dashboard moves "copy as JSON" out of every table row and into the detail pane header. The per-row clipboard icon added a column to an already-dense table; the copy button now sits next to the detail pane's close button and copies whichever package is open in the pane. Open a package to copy its JSON. No action required.
  • The package detail panel's changelog now collapses each version, with only the latest expanded. A long upgrade gap could fill the panel with every intermediate release's notes; each version is now a fold-out, opened by default only for the newest release. Click any version to expand its notes. No action required.
Maintenance
  • Added a changelog opportunity miner (engine behind the "Copy upgrade prompt for AI" button) that classifies a package's full changelog history into adoption candidates ("a new feature you could use") using text heuristics only — no AI — extracts the API names each feature introduces, and cross-references them against the symbols the project actually uses to rank what is genuinely unadopted. The project source is walked once, shared between the import scan and the symbol-usage scan. Service layer with unit tests. No action required.
  • Added a rule-liveness report (dart run saropa_lints:accuracy_report) that scans the expect_lint fixtures and flags any rule declared in a fixture but never firing there — a gap the marker-text contract tests cannot catch. Report-only; not yet wired into CI. No action required.
  • Made every api_network fixture actually exercise its rule. The bad examples were stubs — top-level functions where the rule visits class methods, or missing the package import the rule gates on — so 20 of the network rules never fired on their own fixtures. Each bad example is now a realistic class method with the required import; all 34 api_network rules now trigger. Fixtures only; no rule behavior changed. No action required.
  • Started the same fixture-adequacy pass on code_quality rules: wrapped four bad examples the rule could never see (positional/named bool params and an unnecessary override need a class method; a duplicate-const example needed top-level declarations) so the rules now trigger. Fixtures only; no rule behavior changed. No action required.
  • Stopped the publish audit's duplicated-message check from flagging correctionMessages that enumerate parallel code examples. The inline-repeat heuristic, which exists to catch a prose paragraph pasted twice into one message, fired on two share_plus rules whose messages intentionally repeat an API/call fragment across before/after migrations; repeated windows that look like code (call syntax, method chains, casts, camelCase) are now exempt while prose duplication is still caught. Audit tooling only. No action required.

14.2.0 #

Consolidates four overlapping shrinkWrap: true rules down to one. A single scrollable could be flagged by up to four differently-named diagnostics, so a site suppressed under one rule name was re-flagged under another; the redundant three are now deprecated and avoid_shrink_wrap_expensive is the canonical rule covering the whole concern. log

Changed #

  • Deprecated three redundant shrinkWrap rules in favor of avoid_shrink_wrap_expensive. avoid_shrink_wrap_in_scroll, avoid_shrink_wrap_in_lists, and avoid_shrinkwrap_in_scrollview all policed the same shrinkWrap: true concern, so one site drew up to four diagnostics and an acknowledgment under one rule name did not suppress the others; the canonical avoid_shrink_wrap_expensive flags nested and non-nested cases alike while exempting the safe NeverScrollableScrollPhysics pattern. Deprecated rules are dropped from freshly generated tier configs — re-run init or write-config to clear them, or remove them from analysis_options.yaml by hand.

Fixed #

  • prefer_static_final_for_session_constant no longer flags ThemeCommonSize or ThemeCommonFontSize arithmetic. Those getters fold the avatar-scale preference and the system text scale, so hoisting them to a static final would freeze a value the user can change and show a stale UI; the rule now treats only ThemeCommonSpace as session-constant. No action required.
  • prefer_boolean_prefixes no longer flags boolean fields whose name is a serialization or schema contract. Fields on an Isar @collection/@embedded class, a Drift @DataClassName row, or carrying @JsonKey map their Dart name to a stored property, column, or wire key, so a rename would break persisted data or desync serialization; these are now exempt while ordinary private and state booleans still flag. No action required.

Changed (Extension) #

  • Code Health usage counts and the unused flag are now resolved per declaration, not matched by name. Previously every function sharing a name pooled into one count, so a heavily-used _dispose made every other _dispose look used and hid true orphans; usage is now attributed to the exact declaration each reference binds to, and runtime entry points (main, @pragma('vm:entry-point'), framework @override lifecycle hooks) are no longer mislabeled unused. No action required; scans fall back to the prior name-based count when a project cannot be resolved.
  • Manage Rule Packs treats a package's version variants as a pick-one choice. Packs targeting different majors of the same dependency (dio vs dio 5.x, Riverpod 2 vs 3, app_links vs app_links 6.x, and similar) now carry a "Pick one version" tag and are mutually exclusive — enabling one variant turns its siblings off, and rule_packs.enabled can never list two versions of the same package at once. No action required; the lockfile already gates rules to the version you ship.
Maintenance
  • Split the extension's 1709-line Package Vibrancy report builder into a thin composer plus four focused sibling modules (shared helpers, top chrome, package table, data payloads). Behavior-preserving — the rendered report is byte-identical. No action required.
  • Split the 1356-line command-catalog registry into a types module, three per-group entry data files, and a thin composer. Behavior-preserving — the composed catalog is identical (162 entries, same order). No action required.
  • Extracted the Issues tree's node types and its command layer (hide/suppress, copy, apply-fix) out of the 1340-line issuesTree.ts into sibling modules, leaving the tree-data provider in place. Behavior-preserving; the tree's tests pass unchanged. No action required.
  • Split the two largest dashboard stylesheets (dashboardChromeStyles.ts, violationsDashboardStyles.ts) into per-section sibling modules behind thin composers. The generated CSS is byte-identical. No action required.
  • Decomposed the remaining oversized webview view files into focused sibling modules: the report stylesheet, the command-catalog webview (its CSS and client script), the Project Vibrancy / Code Health controller (its client script), the Findings wide-report stats, and the Package Vibrancy report client script. All byte-identical or test-verified. No action required.

[14.2.0] #

Adds a performance rule that flags arithmetic in a widget's build() whose operands are all fixed for the app session — number literals, constants, and design-token size getters — so the value is computed once in a static final field instead of on every frame. The Package Dashboard now shows a live progress bar while a rescan runs, so a refresh no longer looks like the page has frozen behind a lone notification. The Rule Packs sidebar gains a wave of new concern packs so every rule now belongs to a selectable pack, including cross-cutting "lens" packs that group rules by task — memory leaks, UI polish, release readiness — rather than by category. log

Added #

  • New rule prefer_static_final_for_session_constant (Professional tier, info). Flags compound expressions in build() built only from session-constant operands (literals, const fields, and design-token getters such as ThemeCommonSpace.Footer.size) that recompute on every rebuild; hoist them to a static final field, which—unlike const—works because the token getters resolve at runtime. Bare single getters and anything depending on context, widget, parameters, or locals are not flagged.

Added (Extension) #

  • The Package Dashboard shows a live progress bar during a rescan. A rescan previously updated only a VS Code notification while the dashboard sat on stale data, so it read as hung; the dashboard now fills a determinate bar as each package is scanned and clears it when results refresh. No action required.
  • The package detail pane's Upgrade and Retry buttons now show a busy state. An upgrade runs pub get plus the full test suite (minutes) and a retry re-fetches over the network, but the buttons gave no in-pane signal; they now disable, show a spinner, and relabel ("Upgrading…" / "Retrying…") until the work finishes. No action required.
  • The Saropa Dashboards launchpad now carries the full Actions, Settings, and Help controls. A control band under the hero exposes run analysis, initialize config, the lint-integration / tier / run-after / UI-language settings (each showing its current value), and the help links, so the launchpad is a complete entry point rather than only a dashboard-of-dashboards; toggling a setting updates its label in place without restarting the scans. No action required.
  • Findings can now be grouped by Tier and by Pack(s). The Findings dashboard "Group by" dropdown and the Issues view group-by picker gain two dimensions: Tier (Essential → Pedantic) and Pack(s) (ecosystem, platform, and concern packs). Pack grouping is multi-key like OWASP — a rule belonging to several packs appears under each — and findings whose rule is in no pack collect under "No pack". Both resolve from bundled rule metadata, so they work on an existing report without re-running analysis.
  • Manage Rule Packs gains rule-finding aids. Searching now lists every matching rule in a "Matching rules" panel (each linking to its explanation and to its owning pack), shows a live "N packs · M rules" count beside the box, and highlights the matched text in rule names; section and domain headers read "12 packs · 340 rules" so you can see where rules concentrate before opening a group. No action required.
  • 16 new concern packs broaden Rule Packs coverage and overlap. Thirteen coverage packs give every previously-unpacked rule file a home — Widgets & build, Layout & scrolling, Animation & motion, Dialogs & overlays, Notifications, Naming & conventions, Class & constructor design, BuildContext safety, In-app purchase, Hardware & sensors, Freezed (codegen), File I/O & handles, and Project config & integrity — and three cross-cutting "lens" packs (Memory & resource leaks, UI polish & UX, Release readiness) deliberately span categories so the same rules can be opted into through a task-shaped lens. Packs are additive, so enabling several never double-flags a shared rule.

Changed (Extension) #

  • The sidebar Actions panel merged into Settings. The Actions and Settings panels sat directly adjacent and read as duplicates, so run-analysis and initialize-config now lead the Settings panel (the title-bar play button still runs analysis); the duplicate "Pick UI language" action was dropped because the Settings "UI language" row already shows the current language and changes it on click. No action required.
  • "Saropa Dashboards" is now a launchpad for all six dashboards. It opens instantly with the page chrome and live summary cards for Lints Config, Findings, Package, and Command Catalog (each with an "Open full screen" link), then streams Project Map and Code Health in as their scans finish instead of blocking on both. Each heavy pane has its own Rescan and an inline Retry when a scan fails. The "Saropa Dashboards" row now leads the sidebar's Editor dashboards list as its entry point. No action required.
  • Manage Rule Packs merges each pack's rule count and "View" link into one "N rules" link. The table previously carried a separate count column and a separate "View" button that did the same thing; clicking the "N rules" link now both shows the count and expands the pack's rule list. No action required.

Fixed (Extension) #

  • The consolidated dashboard no longer hangs on a blank "Scanning…" screen or renders corrupted CSS. Both scans ran behind one all-or-nothing gate, and Project Map's stylesheet was double-wrapped so its theme CSS spilled onto the page as visible text and the treemap rendered blank; panes now load independently and the stylesheet is injected verbatim. No action required.
  • The Manage Rule Packs coverage gauge now fills its arc instead of showing the percentage over an empty ring. The gauge's fill level was delivered through an inline style attribute the webview's content-security policy silently dropped, so the arc stayed empty; it is now set from the page script and animates up to the score. No action required.
  • Manage Rule Packs search now finds individual rules, not just pack names. Typing a rule name (or a problem area such as "storage") surfaces the pack that owns it and expands its rule list, where previously search matched only the pack's title. No action required.
  • Toggling several rule packs in quick succession no longer stacks multiple analyses. Each toggle re-ran analysis without stopping the previous run, leaving several "Running analysis" notifications and overlapping analyzer processes; a new run now cancels the in-flight one so only the latest runs. No action required.
Maintenance
  • Removed a redundant import from the pubspec constraint parser test that dart analyze --fatal-infos flagged (unnecessary_import), unblocking the publish analysis gate.

Historical Changelog Archive #

Looking for older changes? See CHANGELOG_ARCHIVE.md for older versions.

9
likes
150
points
4.34k
downloads

Documentation

API reference

Publisher

verified publishersaropa.com

Weekly Downloads

2134 custom lint rules with 254 quick fixes for Flutter and Dart. Static analysis for security, accessibility, and performance.

Homepage
Repository (GitHub)
View/report issues
Contributing

Topics

#linter #static-analysis #code-quality #flutter #dart

License

MIT (license)

Dependencies

analysis_server_plugin, analyzer, analyzer_plugin, collection, meta, path, pub_semver, yaml

More

Packages that depend on saropa_lints