saropa_lints 5.0.0-beta.8 copy "saropa_lints: ^5.0.0-beta.8" to clipboard
saropa_lints: ^5.0.0-beta.8 copied to clipboard

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

Changelog #

All notable changes to this project will be documented in this file. The format is based on Keep a Changelog, and this project adheres to Semantic Versioning. Dates are not included in version headers — pub.dev displays publish dates separately.

Looking for older changes?
See CHANGELOG_ARCHIVE.md for versions 0.1.0 through 4.9.0.

** See the current published changelog: saropa_lints/changelog


5.0.0-beta.8 #

Changed #

  • Version bump

5.0.0-beta.7 #

Added #

  • Init log file now includes a detailed rule-by-rule report listing every rule with its status, severity, tier, and any override/filter notes

Changed #

  • Report files now write into reports/YYYYMMDD/ date subfolders instead of flat in reports/ — reduces clutter when many reports accumulate
  • --tier / --output flags without a value now warn instead of silently using defaults
  • dart run saropa_lints:init without --tier now prompts for interactive tier selection (was silently defaulting to comprehensive)

Fixed #

  • .pubignore pattern test/ was excluding lib/src/fixes/test/ from published package — anchored to /test/ so only the root test directory is excluded; this caused dart run saropa_lints:init to fail with a missing import error for replace_expect_with_expect_later_fix.dart
  • Publish script dart format step failed on fixture files using future language features (extension types, digit separators, non-ASCII identifiers) — now tolerates exit code 65 when all unparseable files are in example fixture directories

5.0.0-beta.6 #

Added #

  • Quick fix for require_subscription_status_check — inserts TODO reminder to verify subscription status in build methods
  • getLineIndent() utility on SaropaFixProducer base class for consistent indentation in fix output

Changed #

  • Moved generated export folders (dart_code_exports/, dart_sdk_exports/, flutter_sdk_exports/) and report caches from scripts/ to reports/ — scripts now write output to the gitignored reports/ directory, keeping scripts/ clean
  • Filled TODO placeholders in 745 fixture files across all example directories — core and async fixtures now have real bad/good triggering code; widget, package, and platform fixtures have NOTE placeholders documenting rule requirements
  • Expanded ROADMAP task backlog with 138 detailed implementation specs
  • Deduplicated _getIndent from 5 fix files into shared SaropaFixProducer.getLineIndent()

Fixed #

  • Audit script get_rules_with_corrections() now handles variable-referenced rule names (e.g. LintCode(_name, ...)) — previously undercounted correction messages by 1 (no_empty_block)
  • OWASP M2 coverage now correctly reported as 10/10 — audit scanner regex updated to match both single-line and dart-formatted multiline OwaspMapping getters; avoid_dynamic_code_loading and avoid_unverified_native_library (M2), avoid_hardcoded_signing_config (M7), and avoid_sudo_shell_commands (M1) were previously invisible to the scanner
  • Completed test fixtures for avoid_unverified_native_library and avoid_sudo_shell_commands (previously empty stubs)
  • Removed 4 dead references to unimplemented rule classes from registration and tier files (require_ios_platform_check, avoid_ios_background_fetch_abuse, require_method_channel_error_handling, require_universal_link_validation) — tracked as bugs/todo_001 through todo_004

5.0.0-beta.5 #

Added #

  • Auto-migration from v4 (custom_lint) to v5 (native plugin) — dart run saropa_lints:init auto-detects and converts v4 config, with --fix-ignores for ignore comment conversion
  • Plugin reads diagnostics: section from analysis_options.yaml to determine which rules are enabled/disabled — previously the generated config was not consumed by the plugin
  • Registration-time rule filtering — disabled rules are never registered with the analyzer, improving startup performance

Fixed #

  • Plugin now respects rule enable/disable config from dart run saropa_lints:init — previously all rules were registered unconditionally regardless of tier selection
  • V4 migration no longer imports all rule settings as overrides — only settings that differ from the selected v5 tier defaults are preserved, preventing mass rule disablement
  • Init script scans and auto-fixes broken ignore comments — detects trailing explanations (// ignore: rule // reason or // ignore: rule - reason) that silently break suppression, and moves the text to the line above
  • Quick fix support for 108 rules via native SaropaFixProducer system — enables IDE lightbulb fixes and dart fix --apply
  • 3 reusable fix base classes: InsertTextFix, ReplaceNodeFix, DeleteNodeFix in lib/src/fixes/common/
  • 108 individual fix implementation files in lib/src/fixes/<category>/, all with real implementations (zero TODO placeholders)
  • Test coverage for all 95 rule categories (Phase 1-3): every category now has a dedicated test/*_rules_test.dart file with fixture verification and semantic test stubs
  • 506 missing fixture stubs across all example directories (Phase 1-4)
  • 12 new package fixture directories: flutter_hooks, workmanager, supabase, qr_scanner, get_it, geolocator, flame, sqflite, graphql, firebase, riverpod, url_launcher

Changed #

  • PERFORMANCE.md rewritten for v5 native plugin architecture — replaced all custom_lint references with dart analyze, updated rule counts, documented lazy rule instantiation and compile-time constant tier sets, added rule deferral info

Fixed #

  • Test fixture paths for bloc, firebase, riverpod, provider, and url_launcher now point to individual category directories instead of shared packages/ directory
  • Platform fixture paths reorganized from shared platforms/ directory to per-platform directories (ios/, macos/, android/, web/, linux/, windows/) — fixes 0% coverage report for all platform categories
  • Coverage script fallback search for fixture files in subdirectories, with prefix-match anchoring and OS error handling

5.0.0-beta.4 #

Fixed #

  • Untrack .github/copilot-instructions.md — was gitignored but tracked, causing dart pub publish --dry-run to exit 65 (warning)
  • Publish workflow dry-run step now tolerates warnings (exit 65) but still fails on errors (exit 66)
  • Publish script now waits for GitHub Actions workflow to complete and reports real success/failure — previously printed "PUBLISHED" immediately without checking CI status

5.0.0-beta.3 #

Fixed #

  • Add analyzer as explicit dependency — dart pub publish rejected transitive-only imports, causing silent publish failure
  • Remove || [ $? -eq 65 ] from publish workflow — was silently swallowing publish failures

5.0.0-beta.2 #

Fixed #

  • Publish script regex patterns updated for v5 positional LintCode constructor — tier integrity, audit checks, OWASP coverage, prefix validation, and correction message stats now match both v5 positional and v4 named parameter formats
  • Publish script version utilities now support pre-release versions (5.0.0-beta.15.0.0-beta.2) — version parsing, comparison, pubspec read/write, changelog extraction, and input validation all handle -suffix.N format

5.0.0-beta.1 — Native Plugin Migration #

Migrated from custom_lint_builder to the native analysis_server_plugin system. This is a breaking change for consumers (v4 → v5).

Why this matters:

  • Quick fixes now work in IDE — the old analyzer_plugin protocol never forwarded fix requests to custom_lint plugins (Dart SDK #61491). The native system delivers fixes properly.
  • Per-file filtering is enforced — 425+ uses of applicableFileTypes, requiredPatterns, requiresWidgets, etc. were defined but never checked. Now enforced via SaropaContext._wrapCallback(), cached per file.
  • Future-proof — the old analyzer_plugin protocol is being deprecated (Dart SDK #62164). custom_lint was the primary client.
  • ~18K lines removed — native API eliminates boilerplate (no more CustomLintResolver/ErrorReporter/CustomLintContext parameter triples).

Added #

  • Native plugin entry point (lib/main.dart) with SaropaLintsPlugin
  • SaropaFixProducer base class for quick fixes (analysis_server_plugin)
  • fixGenerators getter on SaropaLintRule for automatic fix registration
  • SaropaContext with per-file filtering wrapper on all 83 addXxx() methods
  • CompatVisitor bridging callbacks to native SimpleAstVisitor dispatch
  • PoC quick fixes: CommentOutDebugPrintFix, RemoveEmptySetStateFix
  • Native framework provides ignore-comment fixes automatically (no custom code needed)
  • Config loader (config_loader.dart) reads analysis_options_custom.yaml at startup
  • Severity overrides via severities: section (ERROR/WARNING/INFO/false per rule)
  • Baseline suppression wired into reporter — checks BaselineManager before every report
  • Impact tracking — every violation recorded in ImpactTracker by impact level
  • Progress tracking — files and violations tracked in ProgressTracker per file/rule
  • Plugin.start() lifecycle hook for one-time config loading
  • Tier preset YAML files updated to native plugins: saropa_lints: diagnostics: format
  • Migration guide (MIGRATION_V5.md) for v4 to v5 upgrade

Changed #

  • bin/init.dart generates native plugins: format (was custom_lint:)
  • Tier presets use diagnostics: map entries (was rules: list entries)
  • Init command runs dart analyze after generation (was dart run custom_lint)
  • All 96 rule files migrated to native AnalysisRule API
  • SaropaLintRule now extends AnalysisRule (was DartLintRule)
  • LintCode uses positional constructor: LintCode('name', 'message') (was named params)
  • runWithReporter drops CustomLintResolver parameter
  • context.addXxx() replaces context.registry.addXxx()
  • reporter.atNode(node) replaces reporter.atNode(node, code) (code is implicit)
  • Dependencies: analysis_server_plugin: ^0.3.3 replaces custom_lint_builder
  • README updated for v5: dart analyze replaces dart run custom_lint, tier preset includes, v4 migration FAQ

Removed #

  • custom_lint_builder dependency and lib/custom_lint_client.dart
  • Redundant PoC files (saropa_analysis_rule.dart, poc_rules.dart, saropa_reporter.dart)
  • Old v4 ignore-fix classes — superseded by native framework

4.15.1 #

Added #

  • avoid_color_only_meaning (Essential, WARNING): Warns when color is the sole visual indicator to convey meaning or state, violating WCAG 1.4.1. Checks multiple widget types (Container, Card, Material, etc.) and verifies a companion Icon/Text exists
  • avoid_excessive_rebuilds_animation (Essential, WARNING): Warns when animation builder callbacks (AnimatedBuilder, ValueListenableBuilder, StreamBuilder, etc.) contain more than 5 widget constructors, causing excessive rebuilds every frame
  • avoid_misused_hooks (Essential, WARNING): Warns when Flutter hook functions (useState, useEffect, etc.) are called inside callbacks or closures rather than at the top level of the build method
  • require_rtl_layout_support (Recommended, WARNING): Warns when hardcoded left/right directional values are used (TextAlign.left, Alignment.centerLeft, EdgeInsets.fromLTRB) instead of directional equivalents (start/end) for RTL language support. Quick fix converts to directional equivalents
  • avoid_misused_test_matchers (Recommended, WARNING): Warns when raw literals (true, false, null) are used as test matchers instead of proper matchers (isTrue, isFalse, isNull, hasLength). Quick fix replaces with proper matchers

4.15.0 #

Added #

  • avoid_ignoring_return_values (Recommended, INFO): Warns when a function's return value is discarded, which can silently hide bugs where an important result (Future, boolean, parsed value) is lost
  • prefer_optimistic_updates (Professional, INFO): Warns when setState is called after an await expression — update local state immediately for a snappier UX and rollback on failure
  • avoid_full_sync_on_every_launch (Professional, WARNING): Warns when bulk data fetch methods (getAll, fetchAll, findAll) are called inside initState, which wastes bandwidth on every launch instead of using delta sync
  • avoid_cached_image_unbounded_list (Essential, WARNING): Warns when CachedNetworkImage is used in a scrollable list without memCacheWidth/memCacheHeight, causing OOM from full-resolution image caching
  • require_session_timeout (Professional, INFO): Warns when authentication sign-in calls lack session timeout handling, leaving sessions valid indefinitely if tokens are compromised

4.14.5 #

Fixed #

  • require_error_case_tests false positive on defensive try-catch source code: Expanded test-name keyword detection from 12 to 30 keywords, covering defensive behavior patterns (safely, graceful, defensive, default, zero), lifecycle conditions (dispose, closed, disconnect), failure conditions (timeout, cancel, overflow), input validation (malformed, corrupt), and access control (denied, unauthorized, reject). Test files whose names describe fallback-value testing (e.g. "returns zero when unattached") are no longer flagged
  • prefer_setup_teardown false positive on assertion calls: The rule no longer treats expect(), verify(), fail(), and other test assertion/verification functions as duplicated setup code. Also scoped signature matching to within a single group() block — tests in unrelated groups no longer inflate the duplicate count
  • avoid_url_launcher_simulator_tests false positive on non-url_launcher tests: Rule now requires both a url_launcher import and launcher API usage (e.g. launchUrl, canLaunchUrl) — scheme strings alone no longer trigger the warning. Also removed group() matching to avoid flagging hundreds of lines for a single scheme string
  • function_always_returns_null false positive on generator functions: Added return-type guard (Stream/Iterable) as belt-and-suspenders for async*/sync* generators where body.isGenerator fails to resolve correctly in some analyzer versions
  • require_ios_callkit_integration false positive from substring matching: Pattern matching now uses word boundaries instead of String.contains(), preventing false positives when VoIP SDK names appear as substrings of unrelated words (e.g. "Zagora" no longer matches the "Agora" pattern)
  • avoid_unnecessary_setstate false positive on closure callbacks: The rule no longer flags setState inside deferred callbacks (.listen(), Future.delayed(), .then(), addPostFrameCallback()) defined within lifecycle methods — only direct synchronous setState calls are flagged
  • prefer_wheretype_over_where_is false positive on negated type checks: The rule no longer fires on .where((e) => e is! T) patterns, since whereType<T>() only filters for a type and cannot exclude one. The auto-fix is also guarded to prevent generating semantically incorrect replacements

Added #

  • prefer_semantics_container (Professional, INFO): Warns when Semantics wraps a multi-child layout (Column, Row, etc.) without container: true, causing screen readers to announce children individually instead of as a group
  • avoid_redundant_semantics (Comprehensive, INFO): Warns when Semantics wraps an Image that already has semanticLabel, causing duplicate screen reader announcements
  • avoid_image_picker_quick_succession (Professional, WARNING): Warns when pickImage/pickVideo is called without a loading guard, preventing ALREADY_ACTIVE PlatformException from double-taps
  • require_analytics_error_handling (Recommended, INFO): Warns when analytics SDK calls (logEvent, track, etc.) lack try-catch, since analytics failures should never crash the app
  • prefer_input_formatters (Professional, INFO): Warns when TextField/TextFormField has a structured keyboardType (phone, number, datetime) but no inputFormatters for auto-formatting
  • prefer_go_router_redirect (Professional, INFO): Warns when GoRouter is created without a redirect callback, risking flash of protected content during auth checks
  • prefer_permission_request_in_context (Professional, INFO): Warns when permissions are requested in main() or initState() instead of just-in-time when the user performs a relevant action
  • avoid_shared_prefs_large_data (Professional, WARNING): Warns when SharedPreferences setString is called with serialized data (jsonEncode, toJson), which should use a proper database instead
  • prefer_geocoding_cache (Professional, INFO): Warns when reverse geocoding calls lack caching, wasting API quota on repeated lookups for the same coordinates
  • prefer_oauth_pkce (Professional, INFO): Warns when OAuth authorization requests lack PKCE parameters, leaving mobile flows vulnerable to code interception attacks
  • avoid_continuous_location_updates (Professional, WARNING): Warns when continuous location streams (getPositionStream, watchPosition) lack a distanceFilter, causing rapid battery drain from constant GPS polling
  • prefer_adaptive_icons (Recommended, INFO): Warns when Icon widgets use hardcoded literal sizes instead of adaptive sizing via IconTheme or MediaQuery
  • prefer_grace_period_handling (Professional, INFO): Warns when IAP purchase verification only checks PurchaseStatus.purchased without handling pending/grace period states, locking out users during billing retry
  • require_cached_image_device_pixel_ratio (Professional, INFO): Warns when CachedNetworkImage uses fixed width/height without considering device pixel ratio, causing blurry images on high-DPI screens
  • prefer_foreground_service_android (Professional, INFO): Warns when Timer.periodic is used without a foreground service, since Android 8+ kills background tasks within minutes
  • prefer_sliverfillremaining_for_empty (Professional, INFO): Warns when SliverToBoxAdapter is used for empty state content in CustomScrollView instead of SliverFillRemaining
  • avoid_infinite_scroll_duplicate_requests (Professional, WARNING): Warns when scroll listeners trigger data loading without an isLoading guard, causing duplicate simultaneous requests
  • prefer_infinite_scroll_preload (Professional, INFO): Warns when infinite scroll triggers loading only at 100% scroll extent instead of preloading at 70-80%
  • prefer_use_callback (Professional, INFO): Warns when inline closures are passed as callbacks in HookWidget build methods instead of using useCallback for memoization
  • require_stepper_state_management (Professional, INFO): Warns when Stepper widget with form inputs lacks state management (GlobalKey, controllers), causing data loss on step navigation
  • prefer_semantics_container quick fix: "Add container: true" inserts the parameter into the Semantics constructor
  • prefer_sliverfillremaining_for_empty quick fix: "Replace with SliverFillRemaining" renames SliverToBoxAdapter to SliverFillRemaining

4.14.4 #

Added #

  • require_number_format_locale quick fix: New "Use device locale (Intl.defaultLocale)" quick fix inserts Intl.defaultLocale as the locale argument, letting developers explicitly acknowledge intentional device-locale formatting without // ignore comments

Fixed #

  • avoid_ios_hardcoded_device_model false positive on substring matches: Added word boundary assertions to the device model regex so strings like 'tripadvisor.com' (containing ipad as a substring) are no longer flagged as hardcoded iOS device models (v3)
  • check_mounted_after_async false positive on early-return guards: Rule now recognizes if (!mounted) return; and if (!context.mounted) return; guard patterns as valid mounted checks, not just wrapping if (mounted) { ... } blocks. Also correctly invalidates guards when a subsequent await occurs between the guard and the target call (v5)
  • avoid_missing_enum_constant_in_map false positive on complete maps: Rule now resolves the actual enum type instead of using a 2-5 key heuristic, eliminating false positives on maps that include all enum constants
  • no_equal_conditions false positive on if-case pattern matching: Rule now includes the caseClause in the condition key so if (x case A) ... else if (x case B) chains with different patterns are no longer flagged as duplicate conditions (v5)
  • function_always_returns_null false positive on generators: Rule no longer flags async* and sync* generator functions or methods where bare return; statements end the stream/iterable rather than returning null. Covers both top-level functions (addFunctionDeclaration) and class methods (addMethodDeclaration)

4.14.3 #

Fixed #

  • require_websocket_reconnection false positive (v4): Rule no longer fires on WebSocket and WebSocketChannel class definitions themselves; only classes that use WebSocket connections are checked
  • Cross-rule noise in fixture files: Added ignore_for_file directives to 8 example fixtures to suppress unrelated rule violations (e.g. avoid_print_in_release in avoid_variable_shadowing_fixture.dart)

Tests Disabled #

  • super_formal_parameter_without_associated_positional: Disabled pending further investigation.
  • const_constructor_with_non_const_super: Disabled pending further investigation.
  • implicit_super_initializer_missing_arguments: Disabled pending further investigation.

4.14.2 #

Added #

  • US English spelling check in publish pipeline: New _us_spelling.py module with ~90+ UK-to-US spelling pairs scans all source files for British English and blocks publish until fixed. Integrated as a blocking pre-publish audit step

Changed #

  • Split example project into 6 sub-packages: Resolves Out of Memory crash during custom_lint analysis of 1,542 fixture files. Fixtures are now split across example_core/, example_async/, example_widgets/, example_style/, example_packages/, and example_platforms/, each independently analyzable within memory limits
  • Fixture imports use package imports: All fixtures now import flutter_mocks.dart via package:saropa_lints_example/flutter_mocks.dart instead of relative paths
  • New convenience script: scripts/run_custom_lint_all.py runs analysis across all sub-packages
  • TODO audit in publish pipeline: New display_todo_audit() shows per-package bar chart of placeholder TODOs and writes full log to example/reports/todo_audit.log
  • VSCode analysis exclusion: Example sub-packages excluded from Dart analysis via .vscode/settings.json to keep Problems view clean
  • TODO/FIXME/HACK suppression: All example analysis_options.yaml files suppress todo, fixme, and hack diagnostics

Changed (continued) #

  • Removed version numbers from fixture filenames: 16 fixture files and 1 test file renamed to remove hard-coded version suffixes (e.g., stylistic_v270_fixture.dartstylistic_fixture.dart)
  • Stripped version references from comments: Removed (from vX.Y.Z) and added in vX.Y.Z from ~22 section headers and comments across fixture files

Fixed #

  • British spellings corrected: Fixed UK spellings to US English in prefer_fields_before_methods correction message, db_yield_rules comment, and several CHANGELOG entries
  • TODO audit path separator: Fixed hardcoded backslash in TODO log grouping to use os.sep for cross-platform compatibility
  • prefer_no_commented_out_code false positives (v5): Prose labels like OK:, BAD:, GOOD:, LINT: no longer falsely flagged as code. Removed colon from code detection pattern and added expect_lint: to special markers
  • prefer_capitalized_comment_start false positive (v4): Continuation comments on consecutive lines no longer flagged for lowercase start
  • prefer_explicit_type_arguments false positives (v6): Empty collections ([], {}) with types inferred from context (return type, variable declaration) are no longer flagged
  • avoid_variable_shadowing config name mismatch: Fixed avoid_shadowingavoid_variable_shadowing in custom_lint.yaml and analysis_options_template.yaml to match the actual rule name
  • verify_documented_parameters_exist offset bug (v3): Rule reported lints at wrong source offset when non-doc-comment lines (e.g. // expect_lint:) appeared between /// doc comment lines. Fixed by iterating per-token instead of using joined string offset
  • Unfulfilled expect_lint errors: Enabled avoid_nested_assignments, avoid_variable_shadowing, and verify_documented_parameters_exist in example analysis_options.yaml so fixture expect_lint annotations are satisfied

4.14.1 #

Changed #

  • Roadmap summary bars inverted: Bars now show completion (0 remaining = full green bar) instead of remaining count, making progress more intuitive
  • DX Message Quality label padding: Widened impact label column from 10 to 12 chars so "Opinionated" aligns correctly
  • Removed duplicate _make_progress_bar: Consolidated with identical _make_bar function in rule metrics

Added #

  • Unit test coverage for 10 rule categories (1256 tests): Behavior documentation tests for widget_patterns (101 rules), code_quality (100 rules), ios (89 rules), widget_layout (73 rules), security (53 rules), async (46 rules), bloc (52 rules), riverpod (37 rules), provider (27 rules), and firebase (25 rules) — covering fixture verification and expected trigger/no-trigger patterns
  • Unit test coverage metric in publish workflow: New display_unit_test_coverage report shows per-category test file status alongside the existing fixture coverage metric
  • Structured JSON violation export (reports/.saropa_lints/violations.json): Machine-readable export of all lint violations written alongside the markdown report after each analysis run. Enables Saropa Log Capture to cross-reference runtime errors with static analysis findings. Schema v1.0 includes per-violation OWASP mappings, correction messages, impact levels, severity, plus aggregate issuesByFile, issuesByRule, ruleSeverities, enabledRuleNames, disabledPackages, userExclusions, and batchCount
  • VIOLATION_EXPORT_API.md: Exhaustive API reference for the structured JSON export schema — field types, sort order, OWASP ID tables, consumer notes, and full examples
  • correction field on ViolationRecord: Carries LintCode.correctionMessage through the batch pipeline into both the markdown report and JSON export
  • toRelativePath shared utility: Extracted from duplicated path normalization logic in the report pipeline
  • New Essential rule: require_firebase_composite_index (ERROR) — detects Firebase Realtime Database queries using orderByChild with filter methods that need a .indexOn rule in database security rules

4.14.0 #

Added #

  • 6 new Essential rules (ERROR severity):

    • prefer_correct_package_name: Library directive names must use lowercase_with_underscores format per Dart naming conventions
    • avoid_getx_build_context_bypass: Detects Get.context and Get.overlayContext usage that bypasses Flutter's BuildContext propagation
    • avoid_permission_handler_null_safety: Detects deprecated pre-null-safety permission_handler APIs (PermissionHandler(), PermissionGroup)
    • avoid_retaining_disposed_widgets: Detects Widget/State/BuildContext references stored in non-widget classes causing memory leaks
    • require_secure_key_generation: Detects predictable encryption key generation (Key.fromLength, hardcoded byte arrays, List.filled)
    • require_hive_web_subdirectory: Detects Hive.initFlutter() without subDir parameter causing web storage conflicts
  • Quick fix for require_hive_web_subdirectory: Adds subdirectory parameter to Hive.initFlutter() calls

  • 5 new rules:

    • avoid_blocking_main_thread (Essential/WARNING): Detects synchronous I/O methods (readAsStringSync, etc.) on the main isolate (#17)
    • require_log_level_for_production (Professional/INFO): Detects verbose logging without kDebugMode/assert guards
    • require_analytics_event_naming (Professional/INFO): Detects non-snake_case analytics event names (#19)
    • require_feature_flag_type_safety (Recommended/INFO): Detects string-based feature flag access without type-safe wrappers
    • require_timezone_display (Recommended/INFO): Detects DateFormat with time components but no timezone indicator; covers named constructors (Hm, jm, Hms)

Fixed #

  • False positive prevention: Added receiver/target filtering to 3 heuristic rules to prevent false positives on unrelated APIs:

    • require_log_level_for_production: Now requires logger-like receiver; math.log(), myObject.trace() no longer trigger
    • require_analytics_event_naming: track() and sendEvent() now require analytics-like receiver; shipment.track() no longer triggers
    • require_feature_flag_type_safety: isEnabled() moved to target-filtered set; notification.isEnabled() no longer triggers
  • Double-fire note: avoid_synchronous_file_io (Professional) is now also covered by avoid_blocking_main_thread (Essential) which adds isolate detection

  • Rule versioning: Added {vN} version suffixes and Since: vX.Y.Z DartDoc provenance to all rules

  • Publish script enhancements: Auto-sync README/ROADMAP rule counts, roadmap header sync, GitHub issue tracking


4.13.0 #

Changed #

  • avoid_empty_setstate no longer flags mounted-guarded calls: Empty setState(() {}) inside a mounted guard (if (mounted), ternary, or early-return pattern) is now suppressed. This is an intentional Flutter idiom for triggering rebuilds after async gaps or external state mutations. This rule flags a valid Flutter idiom (if (mounted) setState(() {}) after async gaps). It is a style preference, not a bug or correctness issue, so it belongs in the comprehensive tier for quality-focused teams. Impact reduced from medium to low.

Fixed #

  • False positives: replaced substring matching with exact detection across 30+ rules: Rules in animation, async, navigation, API/network, disposal, permission, provider, and widget lifecycle categories now use exact-match sets, endsWith/startsWith, and AST patterns instead of String.contains() substring matching. Eliminates false positives on permission utilities, custom wrappers, and identifiers that happen to contain framework terms like "Location", "Navigator", or "Controller".

  • require_location_timeout no longer fires on permission checks: GPS timeout rule now uses an exact allowlist of GPS methods (getCurrentPosition, getLastKnownPosition, etc.) and detects chained .timeout() calls. Previously matched any method with "location" in the name.

  • Report: duplicate violations on file re-analysis: _currentFile was not updated when a file was re-analyzed, causing _trackByFileAndRule to attribute violations to the wrong file and _clearFileData to skip ImpactTracker cleanup. Violations appeared 10-15x in reports.

  • Report: session detection uses re-analysis instead of timeout: The 3-second debounce could fire mid-analysis (observed 83-second gap between file batches), splitting one run into multiple reports. Session boundaries now use file re-analysis detection — straggler files are new and never trigger false boundaries.

Added #

  • Report: analysis configuration header: Reports now include tier, enabled rules, platforms, packages, user exclusions, and verbatim analysis_options_custom.yaml content.

  • Publish gate: [rule_name] prefix required in all problemMessage strings: The publish script now blocks release if any rule's problemMessage does not start with [rule_name]. All 15 previously non-compliant rules have been updated.

  • CI guard: .contains() anti-pattern detection test: New test scans all rule files for 9 dangerous String.contains() patterns (e.g. methodName.contains(, toSource().contains() and fails CI if new violations are introduced. Per-file baseline counts track the 1,100+ existing instances; baselines tighten as violations are removed.

  • Shared utility: target_matcher_utils.dart: Four functions (extractTargetName, isExactTarget, isFieldCleanedUp, hasChainedMethod) that replace the most common .contains() anti-patterns with exact-match and AST-based detection.

  • False-positive regression tests: 7 new test cases in false_positive_fixes_test.dart covering the patterns that caused the most real-world false positives (location timeout, navigator match, context access, HTTP status, scroll controller dispose).

  • Report: import graph priority scoring: New FILE IMPORTANCE section ranks every analyzed file by a combined score of fan-in (how many files import it) and architectural layer weight. New FIX PRIORITY section sorts all violations by impact * (importance + 1) * layer_weight so the developer sees what to fix first. New PROJECT STRUCTURE section renders the full import dependency tree from entry points.

  • Report: multi-isolate batch consolidation: Reports now merge data from all isolate restarts within a session instead of losing earlier analysis data. Each isolate writes a batch file; the final report consolidates all batches into one combined report.


4.12.3 #

Fixed #

  • Init migrates existing configs: Running dart run saropa_lints:init on projects that already have max_issues now adds the missing output setting instead of skipping the file.

4.12.2 #

Changed #

  • Problems tab capped at 500: After 500 non-ERROR issues in the Problems tab, rules keep running and all remaining issues are written to the report log only. Configurable via SAROPA_LINTS_MAX env var or max_issues in analysis_options_custom.yaml.

  • Single report file: Merged full violation log and summary into one combined report file (_saropa_lint_report.log).

  • Progress shows total after limit: After 500 issues, the progress line shows "500 shown, 847 total" so you can see the report growing in real time.

Added #

  • Graceful abort: Create a .saropa_stop file in the project root to stop analysis mid-flight and get a partial report. Hint shown when the 500-issue limit is reached.

  • File-only output mode: Set output: file in analysis_options_custom.yaml (or SAROPA_LINTS_OUTPUT=file env var for a one-off run) to skip the Problems tab and send all violations to the report log only.

Fixed #

  • Report debounce resets on every file: The debounce timer now resets when any file is processed, not just when a violation is found. Prevents premature report writes during long stretches of clean files.

  • Session reset between analysis runs: Trackers now reset automatically when a new analysis session starts, preventing double-counted violations and stale _limitReached state from previous runs.

  • File re-analysis no longer double-counts: When a file is re-analyzed within the same session (e.g. user saves during active analysis), stale violation data is cleared before recording new violations.


4.12.1 #

Fixed #

  • Analysis report captures all violations: The debounce timer's write-once guard caused reports to contain only the first batch of violations when analysis gaps exceeded 3 seconds. Reports now overwrite on each debounce cycle so the final output reflects the complete analysis.

Added #

  • Regression tests for violation parser: Added 16 tests covering parseViolations() and the Violation model to guard against future custom_lint output format changes (see PR #84 / PR #90).

  • Updated PR #84 review document: Corrected merge status (PR #84 was closed in favor of PR #90), added timeline, risk table, and regression test reference.


4.12.0 #

Added #

  • 10 new lint rules across multiple categories:
    • avoid_context_dependency_in_callback (Essential, WARNING): Detects Theme.of(context), MediaQuery.of(context), etc. inside async callbacks (.then(), Future.delayed, Timer). These calls can use a disposed context. Includes quick fix.
    • avoid_datetime_comparison_without_precision (Professional, INFO): Flags ==/!= on DateTime values which fails due to microsecond differences. Suggests difference().abs() comparison. Includes quick fix.
    • avoid_getx_static_get (Professional, WARNING): Detects Get.find() calls which create hidden global dependencies. Suggests constructor injection. Includes quick fix.
    • avoid_missing_interpolation (Recommended, WARNING): Detects string concatenation with + where interpolation is clearer. Includes quick fix.
    • avoid_provider_listen_false_in_build (Recommended, INFO): Flags Provider.of(context, listen: false) inside build() methods which suppresses reactive rebuilds. Includes quick fix to remove listen: false.
    • avoid_hive_synchronous_in_ui (Essential, WARNING): Detects synchronous Hive operations (get, put, delete, etc.) inside build() or initState() which block the UI thread. Includes quick fix.
    • prefer_spring_animation (Recommended, INFO): Suggests replacing CurvedAnimation with physics-like bounce/elastic curves with SpringSimulation for more natural motion. Includes quick fix.
    • prefer_avatar_loading_placeholder (Recommended, INFO): Flags CircleAvatar with NetworkImage but no error handler or fallback child. Includes quick fix adding onBackgroundImageError and fallback Icon.
    • require_navigation_result_handling (Professional, INFO): Detects Navigator.push*() calls where the result is not awaited or assigned. Includes quick fix adding await.
    • require_semantic_colors (Professional, INFO): Flags Color variables named by appearance (e.g., redColor) instead of semantic purpose (e.g., errorColor). Includes quick fix.

4.11.1 #

  • Improved prefer_using_list_view Rule:
    • Upgraded problemMessage to clearly define the performance pitfalls of eager-loading Column widgets inside scroll views.
    • Rewrote correctionMessage into a detailed technical guide. It now explicitly recommends ListView.separated as the direct replacement for layouts requiring specific spacing, provides guidance on handling Flex constraints, and explains the mechanics of lazy loading.

4.11.0 #

Changed #

  • Centralized the duplicated violation-parsing logic from the baseline and impact_report tools. This resolves a structural issue highlighted by the regex fix in PR #84 and makes future updates more robust. (Thanks @icealive)

4.10.2 #

Readme #

  • Screenshots added to several sections in README.md.
  • Clarifications to some wording and tables. Plus badges were updated in README.md.

Tiers #

  • Renamed the "Insanity" tier ruleset to the "Pedantic" for clarity.

4.10.1 #

Added #

  • New rule verify_documented_parameters_exist (Professional, WARNING): Detects when dartdoc [paramName] references parameters that do not exist in the function signature. Complements the existing require_parameter_documentation rule which checks the inverse direction. Handles edge cases: uppercase type references, single-letter generics, dotted enum/field references, class field names, this. and super. constructor parameters.
  • Added Documentation Rules section to ROADMAP (section 1.63): Lists all 9 documentation rules including the new verify_documented_parameters_exist.

4.10.0 #

Fixed #

  • Removed duplicate rule prefer_async_only_when_awaiting: This rule detected the same issue as avoid_redundant_async (async function without await), causing duplicate diagnostics. The quick fix has been ported to avoid_redundant_async, which has broader detection scope and more robust await detection.
  • Registered 10 Windows/Linux platform rules: 5 Windows rules (avoid_hardcoded_drive_letters, avoid_forward_slash_path_assumption, avoid_case_sensitive_path_comparison, require_windows_single_instance_check, avoid_max_path_risk) and 5 Linux rules (avoid_hardcoded_unix_paths, prefer_xdg_directory_convention, avoid_x11_only_assumptions, require_linux_font_fallback, avoid_sudo_shell_commands) were implemented but not registered in the plugin or assigned to tiers.

Added #

  • 7 new quick fixes for dialog, freezed, and accessibility rules:
    • require_snackbar_duration: adds duration: const Duration(seconds: 4)
    • require_dialog_barrier_dismissible: adds barrierDismissible: false
    • prefer_adaptive_dialog: converts AlertDialog() to AlertDialog.adaptive()
    • avoid_freezed_json_serializable_conflict: removes redundant @JsonSerializable() annotation
    • require_freezed_arrow_syntax: converts fromJson block body to arrow syntax
    • require_freezed_private_constructor: inserts const ClassName._()
    • avoid_icon_buttons_without_tooltip: adds tooltip parameter to IconButton

Changed #

  • Improved diagnostic messages on 503 rules across 76 files: Expanded short problemMessage and correctionMessage strings using DartDoc context, fixed vague language ("consider" to direct commands, "should be" to "must be"), and appended category-specific consequences and testing advice. All 503 rules now fully pass DX quality thresholds.

  • Rewrote DX messages on 24 rules to pass all quality checks: Replaced generic boilerplate with rule-specific explanations (18 rules), fixed passive voice to active (3 rules: require_freezed_explicit_json, prefer_on_field_submitted, prefer_intl_name), removed escaped single quotes that broke audit regex parsing (2 rules: require_content_type_check, prefer_go_router_redirect_auth), and eliminated vague "should be" phrasing (prefer_absolute_imports). All rules now pass the full DX quality audit at 100%.

  • Publish script defers version prompt until after analysis: The publish_to_pubdev.py script no longer asks for the publish version upfront. Audits, prerequisites, tests, formatting, and static analysis all run first; the version prompt now appears at Step 8 only after all analysis passes.

  • prefer_expanded_at_call_site severity upgraded to ERROR: Bumped from WARNING to ERROR and impact from medium to critical — returning Expanded/Flexible/Spacer from build() causes the same class of runtime crash (ParentDataWidget error) as avoid_expanded_outside_flex. Moved from recommended tier to essential tier.

  • prefer_expanded_at_call_site now detects Spacer: Added Spacer to the detection set alongside Expanded and Flexible. Returning Spacer from build() has the same crash risk since it wraps Expanded internally.

  • avoid_expanded_outside_flex now detects Spacer: Same Spacer gap fixed in the sibling rule.

  • prefer_expanded_at_call_site quick fix now unwraps: Replaced the // HACK comment insertion with a proper code transformation that extracts the child argument and returns it directly. Not offered for Spacer (no child to extract).

  • avoid_expanded_outside_flex improved diagnostic messages: Expanded problemMessage to explain the FlexParentData/RenderFlex mechanism and the indirect build() return case. Expanded correctionMessage with actionable guidance for reusable widgets. Added "Why This Crashes" dartdoc section explaining the ParentDataWidget error.

Fixed #

  • Corrected misattributed DartDoc on 3 rules: avoid_unbounded_constraints had keyboard-shortcuts DartDoc, prefer_sliver_app_bar had FutureBuilder error-handling DartDoc, and require_should_rebuild had TextStyle theming DartDoc. Each now has DartDoc matching its actual rule behavior.
  • avoid_expanded_outside_flex no longer duplicates prefer_expanded_at_call_site: When Expanded/Flexible/Spacer is returned directly from build() without an intermediate widget wrapper, avoid_expanded_outside_flex now defers to prefer_expanded_at_call_site instead of reporting a second diagnostic on the same node. Expanded nested inside a non-Flex widget within build() is still reported by avoid_expanded_outside_flex.
  • avoid_single_child_column_row reduced false positives on collection-if and collection-for: Rule now treats IfElement and ForElement as dynamic-count elements (like SpreadElement). Previously, a children list containing a single collection-if or collection-for was incorrectly flagged as a single-child Column/Row, even though these elements can produce 0, 1, or many children at runtime.
  • avoid_manual_date_formatting reduced false positives on non-display contexts: Rule now verifies the target object is actually DateTime via static type checking (properties named year, month, etc. on non-DateTime types are no longer flagged). Additionally, string interpolations used as map keys, cache keys, or arguments to map methods (putIfAbsent, containsKey, remove) are skipped. Variables with internal-use names containing key, cache, tag, hash, bucket, or identifier are also excluded.
  • avoid_nested_assignments false positive on for-loop update clauses: Compound assignments in standard for-loop updaters (i += step, i *= 2, i = next(i), etc.) are no longer flagged. The rule now skips ForParts nodes alongside the existing ForEachParts skip.

4.9.20 #

Added #

  • Linux platform rules (5 new rules): avoid_sudo_shell_commands (ERROR — detects Process.run with sudo/pkexec, OWASP M1), avoid_hardcoded_unix_paths (WARNING — detects /home/, /tmp/, /etc/ literals), avoid_x11_only_assumptions (WARNING — detects X11-only tools and DISPLAY access without Wayland check), prefer_xdg_directory_convention (INFO — detects manual ~/.config/ path construction), require_linux_font_fallback (INFO — detects non-Linux fonts without fontFamilyFallback, quick fix adds fallback fonts)
  • Windows platform rules (5 new rules): avoid_hardcoded_drive_letters (WARNING — detects C:\, D:\ literals), avoid_forward_slash_path_assumption (WARNING — detects / path concatenation instead of path.join()), avoid_case_sensitive_path_comparison (WARNING — detects path equality without case normalization, quick fix adds .toLowerCase()), require_windows_single_instance_check (INFO — detects missing single-instance enforcement in Windows main()), avoid_max_path_risk (INFO — detects deeply nested paths that may exceed 260-char MAX_PATH)

Fixed #

  • avoid_nested_assignments reduced false positives on arrow function bodies: Rule now skips ExpressionFunctionBody parents — setState(() => _field = value) and other arrow functions whose sole body is an assignment are no longer flagged. The arrow syntax () => x = value is semantically equivalent to () { x = value; }, which was already correctly skipped. Also downgraded severity from WARNING to INFO.
  • require_intl_currency_format reduced false positives on non-currency interpolations: The StringInterpolation handler used node.toSource() to check for currency symbols, but every interpolated string's source representation contains $ (Dart's interpolation syntax), causing r'$' in _currencySymbols to always match. This made the rule flag any toStringAsFixed() inside a string interpolation — compass bearings, GPS coordinates, temperatures, percentages, etc. — as manual currency formatting. The fix checks only the literal text segments (InterpolationString.value) for currency symbols, consistent with how the BinaryExpression handler already works.
  • prefer_implicit_boolean_comparison reduced false positives on nullable booleans: Rule now checks the static type of the left operand and only fires when it is non-nullable bool. Previously the rule flagged == true / == false on bool? operands, where the explicit comparison is semantically necessary — removing it either causes a compile error or changes runtime behavior (treating null the same as false). This also resolves a conflict with the sibling rule prefer_explicit_boolean_comparison, which recommends == true for nullable booleans.
  • prefer_stream_distinct reduced false positives: Three fixes — (1) rule now skips Stream<void> and Stream<Null> where .distinct() would suppress all events after the first (breaks Stream.periodic timers and signal-only streams like Isar's watchLazy()); (2) chain detection now walks the full method invocation chain instead of only checking the immediate parent, so stream.distinct().map(f).listen(...) is correctly recognized as already having .distinct(); (3) replaced string-based type matching (getDisplayString().contains('Stream')) with proper InterfaceType checking to avoid false matches on non-stream types.
  • prefer_edgeinsets_symmetric reduced false positives: Detection logic now matches the quick-fix validation — the rule no longer fires on EdgeInsets.only() calls that have a symmetric pair (e.g. top == bottom) but also contain an unpaired side (e.g. right without left) or a non-symmetric axis (e.g. top != bottom), since EdgeInsets.symmetric() cannot express these cases without chaining .copyWith().

4.9.19 #

Fixed #

  • require_did_update_widget_check reduced false positives: Rule now recognizes function-based comparisons (listEquals, setEquals, mapEquals, identical, DeepCollectionEquality().equals) as valid property-change checks. Previously the regex only matched oldWidget adjacent to !=/== operators, so standard Flutter collection comparisons were falsely flagged.
  • prefer_const_widgets_in_lists reduced false positives: Two fixes — (1) rule now verifies list elements are actually Widget subclasses instead of treating any InstanceCreationExpression as a widget, eliminating false positives on List<Color>, List<Offset>, List<EdgeInsets>, and other non-widget types with const constructors; (2) rule now recognizes implicitly const lists inside static const fields, const variable declarations, enum bodies, annotations, and const constructor calls, instead of only checking for an explicit const keyword on the list literal itself.
  • avoid_positioned_outside_stack / avoid_table_cell_outside_table / avoid_spacer_in_wrap reduced false positives in builder callbacks: _findWidgetAncestor now treats named-parameter callbacks (e.g. BlocBuilder.builder, StreamBuilder.builder, Builder.builder, LayoutBuilder.builder) as indeterminate boundaries. Previously the AST walk crossed callback boundaries and incorrectly flagged widgets whose runtime parent depends on the call site, not the builder widget itself. Also applied the same fix to the inline ancestor walk in avoid_flex_child_outside_flex.

4.9.18 #

Fixed #

  • avoid_empty_setstate reduced severity and corrected message: Changed severity from WARNING to INFO — an empty setState(() {}) still triggers a rebuild, so the previous message ("has no effect") was factually incorrect. Reworded to acknowledge that state is often modified before the call (e.g. after an async gap with a mounted check). This is a valid Flutter pattern, not a bug.
  • require_yield_between_db_awaits improved messages and quick fix: Expanded problemMessage and correctionMessage to explain why yielding matters (shared isolate, frame starvation, perceived jank). Quick fix now inserts a blank line, an explanatory comment (// Let the UI catch up to reduce locks), the yieldToUI() call, and a trailing blank line. Quick fix description also expanded.

4.9.17 #

Fixed #

  • avoid_positioned_outside_stack reduced false positives: Two fixes to _findWidgetAncestor: (1) AssignmentExpression is now treated as an indeterminate boundary — when Positioned is assigned via x = Positioned(...) to an already-declared variable that is later used inside a Stack, the rule no longer flags it; (2) when the AST walk reaches the enclosing build() method without finding a Stack and without passing through any intermediate widget constructor, the Positioned is the root widget of that build() — its eventual parent depends on how the caller places the widget, so the result is now indeterminate instead of notFound. Also benefits avoid_table_cell_outside_table which shares the same ancestor-walking logic.
  • avoid_unbounded_listview_in_column / avoid_textfield_in_row reduced false positives: Both rules now stop the ancestor walk at callback boundaries — when a scrollable widget or text field is inside a named builder callback (e.g. Autocomplete.optionsViewBuilder, SearchAnchor.suggestionsBuilder, PopupMenuButton.itemBuilder), the rule no longer treats the callback's AST ancestors as runtime widget ancestors. The standard builder parameter name is excluded from this boundary since widgets like Builder and LayoutBuilder render their output in place.

Changed #

  • README: Added platform and package configuration documentation: Expanded the platform configuration section with a table showing rule counts and examples for all 6 platforms (iOS, Android, macOS, Web, Windows, Linux), shared platform groups (Apple, Desktop), and how shared rules are handled. Added a new package configuration section documenting all 21 configurable packages grouped by category (state management, storage, networking, etc.) with full YAML example.

Package Publishing #

  • Publish script: version prompt with timeout: Script now prompts for the publish version (pre-filled with pubspec value, 30s timeout) allowing major/minor bumps without manual pubspec edits
  • Publish script: [Unreleased] renamed to version before publishing: The [Unreleased] section in CHANGELOG.md is automatically renamed to the publish version at the start of the workflow
  • Publish script: duplicate version detection: Publishing fails immediately if the git tag or GitHub release already exists, instead of continuing silently
  • Publish script: GitHub release failure is now a blocker: Script exits on release creation failure instead of warning and continuing to post-publish steps
  • Publish script: removed automatic post-publish version bump: The script no longer commits an unpublished version number to the repository after publishing

4.9.16 #

Added #

  • New rules: require_yield_between_db_awaits, avoid_return_await_db: Two database/IO yield rules that detect missing yieldToUI() calls after heavy DB or file I/O awaits. Prevents UI jank caused by blocking the main thread with consecutive database operations. Both rules include quick fixes that insert await DelayUtils.yieldToUI(); automatically. Heuristic detection covers Isar, sqflite, Hive, and file I/O methods, with configurable target identifiers. Assigned to Recommended tier with WARNING severity.

  • Extracted package rules into dedicated files: Bloc rules (52) to bloc_rules.dart, Provider rules (26) to provider_rules.dart, Dio rules (14) to dio_rules.dart, SharedPreferences rules (10) to shared_preferences_rules.dart, GetIt rules (3) to get_it_rules.dart. Also Riverpod rules (37) expanded in riverpod_rules.dart, GetX rules (20) expanded in getx_rules.dart, Equatable rules in equatable_rules.dart. Supabase rules to supabase_rules.dart, Workmanager rules to workmanager_rules.dart. No behavior changes — rules remain in their original tiers with the same detection logic.

Changed #

  • Moved ~40 opinionated rules from tier sets to stylistic tier: Rules with no performance or correctness benefit — code style preferences, formatting, ordering, and naming conventions — are now in stylisticRules instead of their original tier sets (recommended, professional, comprehensive, insanity). Each moved rule's LintImpact is set to opinionated, and its problemMessage explicitly states there is no performance benefit. The 6 rules that remained in their original tiers (prefer_sized_box_for_whitespace, prefer_padding_over_container, prefer_align_over_container, prefer_correct_identifier_length, prefer_match_file_name, prefer_correct_test_file_name) now document their objective justification (performance benefit or discoverability) in doc headers and problem messages. Conflicting pair prefer_type_over_var / prefer_var_over_explicit_type are both in the stylistic tier with explicit conflict notes.

  • Audit report now includes DX Message Quality section: The full audit markdown report (reports/*_full_audit.md) now exports the complete DX quality analysis — summary tables by impact level and tier, issues grouped by type, and a searchable per-rule failing table with tier, impact, score, and specific issues. Previously, DX data was only shown in the console during the audit run but omitted from the exported report.

  • Improved DX message quality for 28 lint rules: Rewrote problemMessage and correctionMessage for rules whose messages started with imperative "Avoid" phrasing or were under 200 characters. Messages now use declarative statements that describe what was detected, explain the specific consequence, and provide concrete fix guidance. Affected rules: avoid_nested_shorthands, avoid_string_substring, avoid_debug_print, avoid_di_in_widgets, no_magic_number, no_magic_string, no_magic_number_in_tests, no_magic_string_in_tests, avoid_nested_records, avoid_one_field_records, avoid_positional_record_field_access, avoid_single_field_destructuring, avoid_nullable_async_value_pattern, avoid_returning_void, avoid_hardcoded_colors, avoid_real_timer_in_widget_test, avoid_top_level_members_in_tests, avoid_casting_to_extension_type, avoid_dynamic_type, avoid_nullable_interpolation, avoid_null_assertion, avoid_non_null_assertion, avoid_context_in_initstate_dispose, avoid_late_context, avoid_sized_box_expand, avoid_hardcoded_layout_values, prefer_expanded_at_call_site, require_hive_initialization.

Fixed #

  • avoid_async_call_in_sync_function reduced false positives: Five improvements: (1) exempts cancel() and close() calls in lifecycle methods (dispose(), didUpdateWidget(), deactivate()) — these synchronous overrides cannot be made async, and subscription/controller cleanup is the standard idiomatic pattern; (2) recognizes .ignore() as an explicit fire-and-forget chain alongside .then(), .catchError(), and .whenComplete(); (3) exempts StreamController.close() inside onDone/onError callbacks, which are void Function() and cannot use await; (4) walks through transparent expression wrappers (ParenthesizedExpression, PostfixExpression) to correctly detect handled Futures like (asyncCall()) passed to argument lists; (5) refactored visitor into _shouldReport() for clarity. Added test fixture with BAD and GOOD cases covering unawaited(), lifecycle cleanup, .ignore() chains, and void callbacks.

  • avoid_late_for_nullable reduced false positives: Exempts late final fields with inline initializers (e.g. late final Stream<bool>? _stream = _init()). The late keyword provides lazy evaluation in this pattern — the initializer runs on first access, so LateInitializationError is impossible. The nullable return type carries semantic meaning and should not be flagged.

  • prefer_nullable_over_late reduced false positives: Two exemptions: (1) late final fields with inline initializers — same lazy evaluation rationale as above; (2) late fields in State subclasses — Flutter's lifecycle guarantees initState() runs before build(), making late assignment safe and idiomatic.

  • avoid_unbounded_constraints reduced false positives: Three improvements: (1) checks only direct children for Expanded/Flexible via AST instead of string-matching the entire nested subtree, eliminating false positives on parent Columns that contain nested Rows with Expanded; (2) scroll-direction-aware — only flags when the widget axis matches the scroll axis (e.g. Row with Expanded in a vertical SingleChildScrollView is no longer flagged); (3) constraint widget detection (ConstrainedBox/SizedBox/Container) now only counts widgets between the Column/Row and the scroll view, not above it.

  • avoid_positioned_outside_stack now recognizes Stack subclasses: The rule previously only matched the exact types Stack and IndexedStack by name. Widgets that extend Stack (e.g. Indexer from package:indexed) triggered a false positive. The rule now uses the analyzer type hierarchy (allSupertypes) to accept any subclass of Stack as a valid parent.

  • Platform-aware filtering for keyboard/focus/hover rules: avoid_gesture_only_interactions, require_focus_indicator, and avoid_hover_only now respect the project's platforms: configuration. These rules enforce desktop/web-specific patterns (keyboard alternatives, focus indicators, hover alternatives) and are auto-disabled for mobile-only projects where they produced false positives. Also corrected require_focus_indicator tier grouping — moved from Recommended to Professional in webPlatformRules to match its actual tier assignment.

  • avoid_unbounded_constraints LintImpact upgraded to high: Reclassified from medium to high — Expanded/Flexible in an unbounded scroll axis is a crash path (RenderFlex overflow), not merely a code quality issue.

  • require_deep_link_fallback reduced false positives: Two improvements: (1) methods returning Widget types (Widget, Future<Widget>, PreferredSizeWidget, etc.) are now skipped as UI builders rather than deep link handlers; (2) a positive body signal check requires at least one deep link pattern (Uri, pathSegments, queryParameters, Navigator, GoRouter, or navigation calls) before flagging — methods like linkAccounts() or logRouteChange() that have link/route in their name but no URI parsing or navigation in their body are no longer flagged.

Removed #

  • 3 duplicate rules removed: require_prefs_key_constants (duplicate of require_shared_prefs_key_constants), require_equatable_immutable (duplicate of avoid_mutable_field_in_equatable), avoid_equatable_mutable_collections (duplicate of prefer_unmodifiable_collections). Removed from rule files, _allRuleFactories, tiers, and analysis options.

Archive #


4.9.145 #

Fixed #

  • require_ios_lifecycle_handling rewritten and renamed to require_app_lifecycle_handling: The rule used file-level string matching, causing false positives when unrelated classes in the same file contained lifecycle keywords. Rewritten with per-class AST detection via addClassDeclaration — each State subclass is independently checked for WidgetsBindingObserver mixin, didChangeAppLifecycleState method, and AppLifecycleListener fields. Also added one-shot Timer() constructor detection with word-boundary regex to avoid false positives from types like MyTimer. Renamed to require_app_lifecycle_handling (backward-compatible alias preserved) and moved from ios_rules.dart to lifecycle_rules.dart since the rule is platform-agnostic.

4.9.14 #

Added #

  • 9 layout crash detection rules (Essential tier): Static analysis for Flutter widget hierarchy errors that cause runtime crashes (Red Screen, assertion failures, unbounded constraints):
    • avoid_table_cell_outside_table (ERROR) — TableCell outside Table causes ParentData crash
    • avoid_positioned_outside_stack (ERROR) — Positioned outside Stack causes ParentData crash
    • avoid_spacer_in_wrap (ERROR) — Spacer/Expanded/Flexible in Wrap causes flex paradox crash
    • avoid_scrollable_in_intrinsic (ERROR) — scrollable in IntrinsicHeight/IntrinsicWidth causes geometry loop
    • require_baseline_text_baseline (ERROR) — baseline alignment without textBaseline causes assertion failure; quick fix: adds textBaseline: TextBaseline.alphabetic
    • avoid_unconstrained_dialog_column (WARNING) — Column in AlertDialog/SimpleDialog without MainAxisSize.min can overflow; quick fix: adds mainAxisSize: MainAxisSize.min
    • avoid_unbounded_listview_in_column (ERROR) — ListView/GridView in Column without Expanded causes unbounded constraints crash; quick fix: wraps in Expanded
    • avoid_textfield_in_row (ERROR) — TextField in Row without width constraint causes unbounded width crash; quick fix: wraps in Expanded
    • avoid_fixed_size_in_scaffold_body (WARNING) — Scaffold body Column with TextField but no ScrollView causes keyboard overflow

Fixed #

  • avoid_expanded_outside_flex false positive with Wrap: The rule included Wrap in its valid flex parent types, but Wrap does not extend Flex. Expanded/Flexible inside Wrap causes a ParentData crash. Removed Wrap from the accepted parent set.

4.9.13 #

Fixed #

  • check_mounted_after_async false positive on await showDialog(): The rule flagged await showDialog(...) (and showModalBottomSheet, showSnackBar) as needing a mounted check even when no prior await existed. The _hasAwaitBefore() helper searched for 'await ' in the source text before the MethodInvocation offset, but when the call was itself the awaited expression (await showDialog(...)), its own await keyword appeared in that substring. Now uses the parent AwaitExpression's offset when the target is directly awaited, so only genuinely preceding awaits are detected.

4.9.12 #

Fixed #

  • avoid_late_for_nullable false positive on generic inner type nullability: The rule used string-based detection (toSource().endsWith('?')) which could false-positive when ? appeared on inner generic parameters (e.g., late Future<String?> or late Future<({List<CountryEnum>? countries, int count})>). Replaced with AST-based question token check that only detects nullability on the outer type itself.

  • avoid_nested_assignments false positive on cascade expressions: The rule flagged idiomatic Dart cascade property setters (obj..field = value) as nested assignments. In the AST, cascade sections are AssignmentExpression nodes with a CascadeExpression parent, which was not in the skip list. Added CascadeExpression parent check so cascades are correctly treated as safe sequential assignments.

  • Audited all string-based nullability checks across 8 rule files: Replaced fragile toSource().endsWith('?') patterns with AST-based question token checks in dependency_injection_rules, equatable_rules, hive_rules, type_safety_rules, stylistic_null_collection_rules, disposal_rules, and async_rules. Extracted shared isOuterTypeNullable() utility to type_annotation_utils.dart.

Changed #

  • DX audit now enforces message length on opinionated rules: The _audit_dx.py scoring previously skipped all length checks for LintImpact.opinionated rules (stylistic tier), allowing 30-50 character messages to score 100%. Added 100-character minimum threshold (-10 penalty). Also added "opinionated" to the per-impact breakdown display so these rules appear in the DX report alongside critical/high/medium/low.

  • Improved problemMessage for ~98 stylistic rules: Expanded short problem messages (many under 50 characters) to include rationale and consequences, matching the quality bar already enforced on critical/high/medium rules. Each message now explains what was detected AND why it matters, giving users enough context to evaluate whether to enable the rule.

  • init CLI now outputs ANSI colors on Windows: The dart run saropa_lints:init command previously produced monocolor output on Windows terminals because the color detection was too conservative. Now enables Virtual Terminal Processing via the Windows API (SetConsoleMode) at startup, uses stdout.supportsAnsiEscapes as the primary check, and detects VS Code's integrated terminal (TERM_PROGRAM). Honors NO_COLOR and FORCE_COLOR environment variables. Color support result is cached for performance.

  • Aligned DiagnosticSeverity with actual risk across ~28 rules: Audited all 1,681 rules for severity-vs-impact mismatches. Upgraded 9 crash-path rules from INFO to ERROR (permission failures, missing error boundaries, unhandled navigation results). Upgraded 14 conditional-failure rules from INFO to WARNING (unsafe casts, missing platform checks, unlogged errors). Downgraded 9 style-only rules from WARNING to INFO (naming conventions, test cleanup, empty spreads). Downgraded 2 borderline ERROR rules to WARNING (avoid_nested_scaffolds, require_focus_node_dispose). Also aligned LintImpact for 3 rules whose internal impact level was inconsistent with their crash-path language.


4.9.11 #

Fixed #

  • require_auth_check false positive on Flutter UI functions: The rule flagged any async function whose name contained a protected keyword (e.g. settings, user, profile) because the return type check contains('Future') matched every async function, not just API response handlers. Functions like showContactSettingsDialog({required BuildContext context}) were incorrectly reported as missing auth checks. Now requires Response/HttpResponse in the return type or a Request/HttpRequest parameter to confirm the function is a server-side handler. Also skips Flutter UI function prefixes (show, build, init, dispose, create, navigate, on, etc.), private functions, and functions with Flutter UI parameter types (BuildContext, Widget, etc.). Expanded recognized auth check patterns to include requireAuth, validateToken, hasPermission, authGuard, and others.

  • no_equal_conditions false positive on if-case chains: The rule flagged if (x case 1) {} else if (x case 2) {} as duplicate conditions because it only compared the scrutinee expression (x) and ignored the differing case pattern values (1 vs 2). Each if-case branch tests a distinct constant pattern, so they are not duplicates. Now compares the full if-case expression including the pattern, not just the scrutinee.

  • avoid_unsafe_cast false positive on safe casts: The rule flagged all non-nullable as casts regardless of type safety. Now skips provably safe casts: cast to Object (every Dart value is an Object), same-type casts, and upcasts to a supertype (e.g. int as num). Fixes false positives on patterns like error as Object in catch handlers.

  • nullify_after_dispose false positive on debounce/reset patterns: The rule flagged .cancel() calls on nullable Timer or StreamSubscription fields even when the field was immediately reassigned to a new instance in the same block (e.g., cancel-then-recreate debounce pattern). Since the field is reassigned right after, nullifying it is pointless. Now checks for reassignment after the cancel call and skips reporting when the field receives a new non-null value.

  • prefer_fractional_sizing false positive in collection contexts: The rule flagged MediaQuery.size * fraction inside .add() calls and list literals, where FractionallySizedBox cannot work because parent constraints may be unbounded (e.g. widgets assembled into a list for a horizontal ScrollView). Now walks up the AST and skips reporting when the expression is inside a ListLiteral, .add(), or .insert() call.

Added #

  • prefer_clip_r_superellipse (stylistic): Suggests ClipRSuperellipse instead of ClipRRect for smoother rounded corners. Quick fix replaces the widget preserving all arguments. Requires Flutter 3.32+.
  • prefer_clip_r_superellipse_clipper (stylistic): Suggests ClipRSuperellipse instead of ClipRRect when a custom clipper is present. No quick fix — CustomClipper<RRect> must be manually rewritten as CustomClipper<RSuperellipse>.

Changed #

  • Audit report filenames include project name: Exported audit reports now include the project name from pubspec.yaml in the filename (e.g. 20260202_090730_saropa_lints_full_audit.md instead of 20260202_090730_full_audit.md). Applies to both full audit and DX audit reports.

  • DX audit per-tier breakdown: The DX Message Quality report now includes a "By tier" section showing passing/total counts and percentages for each tier (Essential, Recommended, Professional, Comprehensive, Insanity, Stylistic), color-coded to match the tier distribution display.

  • DX message quality improvements (27 rules): Improved problem and correction messages to pass DX audit checks. Essential tier now 159/159 (100%), Stylistic tier 134/134 (100%). Eliminated all "should have" (3), passive voice (2), and "better" (1) violations. Rewrote 4 "Avoid" prefix messages (avoid_throw_in_finally, avoid_test_sleep, avoid_nested_scaffolds, avoid_inherited_widget_in_initstate) to state what was detected. Expanded short messages for 15 essential rules including avoid_unsafe_collection_methods, always_remove_listener, require_scroll_controller_dispose, require_focus_node_dispose, and others. Fixed curly-quote encoding in require_animation_disposal and avoid_mounted_in_setstate.

  • DX audit grouping: Length-based issue categories no longer fragment by exact character count. Rules that are "too short" or have a "correction too short" are now grouped into single buckets per threshold instead of one bucket per distinct length.

  • analysis_options_custom.yaml now includes a STYLISTIC RULES section: All opinionated/stylistic rules are listed by category between PLATFORM SETTINGS and RULE OVERRIDES, with descriptions read from rule metadata. Users can toggle individual rules (true/false) to opt in without the --stylistic flag. Existing files are automatically updated on next init run — new rules are added as false, existing values are preserved, and rules already in RULE OVERRIDES are skipped.

  • avoid_shrink_wrap_in_scroll reclassified as stylistic (fixes #75): Downgraded from WARNING to INFO and moved from Recommended tier to Stylistic (opinionated). The rule flags every shrinkWrap: true unconditionally, but shrinkWrap is often required (e.g. ListView inside a Column with NeverScrollableScrollPhysics and bounded itemCount). The genuinely dangerous cases are already covered by avoid_shrinkwrap_in_scrollview (context-aware, nested-scrollable + physics check) and avoid_shrink_wrap_expensive (physics-aware). Updated correction message to acknowledge legitimate use cases. Fixed DartDoc that was a copy-paste error from a Form/validator rule.

  • Renamed report files from _saropa_full.log / _saropa_summary.md to _saropa_lint_report_full.log / _saropa_lint_report_summary.log for clearer identification and consistent .log extension


4.9.10 #

Fixed #

  • prefer_late_final false positive on helper methods called from multiple sites: The rule counted assignment AST nodes rather than effective runtime assignments. A late field assigned in a helper method (e.g., _fetchData()) that was called from multiple sites (e.g., initState() and didUpdateWidget()) was incorrectly flagged as single-assignment. Now tracks which methods assign to which fields and counts call sites — if an assigning method is called from N > 1 sites, the field is treated as reassigned.

4.9.9 #

Fixed #

  • require_ios_push_notification_capability false positive on unrelated identifiers: The rule used substring matching on the target expression's source code to detect push notification class names, but also matched method-name patterns (e.g., onMessage) against target source. This caused false positives on classes like CommonMessagePanel which contains the substring onMessage. Now splits patterns into class names (FirebaseMessaging, OneSignal, UNUserNotificationCenter) and method names (onMessage, getToken, etc.) — only class names are checked against target source, while method names are matched exactly against the invoked method name only.

  • require_dispose_pattern false positive on widget classes: The rule flagged StatefulWidget and StatelessWidget subclasses that receive disposable types (e.g., TextEditingController) as constructor parameters. Widget classes are immutable and have no dispose() method — lifecycle cleanup belongs in the corresponding State class, which was already skipped. Now skips StatefulWidget and StatelessWidget in addition to State.


4.9.8 #

  • avoid_unguarded_debug no longer flags debug() calls: The rule previously flagged every bare debug() call without a guard or level: parameter. The project's debug() function is production-safe logging infrastructure with its own level filtering and Crashlytics routing — it does not need external guards. The rule now only flags debugPrint(), which bypasses all filtering and writes directly to the console. Also added recognition of debug*/_debug* method names as implicit guards for debugPrint() calls inside debug helper methods.

  • prefer_dispose_before_new_instance false positive on late final fields: The rule flagged assignments to late final fields in helper methods called from initState(). Since late final fields can only be assigned once, there is no previous instance to leak. The rule now skips late final fields entirely.

  • avoid_unused_instances false positive on fire-and-forget constructors: The rule flagged Future.delayed(...), Timer(...), and similar constructors as unused instances, even though they are intentionally used for side effects without capturing the return value. Added an allowlist for Future and Timer types to skip the warning for known fire-and-forget patterns.

  • nullify_after_dispose false positive on final/non-nullable fields: The rule flagged disposal calls on fields declared as final or with non-nullable types (e.g., final ScrollController _scrollController), where setting the field to null is impossible. Now skips fields that are final or have a non-nullable type, only flagging nullable non-final fields where nullification is actionable.

  • avoid_change_notifier_in_widget false positive on non-ChangeNotifier classes: The rule used substring matching on type names (Model, Controller, Notifier, ViewModel), causing false positives on plain data classes like ContactModel that don't extend ChangeNotifier. Now resolves the actual class hierarchy via the analyzer's type system and checks allSupertypes for ChangeNotifier. Falls back to name matching (without the overly broad Model pattern) only when type resolution is unavailable.


4.9.7 #

Added #

  • Quick fixes for 8 rules across 5 files: Added one-click fixes for require_deprecation_message (replace @deprecated with @Deprecated), avoid_bluetooth_scan_without_timeout (add scan timeout parameter), require_geolocator_error_handling (wrap in try-catch), avoid_touch_only_gestures (add onLongPress callback), prefer_catch_over_on (remove on clause from catch), prefer_expect_over_assert_in_tests (replace assert with expect), require_pdf_error_handling and require_sqflite_error_handling (wrap in try-catch).

  • Shared WrapInTryCatchFix utility: Extracted common try-catch wrapping fix to ignore_fixes.dart for reuse across rules that require error handling (PDF, SQLite, geolocation, etc.).

Improved #

  • DX message quality for code_quality_rules: Expanded problemMessage and correctionMessage text for all 87 rules with DX issues in code_quality_rules.dart. Removes "Avoid" prefixes, fixes vague language, explains consequences, and brings messages above minimum length thresholds.

  • DX message quality for control_flow_rules: Expanded problemMessage and correctionMessage text for all 28 rules with DX issues in control_flow_rules.dart. Removes "Avoid" prefixes, explains consequences of control flow anti-patterns, and meets minimum message length thresholds.


4.9.5 #

Added #

  • Platform configuration in analysis_options_custom.yaml: New platforms: section lets you disable lint rules for platforms your project doesn't target. Only ios and android are enabled by default — enable macos, web, windows, or linux if your project targets those platforms. Rules shared across multiple platforms (e.g., Apple Sign In applies to both iOS and macOS) stay enabled as long as at least one of their platforms is active. User overrides still take precedence over platform filtering.

  • Platform migration for existing configs: Running dart run saropa_lints:init on projects with an existing analysis_options_custom.yaml automatically adds the platforms: section if missing, without disturbing existing settings.

Removed #

  • Removed rule: prefer_const_child_widgets: Redundant. When the parent widget is const, Dart's const context propagation already makes all children implicitly const — there is no additional performance benefit. When the parent is non-const, the built-in prefer_const_literals_to_create_immutables lint already covers the same case.

Fixed #

  • init created backup files even when nothing changed: Running dart run saropa_lints:init repeatedly created a timestamped .bak file on every invocation, even when the output content was identical. Backups are now only created when the file content has actually changed.

  • Plugin tier logging always showed essential: The getLintRules() log message reported tier: essential regardless of the actual tier configured via dart run saropa_lints:init. The plugin read tier from a YAML key that the init command never wrote, so it always fell back to essential. Now infers the effective tier by comparing the final enabled rule set against each tier's definition, reporting the correct tier (e.g., tier: professional).

  • // ignore_for_file: directives now respected: File-level ignore directives were not being checked by custom lint rules. Rules still fired even when // ignore_for_file: rule_name was present. The check now runs once per rule per file before any AST callbacks, efficiently skipping the entire rule when the file opts out. Supports both underscore (rule_name) and hyphen (rule-name) formats.

  • Unresolvable rules now reported: Rules defined in tier configurations or explicit YAML overrides but missing from the rule factory registry are now logged as warnings during plugin initialization. This makes the rule count mismatch between init and the plugin visible (e.g., WARNING: 18 rule(s) could not be resolved).

  • require_isar_nullable_field false positive on static fields: The rule incorrectly flagged static fields in @collection classes. Static fields are not persisted by Isar and should be skipped.

Improved #

  • DX message quality for widget_patterns_rules: Expanded problemMessage and correctionMessage text for all 87 rules in widget_patterns_rules.dart. Messages now explain consequences, remove vague language ("Avoid", "Consider", "best practices"), and meet minimum length thresholds for the DX audit.

  • prefer_spacing_over_sizedbox rule rewritten: Now detects the alternating [content, spacer, content, ...] pattern in Row/Column children instead of just counting SizedBox widgets. Also detects Spacer() widgets, removed false Wrap support, and added a quick fix that inserts the spacing parameter and removes spacer children.

Changed #

  • init skips writing unchanged analysis_options.yaml: Running dart run saropa_lints:init with the same tier and options no longer overwrites the file if the content is identical. Shows ✓ No changes needed instead.

  • Plugin version now read dynamically from pubspec.yaml: The version in createPlugin() was hardcoded at 4.8.0 and never updated. Replaced with a lazy resolver that reads the actual version from pubspec.yaml via .dart_tool/package_config.json at runtime. Works for both path dependencies and pub cache installs. The version string never needs manual updates again.

Added #

  • New rule: avoid_ignore_trailing_comment (Recommended tier, WARNING): Warns when // ignore: or // ignore_for_file: directives have trailing text after the rule names — either a // comment or a - explanation. The custom_lint_builder framework uses exact string matching on rule codes, so any trailing text silently breaks suppression. Quick fix moves the text to a // comment on the line above the directive.

  • New rule: prefer_positive_conditions (Stylistic, INFO): Warns when an if/else or ternary uses a negative condition (!expr or !=) that can be flipped to a positive form with branches swapped. Only flags straightforward cases — skips compound conditions, else-if chains, and complex negations. Quick fix available to invert the condition and swap both branches.

  • New rule: prefer_positive_conditions_first (Stylistic, INFO): Warns when guard clauses use negated conditions (== null, !expr) with early returns, pushing the happy path deeper into the function. Suggests restructuring to place the positive condition first. Opinionated — not included in any tier by default.

  • New rule: missing_use_result_annotation (Comprehensive tier, INFO): Warns when a function returns a value without @useResult annotation. Callers may accidentally ignore the return value, leading to missed error handling or lost data transformations.

  • VS Code extension: Scan file or folder: Right-click any .dart file or folder in the Explorer sidebar and select "Scan with Saropa Lints" to instantly see all diagnostics for that path. Uses diagnostics already computed by the Dart analysis server — no re-scanning required.

  • Quick fixes for 7 stylistic widget rules: Added one-click fixes for prefer_sizedbox_over_container, prefer_container_over_sizedbox, prefer_borderradius_circular, prefer_expanded_over_flexible, prefer_flexible_over_expanded, prefer_edgeinsets_symmetric, and prefer_edgeinsets_only. Each fix handles const preservation and argument reordering.


4.9.4 #

Changed #

Strict Isar migration safety: Replaced require_isar_non_nullable_migration with require_isar_nullable_field. The previous rule allowed non-nullable fields if they had default values, but Isar bypasses constructors/initializers during hydration, leading to crashes on legacy data. The new rule mandates that all fields in @collection classes (except Id) must be nullable (String?) to strictly prevent TypeError during version upgrades.

Added #

Auto-fix for Isar fields: Added dart fix support for the new require_isar_nullable_field rule to automatically append ? to non-nullable fields in Isar collections.

4.9.3 #

Fixed #

  • Progress bar terminal compatibility: Use stdout with space-overwrite approach instead of ANSI escape codes for broader terminal support. Added clear labels (Files:, Issues:, ETA:) to progress output for clarity.

  • Version detection when run from other projects: Fixed init command showing "vunknown" when run via dart run saropa_lints:init from dependent projects. Now correctly reads version from package location found in package_config.json.

  • Full filenames in progress: Removed truncation of long filenames in progress display.


4.9.2 #

Added #

  • Dynamic version detection in init: The init command now reads the version from pubspec.yaml at runtime instead of using a hardcoded constant. Also displays the package source (local path vs pub.dev) to help diagnose version mismatches.

Changed #

  • Full problem messages in generated config: The init command now outputs complete rule descriptions in analysis_options.yaml comments instead of truncating at 60 characters. This improves searchability when looking for specific rule behaviors.

4.9.1 #

Added #

  • In-place progress bar with colors: Terminal output now shows a visual progress bar (████████░░░░) that updates in-place instead of scrolling thousands of lines. Includes color coding (green for progress, red/yellow for issues, cyan for file counts), ETA display, and current file indicator. Cross-platform color detection for Windows Terminal, ConEmu, and Unix terminals. Disable with SAROPA_LINTS_PROGRESS=false.

  • Issue limit for large codebases: New max_issues setting (default: 1000) stops running WARNING/INFO rules after the limit is reached, providing real speedup on legacy projects. ERROR-severity rules always run regardless of limit. Configure in analysis_options_custom.yaml:

    max_issues: 500  # Or 0 for unlimited
    

    The init command now generates this file automatically with sensible defaults.

Changed #

  • Summary output reduced: Final summary now shows top 5 files/rules instead of 10, with color-coded severity indicators and slow file tracking (files taking >2s are reported at the end instead of interrupting progress).

4.9.0 and Earlier #

For details on the initial release and versions 0.1.0 through 4.9.0, please refer to CHANGELOG_ARCHIVE.md.

3
likes
0
points
5.38k
downloads

Publisher

verified publishersaropa.com

Weekly Downloads

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

Homepage
Repository (GitHub)
View/report issues

Topics

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

License

unknown (license)

Dependencies

analysis_server_plugin, analyzer, analyzer_plugin, json2yaml, yaml

More

Packages that depend on saropa_lints