saropa_lints 6.0.7
saropa_lints: ^6.0.7 copied to clipboard
1889 custom lint rules with 292 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.15.1.
** See the current published changelog: saropa_lints/changelog
6.0.7 #
Fixed #
- Unimplemented rule references: Removed four rules from the plugin registry, tiers, analysis_options_template, and roadmap fixture that were never implemented:
avoid_equals_and_hash_code_on_mutable_classes,avoid_implementing_value_types,avoid_null_checks_in_equality_operators,avoid_redundant_argument_values. They remain documented in CHANGELOG 6.0.6 Added; can be re-added when implemented. - require_yield_after_db_write: Suppress when write is last statement, when next statement is
return, when insidecompute()/Isolate.run(), and when file is in test directory. RecognizeFuture.microtask,Future.delayed(Duration.zero), andSchedulerBinding.instance.endOfFrameas valid yields. Replace fragiletoString()check in SchedulerBinding detection with AST-based identifier check. - verify_documented_parameters_exist: Whitelist built-in types and literals (
[String],[int],[null], etc.) to avoid false positives on valid doc references. - Style: Satisfy
curly_braces_in_flow_control_structuresin rule implementation files (api_network, control_flow, lifecycle, naming_style, notification, sqflite, performance, web, security, structure, ui_ux, widget_patterns). Single-statementifbodies are now wrapped in blocks; no behavior change.
Changed #
- Firebase reauth rule: Reauth is now compared by source offset (earliest reauth in method) so order is correct regardless of visit order.
- Firebase token rule: Stored detection now includes VariableDeclaration initializer (e.g.
final t = await user.getIdToken()). - Performance: Firebase Auth rules set requiredPatterns for earlier file skip when content does not match.
- no_empty_block: Confirmed existing implementation in
unnecessary_code_rules.dart; roadmap task archived.
Added #
-
5 new Essential-tier lint rules from roadmap detail requirements:
require_exhaustive_sealed_switch— switch on sealed types must use explicit cases; avoid default/wildcard (same logic as avoid_wildcard_cases_with_sealed_classes, Essential-tier name).require_error_handling_graceful— flag raw exception (e.toString(), e.message, $e) shown in Text/SnackBar/AlertDialog inside catch blocks; recommend friendly messages.require_firebase_reauthentication— sensitive Firebase Auth ops (delete, updateEmail, updatePassword) must be preceded by reauthenticateWithCredential/reauthenticateWithProvider in the same method (firebase_auth only).require_firebase_token_refresh— getIdToken() result stored (variable/prefs) without idTokenChanges listener or forceRefresh (firebase_auth only).require_text_scale_factor_awareness— Container/SizedBox with literal height containing Text may overflow at large text scale; recommend flexible layout (widget files only).
-
9 new lint rules from roadmap detail requirements (banned_usage, prefer_csrf_protection, prefer_no_commented_code alias, prefer_semver_version, prefer_sqflite_encryption, require_conflict_resolution_strategy, require_connectivity_timeout, require_init_state_idempotent, require_input_validation):
banned_usage(Professional, WARNING) — Configurable ban list for identifiers (e.g.print). No-op without config inanalysis_options_custom.yaml. Whole-word match; optionalallowedFilesper entry.prefer_csrf_protection(Professional, WARNING) — State-changing HTTP with Cookie header must include CSRF token or Bearer auth. Web/WebView projects only. OWASP M3/A07.prefer_no_commented_code— Alias for existingprefer_no_commented_out_code(stylistic).prefer_semver_version(Essential, WARNING) — pubspec.yamlversionmust be major.minor.patch (e.g. 1.0.0, 2.3.1+4). Reports when invalid.prefer_sqflite_encryption(Professional, WARNING) — Sensitive DB paths (user/auth/health/payment etc.) with sqflite should use sqflite_sqlcipher. OWASP M9.require_conflict_resolution_strategy(Professional, WARNING) — Sync/upload/push methods that overwrite data should compare timestamps or show conflict UI.require_connectivity_timeout(Essential, WARNING) — HTTP/client/dio requests must have a timeout (e.g..timeout(Duration(seconds: 30))).require_init_state_idempotent(Essential, WARNING) — addListener/addObserver in initState must have matching removeListener/removeObserver in dispose (Flutter widget files).require_input_validation(Essential, WARNING) — Raw controller.textin post/put/patch body without trim/validate/isEmpty. OWASP M1/M4.
-
12 new lint rules from roadmap detail requirements:
avoid_unnecessary_containers(Recommended, INFO) — Container with only child (and optionally key); remove and use child directly (widget files only).prefer_adjacent_strings(Recommended, INFO) — use adjacent string literals instead of+for literal concatenation.prefer_adjective_bool_getters(Professional, INFO) — bool getters should use predicate names (is/has/can) not verb names (validate/load).prefer_asserts_in_initializer_lists(Professional, INFO) — move leading assert() from constructor body to initializer list.prefer_const_constructors_in_immutables(Professional, INFO) — @immutable or StatelessWidget/StatefulWidget subclasses with only final fields should have a const constructor.prefer_const_declarations(Recommended, INFO) — final variables with constant initializers could be const (locals, static, top-level).prefer_const_literals_to_create_immutables(Recommended, INFO) — non-const collection literals passed to immutable widget constructors (widget files only).prefer_constructors_first(Professional, INFO) — constructors should appear before methods in a class.prefer_extension_methods(Professional, INFO) — top-level functions that could be extension methods on first parameter type.prefer_extension_over_utility_class(Professional, INFO) — class with only static methods sharing first param type could be an extension.prefer_extension_type_for_wrapper(Professional, INFO) — single-field wrapper class could be an extension type (Dart 3.3+).prefer_final_fields(Professional, INFO) — fields never reassigned (except via setter) could be final.
-
16 new lint rules from roadmap detail requirements:
prefer_final_locals(Recommended, INFO) — local variables never reassigned should be final.prefer_getters_before_setters(Professional, INFO) — setter should appear after its getter.prefer_if_elements_to_conditional_expressions(Recommended, INFO) — use if element instead of ternary with null in collections.prefer_inlined_adds(Recommended, INFO) — prefer inline list/set literal over empty then add/addAll.prefer_interpolation_to_compose(Recommended, INFO) — prefer string interpolation over + with literals.prefer_lowercase_constants(Recommended, INFO) — const/static final should use lowerCamelCase.prefer_mixin_over_abstract(Professional, INFO) — abstract class with no abstract members and no generative constructor → mixin.prefer_named_bool_params(Professional, INFO) — prefer named bool parameters in small functions.prefer_noun_class_names(Professional, INFO) — concrete classes should use noun/agent names, not gerund/-able.prefer_null_aware_method_calls(Recommended, INFO) — use ?. instead of if (x != null) { x.foo(); }.prefer_raw_strings(Professional, INFO) — use raw string when only escaped backslashes (e.g. regex).prefer_record_over_tuple_class(Professional, INFO) — simple data class with only final fields → record.prefer_sealed_classes(Professional, INFO) — abstract class with 2+ concrete subclasses in same file → sealed.prefer_sealed_for_state(Professional, INFO) — state/event/result abstract with local subclasses → sealed.prefer_static_before_instance(Professional, INFO) — static members before instance in same category.prefer_verb_method_names(Professional, INFO) — methods should use verb names, not noun-only.
-
3 new lint rules from roadmap detail requirements (late/pagination/SSL pinning):
require_late_access_check(Professional, WARNING) — late non-final field set in a method other than constructor/initState and read in another method without initialization check; risk of LateInitializationError.require_pagination_for_large_lists(Essential, WARNING) — ListView.builder/GridView.builder with itemCount from bulk-style list (e.g. allProducts.length) without pagination; OOM and jank risk. Suppressed when project uses infinite_scroll_pagination.require_ssl_pinning_sensitive(Professional, WARNING) — HTTP POST/PUT/PATCH to sensitive paths (/auth, /login, /token) without certificate pinning; OWASP M5, M3. Suppressed when project uses http_certificate_pinning or ssl_pinning_plugin, and for localhost.
-
10 new lint rules from roadmap detail requirements:
avoid_deprecated_usage(Recommended, WARNING) — use of deprecated APIs from other packages; same-package and generated files ignored.handle_throwing_invocations(Professional, INFO) — invocations that can throw (e.g. @Throws, readAsStringSync, jsonDecode) not in try/catch.prefer_form_bloc_for_complex(Professional, INFO) — Form with >5 fields suggests form state management (FormBloc, reactive_forms, etc.).prefer_local_notification_for_immediate(Recommended, INFO) — FCM for server-triggered messages; use flutter_local_notifications for app-generated.prefer_master_detail_for_large(Professional, INFO) — list navigation without MediaQuery/LayoutBuilder; suggest master-detail on tablets.prefer_batch_requests(Professional, INFO) — await in for-loop with fetch-like method names; suggest batch endpoints.prefer_binary_format(Comprehensive, INFO) — jsonDecode in hot path (timer/stream); suggest protobuf/MessagePack or compute().prefer_pool_pattern(Comprehensive, INFO) — non-const allocation in hot path (timer/animation); suggest object pool.require_compression(Comprehensive, INFO) — HTTP get/post/put/delete without Accept-Encoding; suggest gzip.require_expando_cleanup(Comprehensive, INFO) — Expando with entries added but no cleanup (expando[key] = null).
6.0.6 #
Added #
- 15 new lint rules from roadmap detail requirements:
avoid_bool_in_widget_constructors(Professional, INFO) — widget constructors with named bool params; prefer enum or decompositionavoid_classes_with_only_static_members(Recommended, INFO) — prefer top-level functions/constantsavoid_double_and_int_checks(Professional, INFO) — flagis int && is double(always false) andis int || is double(useis num)avoid_equals_and_hash_code_on_mutable_classes(Professional, INFO) — custom ==/hashCode with mutable fields breaks Set/Mapavoid_escaping_inner_quotes(Stylistic, INFO) — switch quote delimiter to avoid escaped inner quotesavoid_field_initializers_in_const_classes(Professional, INFO) — move field initializers to const constructor initializer listavoid_function_literals_in_foreach_calls(Stylistic, INFO) — prefer for-in over .forEach with a literalavoid_implementing_value_types(Professional, INFO) — implements type with custom ==/hashCode without overriding themavoid_js_rounded_ints(Comprehensive, INFO) — integer literals exceeding JS safe integer range (2^53)avoid_null_checks_in_equality_operators(Professional, INFO) — redundant other == null when is! type test presentavoid_positional_boolean_parameters(Professional, INFO) — use named parameters for boolsavoid_private_typedef_functions(Comprehensive, INFO) — private typedef for function type; prefer inline typeavoid_redundant_argument_values(Recommended, INFO) — named argument equals parameter defaultavoid_setters_without_getters(Professional, INFO) — setter with no matching getteravoid_single_cascade_in_expression_statements(Stylistic, INFO) — single cascade as statement; use direct call
Changed #
- Init wizard (stylistic walkthrough): Boolean naming rules are in their own category so "[a] enable all in category" applies to all four; progress shows global position (e.g. 51/143) on resume instead of resetting to 1/N; GOOD/BAD labels are bold and colored; the four boolean rules now have distinct good/bad examples (fields, params, locals, and all booleans) so the wizard differentiates them clearly.
Fixed #
require_minimum_contrast— ignore comments were not honored; rule now respects// ignore: require_minimum_contrastand// ignore_for_file: require_minimum_contrast(and hyphenated forms) viaIgnoreUtils
6.0.5 #
Fixed #
avoid_path_traversal— false positive when trusted platform path (e.g.,getApplicationDocumentsDirectory) is passed to a private helper method; now traces trust through call sites of private methodsrequire_file_path_sanitization— same false positive asavoid_path_traversal; shared inter-procedural platform path trust check
6.0.4 #
Fixed #
avoid_dynamic_sql— false positive on SQLite PRAGMA statements which do not support parameter binding; now exempts PRAGMA syntax. Also improved SQL keyword matching to use word boundaries (prevents false positives from identifiers likeselection,updateTime)avoid_ref_read_inside_build— false positive onref.read()inside callbacks (onPressed, onSubmit, etc.) defined inline inbuild(); now stops traversal at closure boundariesavoid_ref_in_build_body— same false positive as above; now shares the corrected visitor withavoid_ref_read_inside_buildavoid_ref_watch_outside_build— false positive onref.watch()inside Riverpod provider bodies (Provider,StreamProvider,FutureProvider, etc.); now recognizes provider callbacks as reactive contexts alongsidebuild()avoid_path_traversal— false positive when file path parameter originates from platform path APIs (getApplicationDocumentsDirectory,getTemporaryDirectory, etc.); now recognizes these as trusted sourcesrequire_file_path_sanitization— same false positive asavoid_path_traversal; now recognizes platform path APIs as trustedavoid_unsafe_collection_methods— false positive on.first/.lastwhen guarded by early-return (if (list.isEmpty) return;) or when the collection is a callback parameter guaranteed non-empty (e.g.,SegmentedButton.onSelectionChanged)avoid_unsafe_reduce— false positive onreduce()guarded byif (list.length < N) return;orif (list.isEmpty) return;; now detects early-return and if/ternary guardsrequire_app_startup_error_handling— false positive on apps without a crash reporting dependency; now only fires when a monitoring package (e.g.,firebase_crashlytics,sentry_flutter) is detected in pubspec.yamlrequire_search_debounce— false positive when Timer-based debounce is defined as a class field rather than inline in the callback; now checks enclosing class for Timer/Debouncer field declarationsrequire_minimum_contrast— false positive when text color is light but background is set via a variable that can't be resolved statically; now recognizes containers with unresolvable background colors as intentionally set
6.0.3 #
Fixed #
avoid_drift_close_streams_in_tests— rule never fired becausetestRelevancewas not overridden; the framework skipped test files before the rule could run. Now correctly set toTestRelevance.testOnlyavoid_drift_update_without_where— removed unreachable dead code branch
6.0.2 #
Changed #
- Widened
analysis_server_pluginandanalyzer_plugindependency constraints from pinned to^range to reduce version conflicts for consumers
Fixed #
- CI publish workflow: dry run step failed on exit code 65 (warnings) due to
set -ekilling the shell before the exit code could be evaluated; warnings are now reported via GitHub Actions annotations without blocking the publish
6.0.1 #
Added #
- 10 additional Drift lint rules covering common gotchas, Value semantics, migration safety, and Isar-to-Drift migration patterns (total: 31 Drift rules)
avoid_drift_value_null_vs_absent(Recommended, WARNING) — detectsValue(null)instead ofValue.absent()require_drift_equals_value(Recommended, WARNING) — detects.equals()with enum/converter columns instead of.equalsValue()require_drift_read_table_or_null(Recommended, WARNING) — detectsreadTable()with leftOuterJoin instead ofreadTableOrNull()require_drift_create_all_in_oncreate(Recommended, WARNING) — detectsonCreatecallback missingcreateAll()avoid_drift_validate_schema_production(Professional, WARNING) — detectsvalidateDatabaseSchema()without debug guardavoid_drift_replace_without_all_columns(Professional, INFO) — detects.replace()on update builder instead of.write()avoid_drift_missing_updates_param(Professional, INFO) — detectscustomUpdate/customInsertwithoutupdatesparameteravoid_isar_import_with_drift(Recommended, WARNING) — detects files importing both Isar and Drift packagesprefer_drift_foreign_key_declaration(Professional, INFO) — detectsId-suffixed columns withoutreferences()require_drift_onupgrade_handler(Recommended, WARNING) — detects schemaVersion > 1 withoutonUpgradehandler
Fixed #
avoid_drift_missing_updates_param— missing drift import check caused false positives on non-driftcustomUpdate()callsprefer_drift_foreign_key_declaration— false positives on non-FK column names (androidId,deviceId,sessionId, etc.)require_drift_equals_value— false positives on non-enum uppercase types (DateTime,Duration,BigInt, etc.)require_drift_onupgrade_handler— reduced performance cost by checking individual members instead of full classtoSource()
6.0.0 #
Breaking #
- Upgraded
analyzerfrom ^8.0.0 to ^9.0.0 - Pinned
analysis_server_pluginto 0.3.4 (only version targeting analyzer v9) - Pinned
analyzer_pluginto 0.13.11 (only version targeting analyzer v9) - Requires Dart SDK >=3.10.0
Added #
- 21 new Drift (SQLite) database lint rules covering data safety, resource management, SQL injection prevention, migration correctness, performance, and web platform safety
avoid_drift_enum_index_reorder(Essential, ERROR)require_drift_database_close(Recommended, WARNING)avoid_drift_update_without_where(Recommended, WARNING)require_await_in_drift_transaction(Recommended, WARNING)require_drift_foreign_key_pragma(Recommended, WARNING)avoid_drift_raw_sql_interpolation(Recommended, ERROR)prefer_drift_batch_operations(Recommended, WARNING)require_drift_stream_cancel(Recommended, WARNING)avoid_drift_database_on_main_isolate(Professional, INFO)avoid_drift_log_statements_production(Professional, WARNING)avoid_drift_get_single_without_unique(Professional, INFO)prefer_drift_use_columns_false(Professional, INFO)avoid_drift_lazy_database(Professional, INFO)prefer_drift_isolate_sharing(Professional, INFO)avoid_drift_query_in_migration(Comprehensive, WARNING)require_drift_schema_version_bump(Comprehensive, INFO)avoid_drift_foreign_key_in_migration(Comprehensive, INFO)require_drift_reads_from(Comprehensive, INFO)avoid_drift_unsafe_web_storage(Comprehensive, INFO)avoid_drift_close_streams_in_tests(Comprehensive, INFO)avoid_drift_nullable_converter_mismatch(Comprehensive, INFO)
- Drift added as supported package in package filtering system
Changed #
- Migrated all
NamedType.name2references toNamedType.name(analyzer v9 API) - Migrated
VariableDeclaration.declaredElementtodeclaredFragment.element(analyzer v9 API) - Removed deprecated
errorCodeparameter fromSaropaDiagnosticReporter.atOffset()
5.0.3 #
Added #
avoid_string_env_parsing: warns whenfromEnvironment()is called withoutdefaultValue(Recommended)avoid_connectivity_equals_internet: warns whenConnectivityResultis used as a proxy for internet access (Essential)avoid_platform_specific_imports: warns whendart:iois imported in shared/web-capable code (Recommended)avoid_shared_prefs_sync_race: warns when SharedPreferences writes are not awaited in async code (Recommended)avoid_multiple_animation_controllers: warns when a State class has 3+ AnimationController fields (Professional)avoid_form_validation_on_change: warns whenvalidate()is called insideonChanged(Professional)avoid_stack_trace_in_production: warns when stack traces are exposed to users (OWASP M10) (Recommended)avoid_expensive_did_change_dependencies: warns when expensive operations run indidChangeDependencies()(Professional)avoid_permission_request_loop: warns whenPermission.request()is called inside a loop (Professional)avoid_entitlement_without_server: warns when IAP purchases are verified client-side only (OWASP M1/M4) (Professional)avoid_webview_cors_issues: warns whenallowUniversalAccessFromFileURLsorallowFileAccessFromFileURLsis set totrue(OWASP M8/A05) (Professional)
Fixed #
prefer_trailing_comma_always: suppress false positive when the last argument is a callback/closure whose body spans multiple linesinitwalkthrough: skipped stylistic rules now marked as reviewed so they are not re-prompted on subsequent runs// ignore:directives now work correctly on declarations with doc comments; previously, diagnostics reported onMethodDeclaration/FunctionDeclaration/ClassDeclarationnodes started at the doc comment offset, making// ignore:before the signature invisible to the analysis server (47 rules affected)avoid_long_parameter_list: diagnostic now highlights the parameter list instead of the entire declaration
Changed #
- Added
exampleBad/exampleGoodto 26 conflicting stylistic rules for clearer wizard descriptions
Build Process #
initwalkthrough: show GOOD example before BAD for clearer readabilityinitwalkthrough: restructured 77-rule "Opinionated prefer_* rules" bucket into 27 conflicting pick-one categories (e.g.,prefer_await_over_thenvsprefer_then_over_await) plus 32 non-conflicting opinionated rulesinitwalkthrough: skip prompt now says "keeps current" to clarify the rule won't be re-asked
5.0.2 #
Fixed #
prefer_list_first: suppress false positives when the same collection is accessed with sibling indices (list[0]alongsidelist[1]), on assignment targets (list[0] = value), on String subscripts (string[0]), and on Map access (map[0])prefer_list_last: same false positive suppression — assignment targets, String/Map types, and sibling index accessesprefer_catch_over_on: reversed rule logic — now only flagson Object catchandon dynamic catch(redundant, equivalent to barecatch), no longer flags specificonclauses likeon FormatException catchwhich are intentional type filtering. Added quick fix to remove the redundanton Objectclause.avoid_dynamic_type: exemptdynamicin type arguments (List<dynamic>,Map<dynamic, dynamic>), closure/lambda parameters, and for-in loop variables — eliminates false positives in JSON utility codeavoid_ignoring_return_values: addupdate,putIfAbsent,updateAll,addEntriesto safe-to-ignore list — these Map mutation methods are called for their side effect, not their return valueavoid_medium_length_files(and all 8 file length rules): count only code lines, excluding comments and blank lines — well-documented files are no longer penalised for thorough dartdocavoid_long_functions: count only code lines in function bodies, excluding comments and blank lines — well-documented functions are no longer penalised for thorough comments (v5)prefer_no_commented_out_code: tighten keyword and type-name patterns, add prose guard with strong-code-indicator bypass — fixes false positives on section headers (// Iterable extensions), inline prose (// this is non-null), and comments containing type names in natural language
Removed #
avoid_ignore_trailing_commentrule,MoveTrailingCommentFixquick fix, andtrailingCommentOnIgnoreregex — the native Dart analyzer handles ignore directive trailing comments correctly, making this rule produce false positives- Trailing-comment fixer functions from
bin/init.dart(_fixTrailingIgnoreComments,_splitIgnoreParts, etc.) — existed only to support the removed rule scripts/run_custom_lint_all.py— obsolete v4 scriptexample/custom_lint.yaml— v4 configuration artifact
Changed #
- All CLI tools (
bin/baseline.dart,bin/impact_report.dart) now usedart analyzeinstead ofdart run custom_lint - YAML config examples updated from v4
custom_lint:format to v5 nativeplugins: saropa_lints:format across lib/, docs, and scripts - Tier parser in
_analyze_pubspec.pyupdated to read v5plugins.saropa_lints.diagnosticsstructure - Removed
_offer_custom_lint()from publish script (no longer applicable) - VSCode extension updated to run
dart analyzeinstead ofdart run custom_lint
5.0.1 #
Added #
- Quick fixes for 5 blank-line formatting rules:
prefer_blank_line_before_case,prefer_blank_line_before_constructor,prefer_blank_line_before_method,prefer_blank_line_after_declarations,prefer_blank_lines_between_members - Test fixtures for 4 auto_route rules (
avoid_auto_route_context_navigation,avoid_auto_route_keep_history_misuse,require_auto_route_guard_resume,require_auto_route_full_hierarchy) - Test fixtures for
avoid_behavior_subject_last_value(rxdart) - Test fixtures for 3 migration rules (
avoid_asset_manifest_json,prefer_dropdown_initial_value,prefer_on_pop_with_result) - Unit test files for auto_route and rxdart rule categories
- Implemented
prefer_mock_verifyandrequire_mock_http_clientfixture examples (replaced stubs) - Uncommented
prefer_semantics_containerandavoid_redundant_semanticsfixture code (addedcontainerparameter to Semantics mock)
Fixed #
- Publish report: test coverage "Overall" percentage now caps per-category fixture counts at rule counts, preventing excess fixtures from masking gaps
prefer_static_class: no longer fires onabstract final classdeclarations (regression from beta.15 fix)avoid_hardcoded_locale: skip locale-pattern strings inside collection literals (Set, List, Map lookup data)avoid_datetime_comparison_without_precision: skip comparisons against compile-time constants (e.g., epoch sentinel checks)avoid_unsafe_collection_methods: strengthen guard detection with source-text fallback for length/isNotEmpty checksavoid_medium_length_files: exempt files containing onlyabstract finalutility namespace classesprefer_single_declaration_per_file: exempt files where all classes areabstract finalstatic-only namespacesprefer_no_continue_statement: exempt early-skip guard pattern (if (cond) { continue; }at top of loop body)
Changed #
avoid_high_cyclomatic_complexity: raise threshold from 10 to 15 to align with industry standards (SonarQube, ESLint)
5.0.0-beta.15 #
Added #
avoid_cached_image_web: warns when CachedNetworkImage is used inside akIsWebbranch where it provides no caching benefit (Recommended tier)avoid_clip_during_animation: warns when Clip widgets are nested inside animated widgets, causing expensive per-frame rasterization (Professional tier)avoid_auto_route_context_navigation: warns when string-basedcontext.push/context.gois used in auto_route projects instead of typed routes (Professional tier)avoid_auto_route_keep_history_misuse: warns whenreplaceAll/popUntilRootis used outside authentication flows, destroying navigation history (Professional tier)avoid_accessing_other_classes_private_members: warns when code accesses another class's private members through same-file library privacy (Professional tier)avoid_closure_capture_leaks: warns whensetStateis called inside Timer/Future.delayed callbacks without amountedcheck (Professional tier, quick fix)avoid_behavior_subject_last_value: warns when.valueis accessed on a BehaviorSubject inside anisClosedtrue-branch (Professional tier)avoid_cache_stampede: warns when async methods use a Map cache without in-flight request deduplication (Professional tier)avoid_deep_nesting: warns when code blocks are nested more than 5 levels deep (Professional tier)avoid_high_cyclomatic_complexity: warns when functions exceed cyclomatic complexity of 10 (Professional tier)avoid_void_async: warns when async functions returnvoidinstead ofFuture<void>(Recommended tier)avoid_redundant_await: warns whenawaitis used on a non-Future expression (Recommended tier)avoid_unused_constructor_parameters: warns when constructor parameters are not stored or used (Recommended tier)avoid_returning_null_for_void: warns whenreturn nullis used in void functions (Recommended tier)avoid_returning_null_for_future: warns whennullis returned from non-async Future functions (Recommended tier)avoid_shadowing_type_parameters: warns when method type parameters shadow class type parameters (Recommended tier)avoid_redundant_null_check: warns when non-nullable values are compared to null (Recommended tier)avoid_collection_mutating_methods: warns when collections are mutated in-place inside setState (Professional tier)avoid_equatable_nested_equality: warns when mutable collections are included in Equatable props (Professional tier)avoid_getx_rx_nested_obs: warns when GetX Rx observables are nested (Professional tier)avoid_freezed_any_map_issue: warns when @freezed class with fromJson lacks @JsonSerializable(anyMap: true) (Professional tier)avoid_hive_datetime_local: warns when DateTime is stored in Hive without UTC conversion (Professional tier)avoid_hive_type_modification: warns when @HiveField indices are duplicated (Professional tier)avoid_hive_large_single_entry: warns when large objects are stored as single Hive entries (Professional tier)require_auto_route_guard_resume: warns when AutoRouteGuard may not call resolver.next() on all paths (Essential tier)require_auto_route_full_hierarchy: warns when push() is used instead of navigate() in auto_route (Essential tier)avoid_firebase_user_data_in_auth: warns when too many custom claims are accessed from Firebase auth tokens (Professional tier)require_firebase_app_check_production: warns when Firebase is initialized without App Check (Professional tier)
Fixed #
avoid_god_class: false positive on static-constant namespace classes —static constandstatic finalfields are now excluded from the field count since they represent compile-time constants, not instance stateprefer_static_class: conflicting diagnostic withprefer_abstract_final_static_classon classes with private constructors —prefer_static_classnow defers toprefer_abstract_final_static_classwhen a private constructor is presentavoid_similar_names: false positive on single-character variable pairs (y,m,d,h,s) — edit distance is always 1 for any two single-char names, which is not meaningful; confusable-char detection (1/l, 0/O) still catches genuinely dangerous casesavoid_unused_assignment: false positive on definite assignment via if/else branches — assignments in mutually exclusive branches of the same if/else are now recognized as alternatives, not sequential overwrites
5.0.0-beta.14 #
Fixed #
avoid_variable_shadowing: false positive on sequential for/while/do loops reusing the same variable name — loop variables are scoped to their body and don't shadow each otheravoid_unused_assignment: false positive on conditional reassignment (x = x.toLowerCase()inside if-blocks) — now skips loop-body assignments, may-overwrite conditionals, and self-referencing RHSprefer_switch_expression: false positive on switch cases containing control flow (if/for/while) or multiple statements — also detects non-exhaustive switches with post-switch codeno_magic_number: false positive on numeric literals used as default parameter values — the parameter name provides context, making the number self-documentingavoid_unnecessary_to_list/avoid_large_list_copy: false positive when.toList()is required by return type, method chain, expression function body, or argument positionprefer_named_boolean_parameters: false positive on lambda/closure parameters — their signature is constrained by the expected function typeavoid_unnecessary_nullable_return_type: false positive on ternary expressions with null branches, map[]operator, and nullable method delegation — now checks static type nullability recursivelyavoid_duplicate_string_literals/avoid_duplicate_string_literals_pair: false positive on domain-inherent literals ('true','false','null','none') that are self-documentingavoid_excessive_expressions: false positive on guard clauses (early-return if-statements) and symmetric structural patterns — guard clauses now allowed up to 10 operators, symmetric repeating patterns are exemptprefer_digit_separators: false positive on 5-digit numbers — threshold raised from 10,000 to 100,000 (6+ digits) to match common style guide recommendationsrequire_list_preallocate: false positive whenList.add()is inside a conditional branch within a loop — preallocation is impossible when the number of additions is data-dependent
5.0.0-beta.13 #
Fixed #
prefer_match_file_name: false positive on Windows — backslash paths caused file name extraction to fail, reporting every correctly-named classprefer_match_file_name: false positive when file has multiple public classes — second class was reported even when first class matchedavoid_unnecessary_nullable_return_type: false positive on expression-bodied functions — ternaries with null branches, map lookups, and other nullable expressions were not recognizedprefer_unique_test_names: false positives when same test name appears in differentgroup()blocks — now builds fully-qualified names from group hierarchy, matching Flutter's test runner behavioravoid_dynamic_type: false positives forMap<String, dynamic>— the canonical Dart JSON type is now exemptno_magic_number_in_tests: expanded allowed integers to include 6–31 (day/month numbers), common round numbers (10000, 100000, 1000000), and exemptions for DateTime constructor arguments and expect() callsno_magic_string_in_tests: false positives for test fixture data — strings passed as arguments to functions under test and strings in expect() calls are now exemptavoid_large_list_copy: false positives for required copies —List<T>.from()with explicit type arguments (type-casting pattern) is now exempt;.toList()is exempt when returned, assigned, or otherwise structurally required
Changed #
- Merged duplicate rule
prefer_sorted_membersintoprefer_member_ordering;prefer_sorted_memberscontinues to work as a config alias - Clarified correction messages for
prefer_boolean_prefixes,prefer_descriptive_bool_names, andprefer_descriptive_bool_names_strictto distinguish scope (fields-only vs all booleans)
Publishing #
- Publish audit: consolidated quality checks into a single pass/warn/fail list instead of separate subsections per check
- Publish audit: US English spelling check displayed as a simple bullet instead of a standalone subsection
- Publish audit: bug reports grouped by status (done, in progress, unsolved) with scaled bars per group
- Publish audit: test coverage columns dynamically aligned to longest category name
- Init: "what's new" summary now shows all items (no
+N moreor section truncation) — only individual lines are truncated at 78 chars - Init: tier default changed from
comprehensivetoessentialfor fresh setups; re-runs default to the previously selected tier - Init: stale config version warning now tells the user how to fix it (
re-run "dart run saropa_lints" to update) - Init: stylistic walkthrough shows per-rule progress counter (
4/120 — 3%) and[quick fix]indicator for rules with IDE auto-fixes - Init: stylistic walkthrough rule descriptions rendered in default terminal color instead of dim gray for readability
5.0.0-beta.12 #
Added #
- Init: interactive stylistic rule walkthrough — shows code examples and lets users enable/disable each rule individually with y/n/skip/abort support and resume via
[reviewed]markers - Init:
--stylistic-allflag for bulk-enabling all stylistic rules (replaces old--stylisticbehavior);--no-stylisticto skip walkthrough;--reset-stylisticto clear reviewed markers - Init: auto-detect project type from pubspec.yaml — Flutter widget rules are skipped for pure Dart projects, package-specific rules filtered by dependencies
SaropaLintRule:exampleBad/exampleGoodproperties for concise terminal-friendly code snippets (40 rules covered)tiers.dart:flutterStylisticRulesset for widget-specific stylistic rules filtered by platform- Init: "what's new" summary shown during
dart run saropa_lints:init, with line truncation and a link to the full changelog - New rule:
prefer_sorted_imports(Comprehensive) — detects unsorted imports within each group (dart, package, relative) with quick fix to sort A-Z - New rule:
prefer_import_group_comments(Stylistic) — detects missing///section headers between import groups with quick fix to add them - New rule:
avoid_asset_manifest_json(Essential) — detects usage of removedAssetManifest.jsonpath (Flutter 3.38.0); runtime crash since the file no longer exists in built bundles - New rule:
prefer_dropdown_initial_value(Recommended) — detects deprecatedvalueparameter onDropdownButtonFormField, suggestsinitialValue(Flutter 3.35.0) with quick fix - New rule:
prefer_on_pop_with_result(Recommended) — detects deprecatedonPopcallback on routes, suggestsonPopWithResult(Flutter 3.35.0) with quick fix
Fixed #
no_empty_string: only flag empty strings in equality comparisons (== '',!= '') where.isEmpty/.isNotEmptyis a viable alternative — skip return values, default params, null-coalescing, replacement argsprefer_cached_getter: skip methods inside extensions and extension types (cannot have instance fields) and static methods (cannot cache to instance fields)prefer_compute_for_heavy_work: only flag encode/decode/compress calls inside widget lifecycle methods (build,initState, etc.) — library utility methods have no UI thread to protectprefer_keep_alive: check forTabBarView/PageViewidentifiers instead of naivecontains('Tab')/contains('Page')substring matchingprefer_prefixed_global_constants: case-insensitive descriptive pattern check for lowerCamelCase constants; expand pattern list (width, height, padding, etc.); narrow threshold to only flag names < 5 charsprefer_secure_random: only flagRandom()in security-related contexts (variable/method names containing token, password, encrypt, etc.); skip.shuffle()usage and literal-seeded constructorsprefer_static_method: skip methods inside extensions and extension types (cannot be made static in Dart)require_currency_code_with_amount: split into strong (price, amount, cost, fee) and weak (total, balance, rate) monetary signals; weak signals require 2+ matches with double/Decimal type; skip non-monetary class names (stats, count, metric, score, etc.)require_dispose_pattern: skip classes withconstconstructors (hold borrowed references, not owned resources)require_envied_obfuscation: skip class-level@Enviedwarning when all@EnviedFieldannotations explicitly specifyobfuscaterequire_https_only_test: skip HTTP URLs inside test infrastructure calls (test(),expect(),group(), etc.) since URL utility tests must exercise HTTPrequire_ios_callkit_integration: replace brand name string matching (Agora, Twilio, Vonage, WebRTC) with import-based detection for 13 VoIP packages; keep only unambiguous technical terms for string matchingavoid_barrel_files: skip files withlibrarydirective and the mandatory package entry point (lib/<package_name>.dart)avoid_duplicate_number_elements: only flagSetliterals — duplicate numeric values inListliterals are intentional (e.g. days-in-month)avoid_ignoring_return_values: skip property setter assignments (obj.prop = value) which have no return valueavoid_money_arithmetic_on_double: use camelCase word-boundary matching instead of substring matching to avoid false positives ontotalWidth,frameRate, etc.avoid_non_ascii_symbols: narrow from all non-ASCII to invisible/confusable characters only (zero-width, invisible formatters, non-standard whitespace)avoid_static_state: skipstatic constandstatic finalwith known-immutable types (RegExp,DateTime, etc.); retain detection ofstatic finalmutable collectionsavoid_stream_subscription_in_field: skip.listen()calls whose return value is passed as an argument (e.g.subs.add(stream.listen(...)))avoid_string_concatenation_l10n: skip numeric-only interpolated strings (e.g.'$a / $b') that contain no translatable word contentavoid_unmarked_public_class: skip classes where all constructors are private (extension already prevented)
Package Publishing Changes #
- Publish audit: added 3 new blocking checks —
flutterStylisticRulessubset validation,packageRuleSetstier consistency,exampleBad/exampleGoodpairing - Publish audit: doc comment auto-fix (angle brackets, references) now runs during audit step instead of only during analysis step
5.0.0-beta.11 #
Changed #
- CLI defaults to
initcommand when run without arguments (dart run saropa_lintsnow equivalent todart run saropa_lints init) - Publish script:
dart formatnow targets specific top-level paths, excludingexample*/directories upfront instead of tolerating exit-code 65 after the fact - Publish script: roadmap summary now includes color-coded bug report breakdown (unsolved/categorized/resolved) from sibling
saropa_dart_utils/bugs/directory - Deferred
avoid_misused_hooksrule removed from ROADMAP_DEFERRED (hook rules vary by context — not viable as static lint)
5.0.0-beta.10 #
Fixed #
- Init:
_stylisticRuleCategoriessynced withtiers.stylisticRules— removed obsoleteprefer_async_only_when_awaiting, added ~40 rules to proper categories instead of "Other stylistic rules" catch-all - Init: obsolete stylistic rules in consumer
analysis_options_custom.yamlare now cleaned up during rebuild, with warnings for user-enabled rules being dropped - Init: stylistic rules redundantly placed in RULE OVERRIDES section are detected — interactive prompt offers to move them to the STYLISTIC RULES section
- Init:
_buildStylisticSection()now filters againsttiers.stylisticRulesto prevent future category/tier desyncs dart analyzeexit codes 1-2 (issues found) no longer reported as "failed" — only exit code 3+ (analyzer error) is treated as failure- Progress bar stuck at ~83% — recalibration threshold no longer inflates expected file count when discovery overcounts
- Progress bar now shows 100% completion before the summary box
- Publish script: restored post-publish version bump (pubspec +
[Unreleased]section) — accidentally removed in v4.9.17 refactor - Publish script: optional
_offer_custom_lintprompt no longer blocks success status or timing summary on interrupt
Changed #
- Init log (
*_saropa_lints_init.log) now contains only setup/configuration data; rawdart analyzeoutput is no longer mixed in — the plugin's report (*_saropa_lint_report.log) covers analysis results - Init log written before analysis prompt so the path is available upfront
- Plugin report path displayed after analysis completes (with retry for async flush)
- Old report files in
reports/root are automatically migrated toreports/YYYYMMDD/date subfolders during init - Stream drain and exit code now awaited together via
Future.waitto prevent interleaved output - Persistent cache files (
rule_version_cache.json, export directories) moved fromreports/root toreports/_cache/subfolder
5.0.0-beta.9 #
Fixed #
- Plugin silently ignored by
dart analyze— generatedanalysis_options.yamlwas missing the requiredversion:key underplugins: saropa_lints:; the Dart SDK's plugin loader returnsnullwhen no version/path constraint is present, causing zero lint issues to be reported - Analysis server crash loop (FormatException) —
ProgressTrackerwas writing ANSI progress bars tostdout, which corrupts the JSON-RPC protocol used by the analysis server; all output now routes throughstderr
Added #
- Pre-flight validation checks in
init: verifies pubspec dependency, Dart SDK >= 3.6, and audits existing config for stalecustom_lint:sections or missingversion:keys - Post-write validation: confirms the generated file has
plugins:,version:,diagnostics:sections and expected rule count - Analysis results now captured in the init log file (previously only shown on terminal)
- Log summary section with version, tier, rule counts, and collected warnings
Changed #
dart analyzeoutput is now captured and streamed (wasinheritStdiowith no capture)- Log file write deferred until after analysis completes so the report includes everything
- All tier YAML files now include
version: "^5.0.0-beta.8"for direct-include users - All report-generating scripts now write to
reports/YYYYMMDD/date subfolders with timestamped filenames (todo audit, full audit, lint candidates, rule versions)
Archive #
- Rules 4.15.1 and older moved to CHANGELOG_ARCHIVE.md
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 inreports/— reduces clutter when many reports accumulate --tier/--outputflags without a value now warn instead of silently using defaultsdart run saropa_lints:initwithout--tiernow prompts for interactive tier selection (was silently defaulting to comprehensive)
Fixed #
.pubignorepatterntest/was excludinglib/src/fixes/test/from published package — anchored to/test/so only the root test directory is excluded; this causeddart run saropa_lints:initto fail with a missing import error forreplace_expect_with_expect_later_fix.dart- Publish script
dart formatstep 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 onSaropaFixProducerbase class for consistent indentation in fix output
Changed #
- Moved generated export folders (
dart_code_exports/,dart_sdk_exports/,flutter_sdk_exports/) and report caches fromscripts/toreports/— scripts now write output to the gitignoredreports/directory, keepingscripts/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
_getIndentfrom 5 fix files into sharedSaropaFixProducer.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
OwaspMappinggetters;avoid_dynamic_code_loadingandavoid_unverified_native_library(M2),avoid_hardcoded_signing_config(M7), andavoid_sudo_shell_commands(M1) were previously invisible to the scanner - Completed test fixtures for
avoid_unverified_native_libraryandavoid_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 asbugs/todo_001throughtodo_004
5.0.0-beta.5 #
Added #
- Auto-migration from v4 (custom_lint) to v5 (native plugin) —
dart run saropa_lints:initauto-detects and converts v4 config, with--fix-ignoresfor ignore comment conversion - Plugin reads
diagnostics:section fromanalysis_options.yamlto 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 // reasonor// ignore: rule - reason) that silently break suppression, and moves the text to the line above - Quick fix support for 108 rules via native
SaropaFixProducersystem — enables IDE lightbulb fixes anddart fix --apply - 3 reusable fix base classes:
InsertTextFix,ReplaceNodeFix,DeleteNodeFixinlib/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.dartfile 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, causingdart pub publish --dry-runto 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
analyzeras explicit dependency —dart pub publishrejected 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
LintCodeconstructor — 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.1→5.0.0-beta.2) — version parsing, comparison, pubspec read/write, changelog extraction, and input validation all handle-suffix.Nformat
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 viaSaropaContext._wrapCallback(), cached per file. - Future-proof — the old
analyzer_pluginprotocol is being deprecated (Dart SDK #62164). custom_lint was the primary client. - ~18K lines removed — native API eliminates boilerplate (no more
CustomLintResolver/ErrorReporter/CustomLintContextparameter triples).
Added #
- Native plugin entry point (
lib/main.dart) withSaropaLintsPlugin SaropaFixProducerbase class for quick fixes (analysis_server_plugin)fixGeneratorsgetter onSaropaLintRulefor automatic fix registrationSaropaContextwith per-file filtering wrapper on all 83addXxx()methodsCompatVisitorbridging callbacks to nativeSimpleAstVisitordispatch- PoC quick fixes:
CommentOutDebugPrintFix,RemoveEmptySetStateFix - Native framework provides ignore-comment fixes automatically (no custom code needed)
- Config loader (
config_loader.dart) readsanalysis_options_custom.yamlat startup - Severity overrides via
severities:section (ERROR/WARNING/INFO/false per rule) - Baseline suppression wired into reporter — checks
BaselineManagerbefore every report - Impact tracking — every violation recorded in
ImpactTrackerby impact level - Progress tracking — files and violations tracked in
ProgressTrackerper 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.dartgenerates nativeplugins:format (wascustom_lint:)- Tier presets use
diagnostics:map entries (wasrules:list entries) - Init command runs
dart analyzeafter generation (wasdart run custom_lint) - All 96 rule files migrated to native
AnalysisRuleAPI SaropaLintRulenow extendsAnalysisRule(wasDartLintRule)LintCodeuses positional constructor:LintCode('name', 'message')(was named params)runWithReporterdropsCustomLintResolverparametercontext.addXxx()replacescontext.registry.addXxx()reporter.atNode(node)replacesreporter.atNode(node, code)(code is implicit)- Dependencies:
analysis_server_plugin: ^0.3.3replacescustom_lint_builder - README updated for v5:
dart analyzereplacesdart run custom_lint, tier preset includes, v4 migration FAQ
Removed #
custom_lint_builderdependency andlib/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 and Earlier #
For details on the initial release and versions 0.1.0 through 4.15.1, please refer to CHANGELOG_ARCHIVE.md.