saropa_lints 6.0.9
saropa_lints: ^6.0.9 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.9 #
Fixed #
- False positive reduction: Replaced substring/
.contains()with exact match, word-boundary regex, or type checks. CI baselines updated or removed where violations reached 0. Grouped by rule file; all affected rules listed:- animation_rules:
require_animation_controller_dispose— dispose check usesisFieldCleanedUp()fromtarget_matcher_utilsinstead of regex ondisposeBody. - api_network_rules:
require_http_status_check,require_retry_logic,require_connectivity_check,require_request_timeout,prefer_http_connection_reuse,avoid_redundant_requests,require_response_caching,prefer_api_pagination,require_offline_indicator,prefer_streaming_response,avoid_over_fetching,require_cancel_token,require_websocket_error_handling,avoid_websocket_without_heartbeat,prefer_timeout_on_requests,require_permission_rationale,require_permission_status_check,require_notification_permission_android13,require_sqflite_migration,require_websocket_reconnection,require_typed_api_response,require_image_picker_result_handling,require_sse_subscription_cancel— word-boundary regex or exact sets. Baseline removed (0 violations). - async_rules:
require_feature_flag_default, DateTime UTC storage rule, stream listen/StreamController rules,avoid_dialog_context_after_async,require_websocket_message_validation,require_loading_timeout,prefer_broadcast_stream, mounted/setState visitors,require_network_status_check,require_pending_changes_indicator,avoid_stream_sync_events,require_stream_controller_close,avoid_stream_subscription_in_field,require_stream_subscription_no_leak— exact target set,extractTargetName+endsWith, word-boundary regex, or exactStreamSubscriptiontype. Baseline removed. - disposal_rules:
require_media_player_dispose,require_tab_controller_dispose,require_receive_port_close,require_socket_close,require_debouncer_cancel,require_interval_timer_cancel,require_file_handle_close—disposeBody.contains(...)replaced withisFieldCleanedUp(). All disposal rules —typeName.contains(...)replaced with word-boundary RegExp for media/Tab/WebSocket/VideoPlayer/StreamSubscription/ReceivePort/Socket types. Baseline removed. - file_handling_rules: PDF rules, sqflite (whereargs, transaction, error handling, batch, close, reserved word, singleton, column constants), large-file rule,
require_file_path_sanitization— word-boundary regex or static lists. Baseline removed. - navigation_rules:
avoid_deep_link_sensitive_params,prefer_typed_route_params,avoid_circular_redirects,require_deep_link_fallback,require_stepper_validation,require_step_count_indicator,require_go_router_typed_params,require_url_launcher_encoding,avoid_navigator_context_issue— exact property name for queryParameters/pathSegments/pathParameters or word-boundary regex. Baseline removed. - permission_rules:
require_location_permission_rationale,require_camera_permission_check,prefer_image_cropping— word-boundary regex for rationale, camera check, cropper/crop/ImageCropper, profile-context keywords. Baseline removed. - provider_rules:
avoid_provider_for_single_value(exact set for Proxy/Multi),prefer_selector_over_single_watch(regex for.select(),avoid_provider_value_rebuild(endsWith('Provider')). - security_rules: RequireSecureStorageRule, RequireBiometricFallbackRule, AvoidStoringPasswordsRule, RequireTokenRefreshRule, AvoidJwtDecodeClientRule, RequireLogoutCleanupRule, RequireDeepLinkValidationRule, AvoidPathTraversalRule, RequireDataEncryptionRule, AvoidLoggingSensitiveDataRule, RequireSecureStorageForAuthRule, AvoidRedirectInjectionRule, PreferLocalAuthRule, RequireSecureStorageAuthDataRule, AvoidStoringSensitiveUnencryptedRule, HTTP client rule, RequireCatchLoggingRule, RequireSecureStorageErrorHandlingRule, AvoidSecureStorageLargeDataRule, RequireClipboardPasteValidationRule, OAuth PKCE rule, session timeout rule, AvoidStackTraceInProductionRule, RequireInputValidationRule — word-boundary RegExp or RegExp.escape. Baseline removed.
- widget_lifecycle_rules:
require_scroll_controller_dispose,require_focus_node_dispose— disposal check uses RegExp for.dispose()instead ofdisposeBody.contains('.dispose()'). Baseline 18→16.
- animation_rules:
- prefer_permission_request_in_context: Use exact match for Permission target (
PermissionorPermission.*) instead of substring match to avoid false positives on unrelated types.
Changed #
- Roadmap:
- Verified all 185
bugs/roadmaptask files againstlib/src/tiers.dart; none correspond to implemented rules. - Rule quality subsection made self-contained: describes the String.contains() anti-pattern audit (121+ instances across rule files; remediation via exact-match sets or type checks) and
test/anti_pattern_detection_test.dart; removed references to obsolete review documents. - Planned Enhancements table (Discussion #55–#61) removed; details live in
bugs/discussion/(one file per discussion). - Planned rules that have a task file in
bugs/roadmap/were removed from ROADMAP tables; ROADMAP now points to bugs/roadmap/ for task specs. - Deferred content merged into ROADMAP.md (Part 2). ROADMAP_DEFERRED.md removed as redundant.
- Cross-File Analysis CLI roadmap (formerly ROADMAP_CLI.md) merged into ROADMAP.md as Part 3: Cross-File Analysis CLI Tool Roadmap. ROADMAP_CLI.md removed.
- Verified all 185
- CHANGELOG: 6.0.7 Added section — consolidated six separate "new lint rules" lists into one list of 55 rules, removed redundant rule-name headers, and ordered entries alphabetically for readability.
- require_app_startup_error_handling: Documented that the rule only runs when the project has a crash-reporting dependency (e.g. firebase_crashlytics, sentry_flutter).
- Tier reclassification (no orphans): Rule logic, unit tests, and false-positive suppressors unchanged; only tier set membership in
lib/src/tiers.dartupdated. Moved to Essential:check_mounted_after_async,avoid_drift_raw_sql_interpolation. Moved to Recommended:prefer_semver_version,prefer_correct_package_name,require_macos_notarization_ready,avoid_animation_rebuild_waste,require_deep_link_fallback,require_stepper_validation,require_immutable_bloc_state. - Severity reclassification:
LintCodeseverity only; when the rule fires is unchanged. CI using--fatal-infosmay now fail where it did not. WARNING → ERROR:require_unknown_route_handler,avoid_circular_redirects,check_mounted_after_async,require_https_only,require_route_guards. - Discussion 062 (false positive reduction review): Lives in
bugs/discussion/discussion_062_false_positive_reduction_review.md; status in progress (move tobugs/history/only when fully complete). Ongoing guidance: CONTRIBUTING.md § Avoiding False Positives and.claude/skills/lint-rules/SKILL.md§ Reducing False Positives.
Added #
- Publish script (test coverage): Rule-instantiation status is now derived from the codebase. The Test Coverage report shows a "Rule inst." line (categories with a Rule Instantiation group / categories with a test file) and lists categories missing that group. Implemented in
scripts/modules/_rule_metrics.py(_compute_rule_instantiation_stats); does not readbugs/UNIT_TEST_COVERAGE_REVIEW.md. - Tests:
test/fixture_lint_integration_test.dart— runsdart run custom_linton example_async and asserts output is parseable withparseViolations. - Missing fixtures (single-per-category): Added fixtures and test list entries for: freezed (
avoid_freezed_any_map_issue), notification (prefer_local_notification_for_immediate), type_safety (avoid_redundant_null_check), ui_ux (prefer_master_detail_for_large), widget_lifecycle (require_init_state_idempotent). - Missing fixtures (two-per-category batch): Added fixtures and test list entries for: api_network (
prefer_batch_requests,require_compression), code_quality (avoid_inferrable_type_arguments), collection (avoid_function_literals_in_foreach_calls,prefer_inlined_adds), firebase (avoid_firebase_user_data_in_auth,require_firebase_app_check_production), stylistic_additional (prefer_sorted_imports,prefer_import_group_comments), web (avoid_js_rounded_ints,prefer_csrf_protection). - Missing fixtures (three-per-category batch): Added fixtures and test list entries for: control_flow (
avoid_double_and_int_checks,prefer_if_elements_to_conditional_expressions,prefer_null_aware_method_calls), hive (avoid_hive_datetime_local,avoid_hive_type_modification,avoid_hive_large_single_entry), performance (avoid_cache_stampede,prefer_binary_format,prefer_pool_pattern), widget_patterns (avoid_print_in_production,avoid_static_route_config,require_locale_for_text). - Missing fixtures (naming_style, type, class_constructor): Added fixtures and test list entries for: naming_style (
prefer_adjective_bool_getters,prefer_lowercase_constants,prefer_noun_class_names,prefer_verb_method_names), type (avoid_shadowing_type_parameters,avoid_private_typedef_functions,prefer_final_locals,prefer_const_declarations), class_constructor (avoid_accessing_other_classes_private_members,avoid_unused_constructor_parameters,avoid_field_initializers_in_const_classes,prefer_asserts_in_initializer_lists,prefer_const_constructors_in_immutables,prefer_final_fields). - UNIT_TEST_COVERAGE_REVIEW.md: Updated §2 table to show 0 missing fixtures for all 27 categories; added completion note and marked recommendation as done.
- Tests (rule instantiation): Added Rule Instantiation groups to
animation_rules_test.dart(19 rules),collection_rules_test.dart(25 rules),control_flow_rules_test.dart(31 rules),type_rules_test.dart(18 rules),naming_style_rules_test.dart(28 rules),class_constructor_rules_test.dart(20 rules),structure_rules_test.dart(45 rules). Each test instantiates the rule and assertscode.name,problemMessagecontains[code_name],problemMessage.length> 50, andcorrectionMessageis non-null. - Tests (rule instantiation, expanded): Added the same Rule Instantiation group to 24 additional category test files:
api_network_rules_test.dart(38),firebase_rules_test.dart(28),performance_rules_test.dart(46),ui_ux_rules_test.dart(19),widget_lifecycle_rules_test.dart(36),type_safety_rules_test.dart(17),async_rules_test.dart(46),security_rules_test.dart(55),navigation_rules_test.dart(36),accessibility_rules_test.dart(39),provider_rules_test.dart(26),riverpod_rules_test.dart(34),bloc_rules_test.dart(46),getx_rules_test.dart(23),architecture_rules_test.dart(9),documentation_rules_test.dart(9),dio_rules_test.dart(14),get_it_rules_test.dart(3),image_rules_test.dart(22),scroll_rules_test.dart(17),equatable_rules_test.dart(14),forms_rules_test.dart(27), andfreezed_rules_test.dart(10). These tests catch registration and code-name mismatches; behavioral tests (linter-on-code) remain a separate effort. Updatedbugs/UNIT_TEST_COVERAGE_REVIEW.md§1 and §3 accordingly. - Missing fixtures (structure): Added fixtures and test list entries for:
avoid_classes_with_only_static_members,avoid_setters_without_getters,prefer_getters_before_setters,prefer_static_before_instance,prefer_mixin_over_abstract,prefer_record_over_tuple_class,prefer_sealed_classes,prefer_sealed_for_state,prefer_constructors_first,prefer_extension_methods,prefer_extension_over_utility_class,prefer_extension_type_for_wrapper. - Tests (rule instantiation + fixtures): Added rule-instantiation tests (code.name, problemMessage, correctionMessage) and/or missing fixtures for: Debug:
prefer_fail_test_case,avoid_debug_print,avoid_unguarded_debug,prefer_commenting_analyzer_ignores,prefer_debugPrint,avoid_print_in_release,require_structured_logging,avoid_sensitive_in_logs,require_log_level_for_production. Complexity:avoid_bitwise_operators_with_booleans,avoid_cascade_after_if_null,avoid_complex_arithmetic_expressions,avoid_complex_conditions,avoid_duplicate_cascades,avoid_excessive_expressions,avoid_immediately_invoked_functions,avoid_nested_shorthands,avoid_multi_assignment,binary_expression_operand_order,prefer_moving_to_variable,prefer_parentheses_with_if_null,avoid_deep_nesting,avoid_high_cyclomatic_complexity. Connectivity:require_connectivity_error_handling,avoid_connectivity_equals_internet,require_connectivity_timeout(fixture added). Sqflite:avoid_sqflite_type_mismatch,prefer_sqflite_encryption(fixture added). Config:avoid_hardcoded_config,avoid_hardcoded_config_test,avoid_mixed_environments,require_feature_flag_type_safety,avoid_string_env_parsing,avoid_platform_specific_imports,prefer_semver_version. Lifecycle:avoid_work_in_paused_state,require_resume_state_refresh,require_did_update_widget_check,require_late_initialization_in_init_state,require_app_lifecycle_handling,require_conflict_resolution_strategy. Return:avoid_returning_cascades,avoid_returning_void,avoid_unnecessary_return,prefer_immediate_return,prefer_returning_shorthands,avoid_returning_null_for_void,avoid_returning_null_for_future. Exception:avoid_non_final_exception_class_fields,avoid_only_rethrow,avoid_throw_in_catch_block,avoid_throw_objects_without_tostring,prefer_public_exception_classes. Equality:avoid_equal_expressions,avoid_negations_in_equality_checks,avoid_self_assignment,avoid_self_compare,avoid_unnecessary_compare_to,no_equal_arguments,avoid_datetime_comparison_without_precision. Crypto:avoid_hardcoded_encryption_keys,prefer_secure_random_for_crypto,avoid_deprecated_crypto_algorithms,require_unique_iv_per_encryption,require_secure_key_generation. Db_yield:require_yield_after_db_write,suggest_yield_after_db_read,avoid_return_await_db. Context:avoid_storing_context,avoid_context_across_async,avoid_context_after_await_in_static,avoid_context_in_async_static,avoid_context_in_static_methods,avoid_context_dependency_in_callback. Theming:require_dark_mode_testing,avoid_elevation_opacity_in_dark,prefer_theme_extensions,require_semantic_colors. Platform:require_platform_check,prefer_platform_io_conditional,prefer_foundation_platform_check. Notification:require_notification_channel_android,avoid_notification_payload_sensitive,require_notification_initialize_per_platform,require_notification_timezone_awareness,avoid_notification_same_id,prefer_notification_grouping,avoid_notification_silent_failure,prefer_local_notification_for_immediate. Memory management:avoid_large_objects_in_state,require_image_disposal,avoid_capturing_this_in_callbacks,require_cache_eviction_policy,prefer_weak_references_for_cache,avoid_expando_circular_references,avoid_large_isolate_communication,require_cache_expiration,avoid_unbounded_cache_growth,require_cache_key_uniqueness,avoid_retaining_disposed_widgets,avoid_closure_capture_leaks,require_expando_cleanup. Disposal:require_media_player_dispose,require_tab_controller_dispose,require_text_editing_controller_dispose,require_page_controller_dispose,require_lifecycle_observer,avoid_websocket_memory_leak,require_video_player_controller_dispose,require_stream_subscription_cancel,require_change_notifier_dispose,require_receive_port_close,require_socket_close,require_debouncer_cancel,require_interval_timer_cancel,require_file_handle_close,require_dispose_implementation,prefer_dispose_before_new_instance,dispose_class_fields. Error handling:avoid_swallowing_exceptions,avoid_losing_stack_trace,avoid_generic_exceptions,require_error_context,prefer_result_pattern,require_async_error_documentation,avoid_nested_try_statements,require_error_boundary,avoid_uncaught_future_errors,avoid_print_error,require_error_handling_graceful,avoid_catch_all,avoid_catch_exception_alone,avoid_exception_in_constructor,require_cache_key_determinism,require_permission_permanent_denial_handling,require_notification_action_handling,require_finally_cleanup,require_error_logging,require_app_startup_error_handling,avoid_assert_in_production,handle_throwing_invocations.
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. - GitHub:
- Closed issues #13 (prefer_pool_pattern), #14 (require_expando_cleanup), #15 (require_compression), #16 (prefer_batch_requests), #18 (prefer_binary_format). Each was commented with resolution version v6.0.7 and closed as completed.
- Closed issues #20, #29 (require_pagination_for_large_lists, v6.0.7), #25, #34 (require_rtl_layout_support, v4.15.1), #24, #33 (prefer_sliverfillremaining_for_empty, v4.14.5), #26, #35 (require_stepper_state_management, v4.14.5), #38 (avoid_infinite_scroll_duplicate_requests, v4.14.5). Each was commented with the resolution version and closed as completed.
Added #
- 55 new lint rules:
avoid_deprecated_usage(Recommended, WARNING) — use of deprecated APIs from other packages; same-package and generated files ignored.avoid_unnecessary_containers(Recommended, INFO) — Container with only child (and optionally key); remove and use child directly (widget files only).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.handle_throwing_invocations(Professional, INFO) — invocations that can throw (e.g. @Throws, readAsStringSync, jsonDecode) not in try/catch.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_batch_requests(Professional, INFO) — await in for-loop with fetch-like method names; suggest batch endpoints. Resolves #16.prefer_binary_format(Comprehensive, INFO) — jsonDecode in hot path (timer/stream); suggest protobuf/MessagePack or compute(). Resolves #18.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_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_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.prefer_final_locals(Recommended, INFO) — local variables never reassigned should be final.prefer_form_bloc_for_complex(Professional, INFO) — Form with >5 fields suggests form state management (FormBloc, reactive_forms, etc.).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_local_notification_for_immediate(Recommended, INFO) — FCM for server-triggered messages; use flutter_local_notifications for app-generated.prefer_lowercase_constants(Recommended, INFO) — const/static final should use lowerCamelCase.prefer_master_detail_for_large(Professional, INFO) — list navigation without MediaQuery/LayoutBuilder; suggest master-detail on tablets.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_no_commented_code— Alias for existingprefer_no_commented_out_code(stylistic).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_pool_pattern(Comprehensive, INFO) — non-const allocation in hot path (timer/animation); suggest object pool. Resolves #13.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_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.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.require_compression(Comprehensive, INFO) — HTTP get/post/put/delete without Accept-Encoding; suggest gzip. Resolves #15.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_error_handling_graceful— flag raw exception (e.toString(), e.message, $e) shown in Text/SnackBar/AlertDialog inside catch blocks; recommend friendly messages.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_expando_cleanup(Comprehensive, INFO) — Expando with entries added but no cleanup (expando[key] = null). Resolves #14.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_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.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. Resolves #20, #29.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.require_text_scale_factor_awareness— Container/SizedBox with literal height containing Text may overflow at large text scale; recommend flexible layout (widget files only).
6.0.6 #
Added #
- 15 new lint rules:
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.