saropa_lints 4.9.10
saropa_lints: ^4.9.10 copied to clipboard
1677+ custom lint rules with 221 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.6.2.
** See the current published changelog: saropa_lints/changelog
4.9.10 #
Fixed #
prefer_late_finalfalse 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()anddidUpdateWidget()) 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_capabilityfalse 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 likeCommonMessagePanelwhich contains the substringonMessage. 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_patternfalse positive on widget classes: The rule flaggedStatefulWidgetandStatelessWidgetsubclasses that receive disposable types (e.g.,TextEditingController) as constructor parameters. Widget classes are immutable and have nodispose()method — lifecycle cleanup belongs in the correspondingStateclass, which was already skipped. Now skipsStatefulWidgetandStatelessWidgetin addition toState.
4.9.8 #
-
avoid_unguarded_debugno longer flagsdebug()calls: The rule previously flagged every baredebug()call without a guard orlevel:parameter. The project'sdebug()function is production-safe logging infrastructure with its own level filtering and Crashlytics routing — it does not need external guards. The rule now only flagsdebugPrint(), which bypasses all filtering and writes directly to the console. Also added recognition ofdebug*/_debug*method names as implicit guards fordebugPrint()calls inside debug helper methods. -
prefer_dispose_before_new_instancefalse positive onlate finalfields: The rule flagged assignments tolate finalfields in helper methods called frominitState(). Sincelate finalfields can only be assigned once, there is no previous instance to leak. The rule now skipslate finalfields entirely. -
avoid_unused_instancesfalse positive on fire-and-forget constructors: The rule flaggedFuture.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 forFutureandTimertypes to skip the warning for known fire-and-forget patterns. -
nullify_after_disposefalse positive on final/non-nullable fields: The rule flagged disposal calls on fields declared asfinalor with non-nullable types (e.g.,final ScrollController _scrollController), where setting the field tonullis impossible. Now skips fields that arefinalor have a non-nullable type, only flagging nullable non-final fields where nullification is actionable. -
avoid_change_notifier_in_widgetfalse positive on non-ChangeNotifier classes: The rule used substring matching on type names (Model,Controller,Notifier,ViewModel), causing false positives on plain data classes likeContactModelthat don't extendChangeNotifier. Now resolves the actual class hierarchy via the analyzer's type system and checksallSupertypesforChangeNotifier. Falls back to name matching (without the overly broadModelpattern) 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@deprecatedwith@Deprecated),avoid_bluetooth_scan_without_timeout(add scan timeout parameter),require_geolocator_error_handling(wrap in try-catch),avoid_touch_only_gestures(addonLongPresscallback),prefer_catch_over_on(removeonclause from catch),prefer_expect_over_assert_in_tests(replaceassertwithexpect),require_pdf_error_handlingandrequire_sqflite_error_handling(wrap in try-catch). -
Shared
WrapInTryCatchFixutility: Extracted common try-catch wrapping fix toignore_fixes.dartfor reuse across rules that require error handling (PDF, SQLite, geolocation, etc.).
Improved #
-
DX message quality for code_quality_rules: Expanded
problemMessageandcorrectionMessagetext for all 87 rules with DX issues incode_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
problemMessageandcorrectionMessagetext for all 28 rules with DX issues incontrol_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: Newplatforms:section lets you disable lint rules for platforms your project doesn't target. Onlyiosandandroidare enabled by default — enablemacos,web,windows, orlinuxif 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:initon projects with an existinganalysis_options_custom.yamlautomatically adds theplatforms:section if missing, without disturbing existing settings.
Removed #
- Removed rule:
prefer_const_child_widgets: Redundant. When the parent widget isconst, Dart's const context propagation already makes all children implicitlyconst— there is no additional performance benefit. When the parent is non-const, the built-inprefer_const_literals_to_create_immutableslint already covers the same case.
Fixed #
-
initcreated backup files even when nothing changed: Runningdart run saropa_lints:initrepeatedly created a timestamped.bakfile 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: ThegetLintRules()log message reportedtier: essentialregardless of the actual tier configured viadart run saropa_lints:init. The plugin read tier from a YAML key that the init command never wrote, so it always fell back toessential. 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_namewas 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
initand the plugin visible (e.g.,WARNING: 18 rule(s) could not be resolved). -
require_isar_nullable_fieldfalse positive on static fields: The rule incorrectly flaggedstaticfields in@collectionclasses. Static fields are not persisted by Isar and should be skipped.
Improved #
-
DX message quality for widget_patterns_rules: Expanded
problemMessageandcorrectionMessagetext for all 87 rules inwidget_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_sizedboxrule rewritten: Now detects the alternating[content, spacer, content, ...]pattern in Row/Column children instead of just counting SizedBox widgets. Also detectsSpacer()widgets, removed falseWrapsupport, and added a quick fix that inserts thespacingparameter and removes spacer children.
Changed #
-
initskips writing unchangedanalysis_options.yaml: Runningdart run saropa_lints:initwith the same tier and options no longer overwrites the file if the content is identical. Shows✓ No changes neededinstead. -
Plugin version now read dynamically from
pubspec.yaml: The version increatePlugin()was hardcoded at4.8.0and never updated. Replaced with a lazy resolver that reads the actual version frompubspec.yamlvia.dart_tool/package_config.jsonat 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. Thecustom_lint_builderframework 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 (!expror!=) 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@useResultannotation. 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
.dartfile 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, andprefer_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
initcommand showing "vunknown" when run viadart run saropa_lints:initfrom dependent projects. Now correctly reads version from package location found inpackage_config.json. -
Full filenames in progress: Removed truncation of long filenames in progress display.
4.9.2 #
Added #
- Dynamic version detection in init: The
initcommand now reads the version frompubspec.yamlat 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
initcommand now outputs complete rule descriptions inanalysis_options.yamlcomments 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 withSAROPA_LINTS_PROGRESS=false. -
Issue limit for large codebases: New
max_issuessetting (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 inanalysis_options_custom.yaml:max_issues: 500 # Or 0 for unlimitedThe 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 #
Added #
-
Automatic ignore quick fixes for all rules: Every rule extending
SaropaLintRulenow automatically provides two quick fixes: "Ignore 'rule_name' for this line" (inserts// ignore: rule_nameabove the violation with matching indentation) and "Ignore 'rule_name' for this file" (inserts// ignore_for_file: rule_nameafter header comments). This enables one-click suppression for any lint violation. Rules can disable this viaincludeIgnoreFixes => falsefor critical security rules. NewcustomFixesgetter allows rules to provide specific fixes while retaining ignore fixes. Insertion logic respects existing ignore_for_file comments (groups them together) and copyright/license headers (inserts after, not before). -
Quick fixes for 26 additional rules: Added automated quick fix support for frequently triggered rules to enable one-click corrections. Stylistic:
prefer_if_null_over_ternary(replacesx != null ? x : d→x ?? d),prefer_ternary_over_if_null(replacesx ?? d→x != null ? x : d),prefer_where_type_over_where_is(replaces.where((e) => e is T)→.whereType<T>()),prefer_nullable_over_late(replaceslate Type→Type?),prefer_rethrow_over_throw_e(replacesthrow e;→rethrow;). Image:prefer_image_size_constraints(addscacheWidth/cacheHeight),require_cached_image_dimensions(addsmemCacheWidth/memCacheHeight),prefer_cached_image_fade_animation(addsfadeInDuration),prefer_image_picker_request_full_metadata(addsrequestFullMetadata: false),avoid_image_picker_large_files(addsimageQuality: 85),require_image_cache_dimensions(addscacheWidth/cacheHeight). Dependency Injection:avoid_singleton_for_scoped_dependencies(changes toregisterFactorywith factory wrapper),require_typed_di_registration(adds<Type>generic parameter),avoid_functions_in_register_singleton(changes toregisterLazySingleton),prefer_lazy_singleton_registration(changes toregisterLazySingletonwith factory wrapper). Class/Constructor:prefer_const_string_list(addsconstkeyword),prefer_declaring_const_constructor(addsconstkeyword),prefer_final_class(addsfinalmodifier),prefer_interface_class(addsinterfacemodifier),prefer_base_class(addsbasemodifier). Scroll/List:avoid_refresh_without_await(addsasynckeyword),prefer_item_extent(addsitemExtentparameter),prefer_prototype_item(addsprototypeItemparameter),require_key_for_reorderable(addskeyparameter),require_add_automatic_keep_alives_off(addsaddAutomaticKeepAlives: false). Control Flow:prefer_async_only_when_awaiting(removes unnecessaryasynckeyword). All fixes include comprehensive documentation and preserve code formatting.
Changed #
- DX message quality improvements for 25 medium/low priority rules: Improved lint message clarity by replacing vague language with specific, actionable problem descriptions. Changes include: (1) Replaced "better performance" with quantified impacts (e.g., "reduces throughput by 10-100x", "causes full table scan", "recalculates layout on every scroll"), (2) Replaced "consider X" suggestions with direct problem statements (e.g., "loads all entries into memory at once", "collects EXIF metadata by default", "prevents garbage collection under memory pressure"), (3) Replaced "should be/have" with consequence explanations (e.g., "shows blank space during load", "locks out users with damaged sensors", "slow code review and refactoring"). Affected rules:
prefer_sqflite_batch,require_database_index,prefer_firestore_batch_write,prefer_marker_clustering,prefer_item_extent,prefer_lazy_box_for_large,prefer_hive_value_listenable,prefer_image_size_constraints,require_image_loading_placeholder,prefer_image_picker_request_full_metadata,prefer_weak_references_for_cache,prefer_wildcard_for_unused_param,require_svg_error_handler,prefer_deferred_loading_web,require_menu_bar_for_desktop,move_records_to_typedefs,prefer_sorted_pattern_fields,prefer_simpler_patterns_null_check,prefer_sorted_record_fields,prefer_immutable_provider_arguments,prefer_prototype_item,require_biometric_fallback,prefer_data_masking,avoid_screenshot_sensitive,prefer_notifier_over_state. - Audit report organization improved: Refactored audit scripts to reduce repetition and improve report readability.
4.8.8 #
Added #
- Quick fixes for 9 rules: Added automated quick fix support for frequently triggered rules to enable one-click corrections. Internationalization:
require_directional_widgets(convertsEdgeInsets.only(left: x)→EdgeInsetsDirectional.only(start: x)for RTL support),prefer_intl_name(addsnameparameter toIntl.message()),prefer_providing_intl_description(addsdescparameter with TODO),prefer_providing_intl_examples(addsexamplesparameter). Stylistic:prefer_double_quotes(converts single quotes to double quotes),prefer_object_over_dynamic(replacesdynamic→Object?),prefer_dynamic_over_object(replacesObject?→dynamic). Hive:prefer_hive_lazy_box(convertsHive.openBox()→Hive.openLazyBox()for large collections). Logging:prefer_logger_over_print(replacesprint()→log()from dart:developer). All fixes include comprehensive documentation and preserve code formatting.
Changed #
- Widget rules refactored into 3 focused files: Split the 18,953-line
flutter_widget_rules.dartinto three thematic categories for improved maintainability and discoverability:widget_lifecycle_rules.dart(34 rules for State management, dispose, setState patterns),widget_layout_rules.dart(65 rules for layout structure, constraints, rendering), andwidget_patterns_rules.dart(103 rules for best practices, accessibility, themes, platform-specific patterns). All 202 rules remain functionally identical with no breaking changes. This is a pure code reorganization with no logic modifications. - README badges upgraded: Replaced static badges with dynamic, auto-updating badges organized into logical groups (CI/CD, pub.dev metrics, GitHub activity, technical info). Added popularity, likes, stars, forks, last commit, issues count, Dart SDK version, and Flutter platform badges with appropriate logos. The pub version badge now auto-updates from pub.dev, eliminating the need for manual version updates in documentation.
- DX message quality improvements for 24 critical/high impact rules: Achieved 100% pass rate (293/293 high, 61/61 critical) for developer experience message quality audit. Fixed missing consequences (added security/accessibility impact statements), expanded too-short messages to meet 180+ character requirement (added specific failure scenarios and technical details), replaced generic terms with specific types (e.g., "controller" → "animation controller", "widget" → "StatefulWidget", "resource" → "memory and processing resources"), and enhanced correction messages to meet 80+ character requirement with actionable guidance. Affected rules:
avoid_deep_link_sensitive_params,avoid_color_only_indicators,require_cached_image_dimensions,avoid_image_picker_large_files,require_notification_initialize_per_platform,avoid_unsafe_deserialization,require_image_semantics,require_vsync_mixin,avoid_unassigned_stream_subscriptions,avoid_obs_outside_controller,require_key_for_collection,require_workmanager_constraints,prefer_riverpod_auto_dispose,require_test_widget_pump,avoid_notification_payload_sensitive,require_url_validation,avoid_bloc_context_dependency,avoid_provider_value_rebuild,avoid_riverpod_notifier_in_build,avoid_bloc_business_logic_in_ui,require_mock_http_client,avoid_dynamic_json_access,require_enum_unknown_value,avoid_future_tostring.
4.8.7 #
Fixed #
avoid_context_across_asyncandavoid_context_after_await_in_staticfalse positives on mounted ternary guards: Fixed two issues where safe mounted guard patterns were incorrectly flagged: (1) Ternary guards in catch blocks (context.mounted ? context : null) were not detected due to premature AST traversal termination at statement boundaries. (2) Nullable-safe mounted checks (context?.mounted ?? false ? context : null) were not recognized. Both patterns now correctly suppress violations.- Trailing ignore comments now work for all rules: Fixed critical bug in
IgnoreUtils._checkTokenCommentsOnLine()where trailing// ignore:comments (same-line format:final x = y; // ignore: rule_name) were not detected. Root cause: the function checked if the token was past the target line before examining the token's preceding comments, causing it to miss trailing comments attached to the next line's first token (a common Dart tokenization pattern). The fix reorders the logic to examine comments first, then check token position. Also added statement-level trailing comment detection for cases where the comment is at the end of a statement rather than immediately after the expression. Now properly detects both leading (// ignore:on preceding line) and trailing (// ignore:on same line) ignore patterns for all rule name formats (underscores and hyphens). Affects all 1677 saropa_lints rules.
4.8.6 #
Added #
TestRelevanceenum: New three-value enum (never,always,testOnly) for granular control over whether lint rules run on test files. Rules can now declare their test file relationship explicitly instead of using a boolean flag.
Changed #
- Rules now skip test files by default: The default
testRelevanceisTestRelevance.never, meaning ~1600 production-focused rules no longer fire on test files. This eliminates thousands of irrelevant violations (e.g.,prefer_matcher_over_equals,move_variable_closer_to_its_usage,no_empty_string) that were flooding test code. Rules that should run on tests can overridetestRelevance => TestRelevance.always. - Backwards-compatible auto-detection: Rules using
applicableFileTypes => {FileType.test}are automatically treated asTestRelevance.testOnlywith no code changes required. - DX message quality improvements for 25 rules: Expanded
problemMessagetext for rules that were below the 180-character minimum or lacked a clear consequence. Each message now explains both the problem and its real-world impact (e.g., memory leaks, crashes, stale UI). Affected rules:prefer_consumer_widget,avoid_bloc_in_bloc,avoid_change_notifier_in_widget,require_provider_dispose,require_error_handling_in_async,require_getx_controller_dispose,dispose_provider_instances,avoid_getx_rx_inside_build,avoid_mutable_rx_variables,dispose_provided_instances,avoid_listen_in_async,avoid_equatable_mutable_collections,avoid_static_state,avoid_provider_in_init_state,require_bloc_manual_dispose,prefer_bloc_listener_for_side_effects,avoid_bloc_context_dependency,avoid_provider_value_rebuild,avoid_riverpod_notifier_in_build,avoid_bloc_business_logic_in_ui,require_mock_http_client,avoid_dynamic_json_access,require_enum_unknown_value,require_image_semantics,require_badge_semantics.
Fixed #
avoid_isar_clear_in_productionfalse positive on non-Isar types: The rule previously flagged every.clear()call regardless of receiver type, producing ERROR-severity false positives onMap.clear(),List.clear(),Set.clear(),TextEditingController.clear(), and any other class with aclear()method. Now uses static type checking to verify the receiver is anIsarinstance before reporting.require_error_case_testsfalse positives: Expanded test name keyword detection to recognize boundary/defensive test patterns (null,empty,boundary,edge,negative,fallback,missing) in addition to error keywords. Updated correction message to acknowledge that test files for pure enums, defensive try/catch code, and non-nullable extension methods legitimately have no error-throwing paths.prefer_setup_teardownfalse positive on independent locals: The rule no longer flags repeated primitive variable declarations (int count = 0,const int iterations = 1000) as duplicated setup code. Simple literal initializations and const declarations are now excluded from the setup signature comparison, so only meaningful setup (object construction, service initialization) triggers the suggestion.require_change_notifier_disposefalse positives: Fixed three categories of false positive: (1) Fields not owned by the class (e.g., controllers received fromAutocompletecallbacks) are no longer flagged — the rule now uses_findOwnedFieldsOfTypeownership detection consistent with sibling disposal rules. (2) Generic container types likeMap<K, ScrollController>no longer match via substring — exact type matching is now used. (3) Extension method disposal wrappers (e.g.,.disposeSafe()) are now recognized as valid disposal patterns by all disposal rules.
Deprecated #
skipTestFilesgetter: Replaced bytestRelevance. The old boolean getter still compiles but is marked@Deprecated. Migration:skipTestFiles => trueis now the default;skipTestFiles => falsebecomestestRelevance => TestRelevance.always.
4.8.5 #
Added #
TestRelevanceenum: New three-value enum (never,always,testOnly) for granular control over whether lint rules run on test files. Rules can now declare their test file relationship explicitly instead of using a boolean flag.
Changed #
- Rules now skip test files by default: The default
testRelevanceisTestRelevance.never, meaning ~1600 production-focused rules no longer fire on test files. This eliminates thousands of irrelevant violations (e.g.,prefer_matcher_over_equals,move_variable_closer_to_its_usage,no_empty_string) that were flooding test code. Rules that should run on tests can overridetestRelevance => TestRelevance.always. - Backwards-compatible auto-detection: Rules using
applicableFileTypes => {FileType.test}are automatically treated asTestRelevance.testOnlywith no code changes required. - DX message quality improvements for 25 rules: Expanded
problemMessagetext for rules that were below the 180-character minimum or lacked a clear consequence. Each message now explains both the problem and its real-world impact (e.g., memory leaks, crashes, stale UI). Affected rules:prefer_consumer_widget,avoid_bloc_in_bloc,avoid_change_notifier_in_widget,require_provider_dispose,require_error_handling_in_async,require_getx_controller_dispose,dispose_provider_instances,avoid_getx_rx_inside_build,avoid_mutable_rx_variables,dispose_provided_instances,avoid_listen_in_async,avoid_equatable_mutable_collections,avoid_static_state,avoid_provider_in_init_state,require_bloc_manual_dispose,prefer_bloc_listener_for_side_effects,avoid_bloc_context_dependency,avoid_provider_value_rebuild,avoid_riverpod_notifier_in_build,avoid_bloc_business_logic_in_ui,require_mock_http_client,avoid_dynamic_json_access,require_enum_unknown_value,require_image_semantics,require_badge_semantics.
Fixed #
avoid_isar_clear_in_productionfalse positive on non-Isar types: The rule previously flagged every.clear()call regardless of receiver type, producing ERROR-severity false positives onMap.clear(),List.clear(),Set.clear(),TextEditingController.clear(), and any other class with aclear()method. Now uses static type checking to verify the receiver is anIsarinstance before reporting.require_error_case_testsfalse positives: Expanded test name keyword detection to recognize boundary/defensive test patterns (null,empty,boundary,edge,negative,fallback,missing) in addition to error keywords. Updated correction message to acknowledge that test files for pure enums, defensive try/catch code, and non-nullable extension methods legitimately have no error-throwing paths.prefer_setup_teardownfalse positive on independent locals: The rule no longer flags repeated primitive variable declarations (int count = 0,const int iterations = 1000) as duplicated setup code. Simple literal initializations and const declarations are now excluded from the setup signature comparison, so only meaningful setup (object construction, service initialization) triggers the suggestion.require_change_notifier_disposefalse positives: Fixed three categories of false positive: (1) Fields not owned by the class (e.g., controllers received fromAutocompletecallbacks) are no longer flagged — the rule now uses_findOwnedFieldsOfTypeownership detection consistent with sibling disposal rules. (2) Generic container types likeMap<K, ScrollController>no longer match via substring — exact type matching is now used. (3) Extension method disposal wrappers (e.g.,.disposeSafe()) are now recognized as valid disposal patterns by all disposal rules.
Deprecated #
skipTestFilesgetter: Replaced bytestRelevance. The old boolean getter still compiles but is marked@Deprecated. Migration:skipTestFiles => trueis now the default;skipTestFiles => falsebecomestestRelevance => TestRelevance.always.
4.8.4 #
Added #
- Test coverage top offenders report: The publish workflow's test coverage summary now lists the 10 worst categories ranked by untested rule count, color-coded by severity.
- File health top offenders: The "Files needing quick fixes" audit section now lists the top 5 worst files sorted by fix coverage, showing fixes/rules and percentage.
- Doc reference auto-fix in publish pipeline: The
--fix-docsflag and Step 6 analysis now detect and auto-fix unresolvable[reference]patterns in DartDoc comments (OWASP codes, file names, snake_case rule names) alongside the existing angle bracket fixer.
Changed #
- Improved DX messages for 58 high-impact rules: Expanded
problemMessageandcorrectionMessagetext acrossnavigation_rules,notification_rules,package_specific_rules,performance_rules,platform_rules,qr_scanner_rules,resource_management_rules,riverpod_rules,scroll_rules,security_rules, andstate_management_rulesto explain user-facing consequences and provide more specific fix guidance. Each problem message now meets the 180-character minimum with clear issue/consequence/impact structure; each correction message meets the 80-character minimum with actionable fix guidance. - DX audit: relaxed vague language check for low-impact rules: The
_audit_dx.pyscoring module no longer penalises advisory phrasing ("consider", "prefer", etc.) in low-impact rules, since suggestive language is appropriate for rules that are informational by nature.
Fixed #
- 20 unresolved doc reference warnings: Escaped non-symbol references in DartDoc comments (OWASP codes, rule names, Flutter widget names, file names) that
dart doccould not resolve, eliminating all documentation generation warnings. - Publish script commit step: "No changes to commit" message now shows as success (green) instead of warning (yellow), since it is not an error condition.
- Publish script Step 9: Suppressed spurious Windows
nulpath warning in pre-publish validation output. - ROADMAP near-match false positives:
_testvariant rules are now excluded from near-match detection, matching the existing duplicate exclusion logic.
Removed #
doc/flutter_widget_rules_full_table.md: Obsolete split-plan document that was no longer referenced.
4.8.3 #
Added #
avoid_dynamic_code_loading(Essential, ERROR): Detects runtime code loading viaIsolate.spawnUri()and package management commands inProcess.run()/Process.start(). OWASP M2 (Supply Chain Security).avoid_unverified_native_library(Essential, ERROR): DetectsDynamicLibrary.open()with dynamic or absolute paths that bypass package verification. OWASP M2 (Supply Chain Security).avoid_hardcoded_signing_config(Recommended, WARNING): Detects hardcoded keystore paths, signing aliases, and configuration strings extractable from compiled binaries. OWASP M7 (Binary Protections).- OWASP Mobile coverage: 8/10 → 10/10 (100%). Added M2 and M7 coverage; also mapped
avoid_eval_like_patternsto M7.
Fixed #
- 141 orphan rules restored: Rules that were implemented but never registered in
_allRuleFactoriesor assigned to tiers are now fully active. Includes rules for Bloc, GetX, Isar, Dio, Riverpod, image_picker, permissions, notifications, WebView, and more. Critical-impact rules assigned to recommended tier, high to professional, medium/low to comprehensive. - 9 GetX rules unhidden: Removed
hidedirectives inall_rules.dartthat blocked GetX rules from export. - 3 opinionated rules registered:
prefer_early_return,prefer_mutable_collections, andprefer_record_over_equatablemoved from dead code to stylistic tier. format_comment_stylemoved to insanity tier: Previously in professional tier, now correctly placed as documentation pedantry.- Pre-publish audit script bugs fixed:
_code\s*=regex now matches variant field names (_codeField,_codeMethod), eliminating phantom rule false positives. Opinionated prefer_* detection uses class-scoped search instead of backward search, preventing cross-class name resolution errors.
Changed #
- Improved DX message quality for 25 critical/high-impact lint rules: Expanded problem messages to clearly explain the detected issue, its real-world consequence, and the user impact. Expanded correction messages with specific, actionable fix guidance. Affected rules span forms, navigation, memory management, images, Riverpod, GetX, lifecycle, Firebase, JSON, and API categories.
- Critical DX pass rate: 98.3% → 100% (60/60 rules passing)
- High DX pass rate: 34.8% → 42.7% (+23 additional rules passing)
- Audit scripts refactored:
_audit.pysplit into_audit_checks.py(extraction/display) and_audit_dx.py(DX quality analysis) for maintainability.
4.8.2 #
Added #
require_https_only_testrule: New test-file variant ofrequire_https_onlyat INFO severity (Professional tier). The production rule now skips test files, and the test variant covers them independently so teams can disable HTTP URL linting in tests without affecting production enforcement.avoid_hardcoded_config_testrule: New test-file variant ofavoid_hardcoded_configat INFO severity (Professional tier). Hardcoded URLs and keys in test files are typically test fixture data; this rule surfaces them at reduced severity for awareness without blocking.require_deep_link_fallbacktest fixture: Added coverage for lazy-loading getters that use utility class methods (e.g.,_uri ??= UrlUtils.getSecureUri(url)) to prevent false positives.
Changed #
- 98 opinionated rules moved to stylistic tier: Rules that are subjective or conflict with each other are now opt-in only via
--stylisticflag, not enabled by default in any tier. This includes member ordering, argument ordering, naming conventions, and similar stylistic preferences. - 8 stylistic rules renamed for consistency: All stylistic rules now use
prefer_prefix:always_fail_test_case→prefer_fail_test_caseenforce_member_ordering→prefer_member_orderingenforce_arguments_ordering→prefer_arguments_orderingcapitalize_comment_start→prefer_capitalized_comment_startavoid_continue_statement→prefer_no_continue_statementavoid_getter_prefix→prefer_no_getter_prefixavoid_commented_out_code→prefer_no_commented_out_codeavoid_inferrable_type_arguments→prefer_inferred_type_arguments- Old names preserved as
configAliasesfor backwards compatibility.
- Conflicting member-ordering rules moved to stylistic tier:
prefer_static_members_first,prefer_instance_members_first,prefer_public_members_first,prefer_private_members_firstnow require explicit opt-in since they conflict in pairs. - Tier assignment: single source of truth:
tiers.dartis now the sole authority for rule tier assignments. Removed theRuleTier get tiergetter fromSaropaLintRuleand the legacy two-phase fallback inbin/init.dart. The init script now reads tier assignments exclusively fromtiers.dartsets. - Unified CLI entry point: Added
bin/saropa_lints.dartas a dispatcher supportinginit,baseline, andimpact-reportsubcommands. - Progress tracking improvements:
ProgressTrackernow derives project root from the first analyzed file path instead of using.(which fails in plugin mode). Shows enabled rule count instead of misleading file percentage.
Fixed #
avoid_context_after_await_in_staticfalse positives in try-catch: The rule now recurses into try, catch, and finally blocks instead of skipping the entireTryStatement. Mounted guards and ternary patterns inside try-catch are correctly recognized.avoid_context_across_asyncfalse positives in try-catch: Same try-catch recursion fix applied to the non-static context rule.avoid_storing_contextfalse positives on function type fields: Fields declaring callback signatures (e.g.,void Function(BuildContext)) no longer trigger the rule. Only actualBuildContextstorage is flagged.avoid_expanded_outside_flexfalse positives: Three scenarios fixed:ExpandedinsideList.generate()or.map()callbacks within helper methods is now trusted.- Expression-body helper methods (e.g.,
List<Widget> _items() => [Expanded(...)]) are now trusted. - Top-level
FunctionDeclarationboundaries are now recognized.
avoid_builder_index_out_of_boundsfalse positives withitemCount: Lists whose bounds are guaranteed byitemCount: list.lengthare no longer flagged.avoid_long_running_isolatesfalse positive onIsolate.run:Isolate.run()is now correctly classified as short-lived (likecompute()), not as a persistent isolate likeIsolate.spawn(). Context window expanded from 200 to 500 chars. Added fire-and-forget and never-block awareness keywords.require_immutable_bloc_statefalse positives on non-BLoC classes: Skip indirect Flutter State subclasses (PopupMenuItemState,FormFieldState,AnimatedWidgetBaseState,ScrollableState,RefreshIndicatorState) andStatefulWidget/StatelessWidgetsubclasses using "State" as a domain term.require_cache_key_determinismfalse positive on metadata parameters: Common metadata parameter names (createdAt,updatedAt,timestamp,expiresAt,ttl, etc.) are now excluded from determinism checks. Diagnostics now report at the specific offending argument instead of the entire variable declaration. Extracted shared_checkArgumentListhelper to reduce duplication.
4.8.1 #
Added #
avoid_uncaught_future_errorsquick fix: New "Add // ignore: comment" quick fix for cases where the called method handles errors internally but is defined in a different file. The rule cannot detect cross-file try-catch, so this fix provides a convenient workaround.
Changed #
avoid_builder_index_out_of_boundsdocumentation: Added note clarifying the rule's limitation with synchronized lists. The rule cannot detect cross-method relationships likeList.generate(otherList.length, ...), so explicit bounds checks or ignore comments are recommended for multiple lists of the same length.avoid_uncaught_future_errorsdocumentation: Added "Limitation: Cross-file analysis" section explaining that the rule can only detect try-catch in functions defined in the same file.
Fixed #
require_deep_link_fallbackfalse positives: Fixed three false positive scenarios:return null;statements now recognized as valid fallback patterns- Ternary expressions with null fallback (e.g.,
condition ? value : null) now recognized - Methods returning
String?orUri?are now skipped as they are URL parsers/converters, not deep link handlers
4.8.0 #
Changed #
- Lazy rule loading for reduced memory usage: Rules are now instantiated on-demand instead of at compile time. Previously, all 1500+ rules were created as a
const List<LintRule>, consuming ~4GB of memory regardless of which tier was selected. Now only rules needed for the selected tier are created. For essential tier (~250 rules), this reduces memory usage significantly and eliminates OOM crashes on resource-constrained systems.- Rule list changed from
const List<LintRule>tofinal List<LintRule Function()>factories - Factory map built lazily on first access
- No generated files required - stays in sync automatically when rules are added/removed
- Rule list changed from
4.7.6 #
Fixed #
require_ios_permission_descriptionfalse positive on ImagePicker constructor: Fixed false positives where the rule required bothNSPhotoLibraryUsageDescriptionANDNSCameraUsageDescriptionwhen onlyImagePicker()was instantiated, before any method was called. The rule now uses smart method-level detection:ImagePicker()constructor alone → no warningpicker.pickImage(source: ImageSource.gallery)→ requires onlyNSPhotoLibraryUsageDescriptionpicker.pickImage(source: ImageSource.camera)→ requires onlyNSCameraUsageDescriptionpicker.pickImage(source: variable)→ requires both (can't determine statically)
4.7.5 #
Added #
- Mid-chain ignore comments:
// ignore:comments now work when placed before the method or property name in chained calls. Previously, ignore comments had to be placed before the entire statement. Now both formats work:// Before (still works): // ignore: rule_name object.method(); // Now also works: object // ignore: rule_name .method();
4.7.4 #
Added #
- Quick fix for
avoid_unbounded_cache_growth: New quick fix adds astatic const int maxSize = 100;field to cache classes. Developers need to manually add eviction logic in mutation methods.
Fixed #
avoid_unbounded_cache_growthstatic regex patterns: Improved performance by making regex patterns (_limitPattern,_mutationMethodPattern,_mapKeyPattern) static class fields instead of creating them on each method call.avoid_overlapping_animationsfalse positives on different-axis SizeTransitions: Fixed false positives when nestingSizeTransitionwidgets with different axes.SizeTransition(axis: Axis.vertical)animates height whileSizeTransition(axis: Axis.horizontal)animates width - these are independent properties and should not conflict. The rule now distinguishessize_verticalfromsize_horizontalbased on theaxisparameter.avoid_unbounded_cache_growthfalse positives on enum-keyed maps: Fixed false positives on caches that use enum keys (e.g.,Map<PanelEnum, Widget>). Enum-keyed maps are inherently bounded by the number of enum values and cannot grow indefinitely. Also added detection for immutable caches (read-only maps with no mutation methods likeadd,set, or index assignment).require_stream_controller_closefalse positives on helper classes: Fixed false positives on helper/wrapper classes that have aclose()method instead ofdispose(). Classes likeCustomStreamControllerwith aclose()method that properly closes the internal StreamController are no longer flagged. The rule now checks bothdispose()andclose()methods for cleanup calls.avoid_unbounded_cache_growthfalse positives on database models: Fixed false positives on Isar (@collection), Hive (@HiveType), and Floor (@Entity) database models that have "cache" in the class name. These ORM models use disk-based storage with external cleanup, not in-memory Map caching. Also improved detection to only flag actual Map field declarations, nottoMap()serialization method return types.avoid_path_traversalfalse positives on trusted system paths: Fixed false positives when file paths are constructed using trusted system APIs (e.g.,path_provider, platform MethodChannels) combined with hardcoded constants. The rule now only flags paths containing function parameters (actual user input), not paths using private fields, constants, or system API returns likegetApplicationDocumentsDirectory().require_deep_link_fallbackfalse positives on URI getters: Fixed false positives on simple URI getter and converter methods that are not deep link handlers. Now skips: lazy-loading patterns (_uri ??= parseUri(url)), method invocations on fields (url.toUri()), and null-aware property access (url?.uri).require_stream_controller_disposefalse positive with typed StreamControllers: Fixed false positives whenStreamController<T>has a concrete type parameter (e.g.,StreamController<String>,StreamController<(double, double)>). The type classification logic incorrectly treated these as wrapper types. Also fixed wrapper types to accept both.close()and.dispose()methods.avoid_expanded_outside_flexfalse positive in helper methods: Fixed false positives whenExpanded/Flexibleis created inside helper methods that returnList<Widget>, or inside collection builders likeList.generate()and.map(). These patterns are now trusted since the widgets typically end up inside Flex parents at runtime.avoid_flashing_contentfalse positives on non-repeating animations: Fixed false positives where the rule flagged allAnimationControllerinstances withduration < 333ms, regardless of whether the animation actually repeats. Per WCAG 2.3.1, a "flash" requires alternating between states, so only.repeat()cascades now trigger the rule. Single-direction animations (.forward(),.reverse(),.animateTo()) are no longer flagged.
Added #
- Quick fix for
avoid_flashing_content: New quick fix increases animation duration to 333ms (minimum WCAG 2.3.1 compliant threshold). - Quick fix for
require_stream_controller_close: New quick fix addscontroller.close()call to the dispose/close method.
4.7.3 #
Added #
- Interactive analysis prompt: After init completes, prompts user "🔍 Run analysis now? [y/N]" to optionally run
dart run custom_lintimmediately.
Fixed #
-
Progress tracking: misleading percentages: Fixed bug where progress would show constantly recalibrating percentages (e.g., "130/149 (87%)") when file discovery failed. Now shows simple file count ("X files") when accurate totals aren't available.
-
Progress tracking: rule name prefix spam: Fixed progress output being prefixed with rule name (e.g.,
[require_ios_privacy_manifest]) for every update. Progress now uses stderr to avoid custom_lint's automatic rule tagging.
4.7.2 #
Added #
-
Custom overrides file: New
analysis_options_custom.yamlfile for rule customizations that survive--reset. Place rule overrides in this file to always apply them regardless of tier or reset. -
Timestamped backups: Backup files now include datetime stamp (
yyyymmdd_hhmmss_filename.bak) for history tracking. -
Enhanced debugging: Added version number, file paths, and file size to init script output.
-
Detailed log files: Init script now writes detailed logs to
reports/yyyymmdd_hhmmss_saropa_lints_init.logfor history and debugging.
Fixed #
- Init script: false "user customizations" count: Fixed bug where switching tiers would incorrectly count all tier-changed rules as "user customizations". Now only rules explicitly in the USER CUSTOMIZATIONS section are preserved. Added warning when >50 customizations detected (suggests using
--resetto fix corrupted config).
4.7.0 #
Added #
-
Single source of truth for rule tiers: Rules now declare their tier directly in the rule class via
RuleTier get tiergetter. The init script reads tier from rule classes with fallback to legacytiers.dartfor backwards compatibility. -
Enhanced init script output:
- Cross-platform ANSI color support (Windows Terminal, ConEmu, macOS, Linux)
- Rules organized by tier with visual tier headers
- Problem message comments next to every rule in YAML
- Stylistic rules in dedicated section
- Summary with counts by tier and severity
- Massive ASCII art section headers for easy navigation
-
Progress tracking with ETA:
- File discovery at startup for accurate progress percentage
- Rolling average for stable ETA calculation
- Slow file detection (files taking >5 seconds)
- Violation count tracking
- Recalibration when more files found than expected
Fixed #
prefer_small_length_filesandprefer_small_length_test_filestier misassignment: Fixed bug where these insanity-tier rules were incorrectly enabled in comprehensive tier. Rules now correctly havetier => RuleTier.insanityoverride.
4.6.2 and Earlier #
For details on the initial release and versions 0.1.0 through 4.6.2, please refer to CHANGELOG_ARCHIVE.md.