saropa_lints 4.1.2
saropa_lints: ^4.1.2 copied to clipboard
1500+ custom lint rules with 201 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.
Looking for older changes?
See CHANGELOG_ARCHIVE.md for versions 0.1.0 through 2.7.0.
4.1.1 - 2026-01-13 #
Added #
- New Rule:
avoid_cached_isar_stream([lib/src/rules/isar_rules.dart])- Detects and prevents caching of Isar query streams (must be created inline).
- Tier: Professional
- Quick Fix: Inlines offending Isar stream expressions at usage sites and removes the cached variable.
- Example: [example/lib/isar/avoid_cached_isar_stream_fixture.dart]
Tier Assignment for Previously Unassigned Rules #
The following 6 rules, previously implemented but not assigned to any tier, are now included in the most appropriate tier sets in lib/src/tiers.dart:
- Recommended Tier:
avoid_duplicate_test_assertions(test quality)avoid_real_network_calls_in_tests(test reliability)require_error_case_tests(test completeness)require_test_isolation(test reliability)prefer_where_or_null(idiomatic Dart collections)
- Professional Tier:
prefer_copy_with_for_state(state management, immutability)
This ensures all implemented rules are available through tiered configuration and improves coverage for test and state management best practices.
Rule Tier Assignment Audit #
- Ran
scripts/audit_rules.pyto identify all implemented rules not assigned to any tier. - Assigned the following rules to the most appropriate tier sets in
lib/src/tiers.dart:- Recommended:
avoid_duplicate_test_assertions,avoid_real_network_calls_in_tests,require_error_case_tests,require_test_isolation,prefer_where_or_null - Professional:
prefer_copy_with_for_state
- Recommended:
- All implemented rules are now available through tiered configuration. This ensures no orphaned rules and improves test and state management coverage.
- Updated changelog to document these assignments and maintain full transparency of tier coverage.
Tier Set Maintenance #
- Commented out unimplemented rules in all tier sets in
lib/src/tiers.dartto ensure only implemented rules are active per tier. - Confirmed all unimplemented rules are tracked in
ROADMAP.mdfor future implementation. - This change improves roadmap alignment and prevents accidental activation of unimplemented rules.
- Materially improve the message quality for all Critial rules
4.1.0 - 2026-01-12 #
Tier Assignment Audit #
181 rules previously unassigned to any tier are now properly categorized. These rules existed but were not included in tier configurations, meaning users weren't getting them unless explicitly enabled.
Essential Tier (+50 rules)
Critical and high-impact rules now included in the essential tier:
| Category | Rules Added |
|---|---|
| Security | avoid_deep_link_sensitive_params, avoid_path_traversal, avoid_webview_insecure_content, require_data_encryption, require_secure_password_field, prefer_html_escape |
| JSON/Type Safety | avoid_dynamic_json_access, avoid_dynamic_json_chains, avoid_unrelated_type_casts, require_null_safe_json_access |
| Platform Permissions | avoid_platform_channel_on_web, require_image_picker_permission_android, require_image_picker_permission_ios, require_permission_manifest_android, require_permission_plist_ios, require_url_launcher_queries_android, require_url_launcher_schemes_ios |
| Memory/Resource Leaks | avoid_stream_subscription_in_field, avoid_websocket_memory_leak, prefer_dispose_before_new_instance, require_dispose_implementation, require_video_player_controller_dispose |
| Widget Lifecycle | check_mounted_after_async, avoid_ref_in_build_body, avoid_flashing_content |
| Animation | avoid_animation_rebuild_waste, avoid_overlapping_animations |
| Navigation | prefer_maybe_pop, require_deep_link_fallback, require_stepper_validation |
| Firebase/Backend | prefer_firebase_remote_config_defaults, require_background_message_handler, require_fcm_token_refresh_handler |
| Forms/WebView | require_validator_return_null, avoid_image_picker_large_files, prefer_webview_javascript_disabled, require_webview_error_handling, require_webview_navigation_delegate, require_websocket_message_validation |
| Data/Storage | prefer_utc_for_storage, require_database_migration, require_enum_unknown_value |
| State/UI | require_error_widget, require_feature_flag_default, require_immutable_bloc_state, require_map_idle_callback, require_media_loading_state, prefer_bloc_listener_for_side_effects, require_cors_handling |
Recommended Tier (+83 rules)
Medium-impact rules for better code quality:
| Category | Rules Added |
|---|---|
| Widget Structure | avoid_deep_widget_nesting, avoid_find_child_in_build, avoid_layout_builder_in_scrollable, avoid_nested_providers, avoid_opacity_misuse, avoid_shrink_wrap_in_scroll, avoid_unbounded_constraints, avoid_unconstrained_box_misuse |
| Gesture/Input | avoid_double_tap_submit, avoid_gesture_conflict, avoid_gesture_without_behavior, prefer_actions_and_shortcuts, prefer_cursor_for_buttons, require_disabled_state, require_drag_feedback, require_focus_indicator, require_hover_states, require_long_press_callback |
| Forms/Testing | require_button_loading_state, require_form_validation, avoid_flaky_tests, avoid_real_timer_in_widget_test, avoid_stateful_test_setup, prefer_matcher_over_equals, prefer_mock_http, require_golden_test, require_mock_verification |
| Performance | avoid_hardcoded_layout_values, avoid_hardcoded_text_styles, avoid_large_images_in_memory, avoid_map_markers_in_build, avoid_stack_overflow, prefer_clip_behavior, prefer_deferred_loading_web, prefer_keep_alive, prefer_sliver_app_bar, prefer_sliver_list |
| State Management | avoid_late_context, prefer_cubit_for_simple_state, prefer_selector_over_consumer, require_bloc_consumer_when_both |
| Accessibility | avoid_screenshot_sensitive, avoid_semantics_exclusion, prefer_merge_semantics, avoid_small_text |
| Database/Navigation | require_database_index, prefer_transaction_for_batch, prefer_typed_route_params, require_refresh_indicator, require_scroll_controller, require_scroll_physics |
| Desktop/i18n | require_menu_bar_for_desktop, require_window_close_confirmation, require_intl_locale_initialization, require_notification_timezone_awareness |
Comprehensive Tier (+48 rules)
Low-impact style and pattern rules:
- Code style:
avoid_digit_separators,avoid_nested_try_statements,avoid_type_casts - Documentation:
prefer_doc_comments_over_regular,prefer_error_suffix,prefer_exception_suffix - Patterns:
prefer_class_over_record_return,prefer_record_over_equatable,prefer_guard_clauses - Async:
prefer_async_only_when_awaiting,prefer_await_over_then,prefer_sync_over_async_where_possible - Testing:
prefer_expect_over_assert_in_tests,prefer_single_expectation_per_test - And 33 more...
Intentionally Untiered (81 rules)
Stylistic/opinionated rules remain untiered for team-specific configuration:
- Quote style:
prefer_single_quotesvsprefer_double_quotes - Import style:
prefer_relative_importsvsprefer_absolute_imports - Member ordering:
prefer_fields_before_methodsvsprefer_methods_before_fields - Control flow:
prefer_ternary_over_if_nullvsprefer_if_null_over_ternary - Debug rules:
always_fail,greeting,firebase_custom
4.0.1 - 2026-01-12 #
Testing Best Practices Rules #
Activated 5 previously unregistered testing best practices rules:
| Rule | Tier | Description |
|---|---|---|
prefer_test_find_by_key |
Recommended | Suggests find.byKey() over find.byType() for reliable widget testing |
prefer_setup_teardown |
Recommended | Detects duplicated test setup code (3+ occurrences) |
require_test_description_convention |
Recommended | Ensures test names include descriptive words |
prefer_bloc_test_package |
Professional | Suggests blocTest() when detecting Bloc testing patterns |
prefer_mock_verify |
Professional | Warns when when() is used without verify() |
Note: avoid_test_sleep was already registered.
Code cleanup: Removed redundant test file path checks from these rules (file type filtering is handled by applicableFileTypes).
DX Message Quality Improvements #
Improved problem messages for 7 critical-impact rules to provide specific consequences instead of generic descriptions:
| Rule | Improvement |
|---|---|
require_secure_storage |
Now explains XML storage exposure enables credential extraction |
avoid_storing_sensitive_unencrypted |
Added backup extraction and identity theft consequence |
check_mounted_after_async |
Specifies State disposal during async gap |
avoid_stream_subscription_in_field |
Clarifies callbacks fire after State disposal |
require_stream_subscription_cancel |
Specifies State disposal context |
require_interval_timer_cancel |
Specifies State disposal context |
avoid_dialog_context_after_async |
Clarifies BuildContext deactivation during async gap |
Result: Critical impact rules now at 100% DX compliance (40/40 passing).
Documentation #
- PROFESSIONAL_SERVICES.md: Rewrote professional services documentation with clearer service offerings and contact information
4.0.0 - 2026-01-12 #
OWASP Compliance Mapping #
Security rules are now mapped to OWASP Mobile Top 10 (2024) and OWASP Top 10 (2021) standards, transforming saropa_lints from a developer tool into a security audit tool.
Coverage
| OWASP Mobile | Rules | OWASP Web | Rules |
|---|---|---|---|
| M1 Credential Usage | 5+ | A01 Broken Access Control | 4+ |
| M3 Authentication | 5+ | A02 Cryptographic Failures | 10+ |
| M4 Input Validation | 6+ | A03 Injection | 6+ |
| M5 Communication | 2+ | A05 Misconfiguration | 4+ |
| M6 Privacy Controls | 5+ | A07 Authentication Failures | 8+ |
| M8 Misconfiguration | 4+ | A09 Logging Failures | 2+ |
| M9 Data Storage | 7+ | ||
| M10 Cryptography | 4+ |
Gaps: M2 (Supply Chain), M7 (Binary Protection), and A06 (Outdated Components) require separate tooling — dependency scanners and build-time protections.
New Files
lib/src/owasp/owasp_category.dart-OwaspMobileandOwaspWebenums with category metadatalib/src/owasp/owasp_mapping.dart- Compliance reporting utilitieslib/src/owasp/owasp.dart- Barrel export
API
Rules expose OWASP mappings via the owasp property:
final rule = AvoidHardcodedCredentialsRule();
print(rule.owasp); // Mobile: M1 | Web: A07
// Generate compliance report
final mappings = getAllSecurityRuleMappings();
final report = generateComplianceReport(mappings);
Modified Files
lib/src/saropa_lint_rule.dart- AddedOwaspMapping? get owasptoSaropaLintRulebase classlib/src/rules/security_rules.dart- Added OWASP mappings to 41 security ruleslib/src/rules/crypto_rules.dart- Added OWASP mappings to 4 cryptography ruleslib/saropa_lints.dart- ExportOwaspMapping,OwaspMobile,OwaspWeb
Baseline Feature for Brownfield Projects #
The problem: You want to adopt saropa_lints on an existing project, but running analysis shows 500+ violations in legacy code. You can't fix them all before your next sprint, but you want new code to be clean.
The solution: The baseline feature records existing violations and hides them. Old code is "baselined" (hidden), new code is still checked. You can adopt linting today without fixing legacy code first.
Quick Start
# Generate baseline - hides all current violations
dart run saropa_lints:baseline
This command creates saropa_baseline.json and updates your analysis_options.yaml. Old violations are hidden, new code is still checked.
Three Combinable Baseline Types
| Type | Config | Description |
|---|---|---|
| File-based | baseline.file |
JSON file listing specific violations to ignore |
| Path-based | baseline.paths |
Glob patterns for directories (e.g., lib/legacy/) |
| Date-based | baseline.date |
Git blame - ignore code unchanged since a date |
All three types are combinable - any match suppresses the violation.
Full Configuration
custom_lint:
saropa_lints:
tier: recommended
baseline:
file: "saropa_baseline.json" # Specific violations
date: "2025-01-15" # Code unchanged since this date
paths: # Directories/patterns
- "lib/legacy/"
- "lib/deprecated/"
- "**/generated/"
only_impacts: [low, medium] # Only baseline these severities
CLI Commands
dart run saropa_lints:baseline # Generate new baseline
dart run saropa_lints:baseline --update # Refresh, remove fixed violations
dart run saropa_lints:baseline --dry-run # Preview without changes
dart run saropa_lints:baseline --help # See all options
New Files
lib/src/baseline/baseline_config.dart- Configuration parsinglib/src/baseline/baseline_file.dart- JSON file handlinglib/src/baseline/baseline_paths.dart- Glob pattern matchinglib/src/baseline/baseline_date.dart- Git blame integrationlib/src/baseline/baseline_manager.dart- Central orchestratorbin/baseline.dart- CLI tool
See README.md for full documentation.
New Rules #
OWASP Coverage Gap Rules
Five new rules to fill gaps in OWASP coverage:
| Rule | OWASP | Severity | Description |
|---|---|---|---|
avoid_ignoring_ssl_errors |
M5, A05 | ERROR | Detects badCertificateCallback = (...) => true that bypasses SSL validation |
require_https_only |
M5, A05 | WARNING | Flags http:// URLs (except localhost). Has quick fix to replace with HTTPS |
avoid_unsafe_deserialization |
M4, A08 | WARNING | Detects jsonDecode results used in dangerous operations without type validation |
avoid_user_controlled_urls |
M4, A10 | WARNING | Flags user input (text controllers) passed directly to HTTP methods without URL validation |
require_catch_logging |
M8, A09 | WARNING | Catch blocks that silently swallow exceptions without logging or rethrowing |
3.4.0 - 2026-01-12 #
Performance Optimizations #
Added comprehensive performance infrastructure to support 1400+ lint rules efficiently.
Caching Infrastructure
SourceLocationCache: O(log n) offset-to-line lookups via binary search with cached line start offsetsSemanticTokenCache: Caches resolved type information and symbol metadata across rulesCompilationUnitCache: Caches expensive AST traversal results (class names, method names, imports)ImportGraphCache: Caches project import graph for dependency queries and circular import detection
IDE Integration (Infrastructure Only)
ThrottledAnalysis: Debounces analysis during rapid typing (requires IDE hooks not available in custom_lint)SpeculativeAnalysis: Pre-analyzes files likely to be opened next (requires IDE hooks not available in custom_lint)- Note: These classes exist for future IDE integration but cannot be fully wired up without custom_lint framework changes
Rule Execution Optimization
RuleGroupExecutor: Groups related rules to share setup/teardown costs and intermediate resultsConsolidatedVisitorDispatch: Single AST traversal for multiple rules (reduces O(rules × nodes) to O(nodes))BaselineAwareEarlyExit: Skips rules when all violations are baselinedDiffBasedAnalysis: Only re-analyzes changed regions of files
Memory Optimization
StringInterner: Interns common strings (StatelessWidget, BuildContext) to reduce memory allocation- Pre-interns 35+ common Dart/Flutter strings at startup
LruCache: Generic LRU cache with configurable size limits to prevent unbounded memory growthMemoryPressureHandler: Monitors memory usage and auto-clears caches when threshold exceeded
Profiling
HotPathProfiler: Instruments hot paths to identify slow rules and operations- Tracks execution times, slow operations (>50ms threshold), and provides statistical analysis
- Enable via
HotPathProfiler.enable()for development debugging
Parallel Execution
ParallelAnalyzer: Now uses realIsolate.run()for true parallel file analysis- Distributes work across multiple CPU cores for 2-4x speedup on large projects
- Automatic fallback to sequential processing when isolates unavailable
Integration Wiring
- Startup initialization:
initializeCacheManagement()andStringInterner.preInternCommon()called at plugin startup - Memory tracking:
MemoryPressureHandler.recordFileProcessed()called per-file to trigger auto-clearing - Rule groups registered: 6 groups defined (async, widget, context, dispose, test, security) for batch execution
- Rapid analysis throttle: Content-hash-based throttle prevents duplicate analysis of identical content within 300ms
- Bloom filter pre-screening: O(1) probabilistic membership testing in
PatternIndexbefore expensive string searches - Content region skipping: Rules can declare
requiresClassDeclaration,requiresMainFunction,requiresImportsto skip irrelevant files - Git-aware file priority:
GitAwarePrioritytracks modified/staged files for prioritized analysis - Import-based rule filtering:
requiresFlutterImportgetter skips widget rules instantly for pure Dart files - Adaptive tier switching: Auto-switches to essential-tier rules during rapid editing (3+ analyses in 2 seconds)
New Rules #
avoid_circular_imports: Detects circular import dependencies usingImportGraphCache- Reports when files are part of an import cycle
- Suggests extracting shared types to break cycles
3.3.1 - 2026-01-12 #
Quick Fix Policy Update #
Updated contribution guidelines and roadmap with a plan to achieve 90% quick fix coverage.
HACK Comment Fixes Discouraged
// HACK: fix this manually fixes are now discouraged. They provide no real value. See CONTRIBUTING.md for details.
- Real fixes that transform code are required
- If a fix can't be implemented safely, don't add one
- Document "no fix possible" in the rule's doc comment
Quick Fix Implementation Plan
Added comprehensive plan to ROADMAP.md with:
- Category A: Safe transformations (100% target) - ~200 rules
- Category B: Contextual transformations (80% target) - ~400 rules
- Category C: Multi-choice fixes (50% target) - ~300 rules
- Category D: Human judgment required (0% fixes) - ~600 rules
Safety checklist: no deleting code, no behavior changes, works in edge cases.
Tooling #
scripts/audit_rules.py: Now displays per-file statistics table with line counts, rule counts, and fix counts for each rule file
3.3.0 - 2026-01-12 #
Audit Script v2.0 #
The scripts/audit_rules.py has been completely redesigned with improved readability and comprehensive analysis.
New Features
- OWASP Coverage Stats - Visual progress bars showing Mobile (8/10) and Web (10/10) coverage with uncovered categories listed
- Tier Distribution - Rule counts per tier (essential, recommended, professional, comprehensive, insanity) with cumulative totals and visual bars
- Severity Distribution - Critical/high/medium/low breakdown with percentages
- Quality Metrics - Quick fix coverage (13%), correction message coverage (99.6%), lines of code stats
- Orphan Rules Detection - Identifies rules implemented but not assigned to any tier (262 found)
- File Health Analysis - Largest files by rule count, files needing quick fixes
- DX Message Audit - Now shows all impact levels with pass rates and percentages
Improved Output
- Organized into logical sections: Rule Inventory, Distribution Analysis, Security & Compliance, Quality Metrics, ROADMAP Sync, DX Message Audit
- Visual progress bars for coverage metrics
- Cleaner section headers with Unicode box-drawing characters
- Compact mode (
--compact) to skip the file table for faster runs - Top 3 worst offenders shown in terminal, full details in exported report
Command Options
python scripts/audit_rules.py # Full audit
python scripts/audit_rules.py --compact # Skip file table
python scripts/audit_rules.py --dx-all # Show all DX issues
python scripts/audit_rules.py --no-dx # Skip DX audit
3.1.2 - 2026-01-12 #
New Rules #
Tiered File Length Rules (OPINIONATED)
Opinionated style preferences for teams that prefer smaller files. Not quality indicators - large files are often necessary and valid for data, enums, constants, generated code, configs, and lookup tables.
| Rule | Threshold | Tier | Severity |
|---|---|---|---|
prefer_small_files |
200 lines | insanity | INFO |
avoid_medium_files |
300 lines | professional | INFO |
avoid_long_files |
500 lines | comprehensive | INFO |
avoid_very_long_files |
1000 lines | recommended | INFO |
All rules can be disabled per-file with // ignore_for_file: rule_name.
Performance Optimizations #
Combined Pattern Index
- Global pattern index: Instead of each rule scanning for its patterns individually, we now build a combined index at startup and scan file content ONCE.
- O(patterns) instead of O(rules x patterns): For 1400+ rules with multiple patterns, this is a massive speedup in the pre-filtering phase.
- New
PatternIndexclass: Automatically built when rules are loaded, transparent to rule authors.
Incremental Analysis Tracking
- Skip unchanged files: New
IncrementalAnalysisTrackerremembers which rules passed on which files. - Content hash comparison: Only re-runs rules when file content actually changes.
- Config-aware cache invalidation: Cache automatically clears when tier or rule configuration changes.
- Per-rule tracking: Individual rules that pass are recorded, so even partial re-analysis benefits.
- Disk persistence: Cache survives IDE restarts! Saved to
.dart_tool/saropa_lints_cache.json. - Auto-save throttling: Saves after every 50 changes to balance performance vs data safety.
- Atomic writes: Uses temp file + rename to prevent corruption on crash.
File Metrics Cache
- Cached file metrics: New
FileMetricsCachecomputes line count, class count, function count, etc. once per file. - Shared across rules: All rules accessing file metrics use the same cached values.
- Includes content indicators:
hasAsyncCode,hasWidgetsfor fast filtering.
New Rule Optimization Hooks
requiresAsyncgetter: Skip rules on files without async/Future patterns.requiresWidgetsgetter: Skip rules on files without Widget/State patterns.maximumLineCountgetter: (DANGEROUS - use sparingly) Skip rules on very large files. Only for O(n²) rules where analysis time is prohibitive. Off by default.
Smart Content Filter
- New
SmartContentFilterclass: Combines multiple heuristics in a single filter check. - Supports patterns, line counts, keywords, async, widgets: One call to check all constraints.
Rule Priority Queue
- Cost-based rule ordering: Rules sorted by cost so cheap rules run first.
- Fail-fast optimization: Cheaper rules provide faster initial feedback.
- New
RulePriorityQueueclass: Sorts rules by cost + pattern count for optimal execution order.
Content Region Index
- Pre-indexed file regions: Imports, class declarations, and top-level code indexed separately.
- Targeted scanning: Rules checking imports don't need to scan function bodies.
- New
ContentRegionIndexclass: Computes and caches structural regions per file.
AST Node Type Registry
- Batch rules by node type: Group rules that care about the same AST nodes.
- Reduced visitor overhead: Instead of each rule registering callbacks, batch invocations.
- New
AstNodeTypeRegistryclass: Tracks which rules care about which node categories.
Content Fingerprinting
- Structural fingerprints: Quick hash of file characteristics (imports, classes, async, widgets).
- Similarity detection: Files with same fingerprint likely have same violations.
- New
ContentFingerprintclass: Enables caching across similar files.
Rule Dependency Graph
- Fail-fast chains: If rule A finds violations, skip dependent rule B.
- Prerequisite tracking: Declare rule dependencies for smarter execution.
- New
RuleDependencyGraphclass: Track and query rule dependencies.
Rule Execution Statistics
- Historical performance tracking: Track execution time and violation rates per rule.
- Dynamic optimization: Identify slow rules and rules that rarely find violations.
- New
RuleExecutionStatsclass: Records and queries rule performance data.
Lazy Pattern Compilation
- Deferred regex compilation: Patterns compiled only when actually needed.
- Skip compilation for filtered rules: If early filtering skips a rule, its patterns are never compiled.
- New
LazyPatternandLazyPatternCacheclasses: Lazy regex infrastructure.
Parallel Pre-Analysis
- Parallel file scanning: Pre-analyze files in parallel to populate caches before rules execute.
- Async batch processing: Files processed in batches with async gaps to avoid blocking.
- Unified cache warming: Computes metrics, fingerprints, file types, and pattern matches in one pass.
- Batch execution planning: Determines which rules should run on which files upfront.
- New
ParallelAnalyzerclass: Manages parallel pre-analysis of files. - New
ParallelAnalysisResultclass: Contains all pre-computed analysis data for a file. - New
RuleBatchExecutorclass: Plans and tracks which rules apply to which files. - New
BatchableRuleInfoclass: Rule metadata for batch execution planning.
Consolidated Visitor Dispatch
- Single-pass AST traversal: Instead of each rule registering separate visitors, dispatch to all rules from one traversal.
- Reduced traversal overhead: O(nodes) instead of O(rules × nodes) for visitor callbacks.
- Category-based registration: Rules register for specific AST node categories (imports, classes, invocations, etc.).
- New
ConsolidatedVisitorDispatchclass: Manages rule callbacks by node category. - New
NodeVisitCallbacktypedef: Standard callback signature for consolidated visitors.
Baseline-Aware Early Exit
- Skip fully-baselined rules: If all violations of a rule in a file are baselined, skip the rule entirely.
- Path-based baseline detection: Files covered by path-based baseline can skip matching rules.
- Violation counting: Track baselined violation counts for optimization decisions.
- New
BaselineAwareEarlyExitclass: Tracks and queries baseline coverage per file/rule.
Diff-Based Analysis
- Changed region tracking: Only re-analyze lines that changed since last analysis.
- Line range overlap detection: Skip rules whose scope doesn't overlap with changes.
- Simple line-by-line diff: Fast diff computation without external dependencies.
- Range merging: Consolidate overlapping change regions for efficient queries.
- New
DiffBasedAnalysisclass: Computes and caches changed regions per file. - New
LineRangeclass: Represents line ranges with overlap/merge operations.
Import Graph Cache
- Project-wide import graph: Parse imports once and cache the dependency graph.
- Transitive dependency queries: Check if file A transitively imports file B.
- Reverse graph: Track which files import a given file.
- Circular import detection: Find import cycles involving a specific file.
- New
ImportGraphCacheclass: Builds and queries the import graph. - New
ImportNodeclass: Represents a file's import relationships.
3.1.1 - 2026-01-12 #
New Rules #
- prefer_descriptive_bool_names_strict: Strict version of bool naming rule for insanity tier. Requires traditional prefixes (
is,has,can,should). Does not allow action verbs.
Enhancements #
- prefer_descriptive_bool_names: Now lenient (professional tier). Allows action verb prefixes (
process,sort,remove, etc.) andvaluesuffix.
Bug Fixes #
- no_boolean_literal_compare: Fixed rule not being registered in plugin. Was implemented but missing from
saropa_lints.dart. - avoid_conditions_with_boolean_literals: Now only checks logical operators (
&&,||). Equality comparisons (==,!=) are handled byno_boolean_literal_comparewhich has proper nullable type checking. This eliminates double-linting and false positives onnullableBool == true. - require_ios_permission_description: Fixed false positive on
ImagePicker()constructor. The rule now only triggers on method calls (pickImage,pickVideo, etc.) where it can detect the actual source (gallery vs camera). - require_ios_face_id_usage_description: Now checks Info.plist before reporting. Previously always triggered on
LocalAuthenticationusage regardless of whetherNSFaceIDUsageDescriptionwas already present. - AvoidContextAcrossAsyncRule: Now recognizes mounted-guarded ternary pattern
context.mounted ? context : nullas safe. - PreferDocCurlyApostropheRule: Fixed quick fix not appearing - was searching
precedingCommentsinstead ofdocumentationComment. Renamed fromPreferCurlyApostropheRuleto clarify it only applies to documentation. - Missing rule name prefixes: Fixed 17 rules that were missing the
[rule_name]prefix in theirproblemMessage. Affected rules:avoid_future_tostring,prefer_async_await,avoid_late_keyword,prefer_simpler_boolean_expressions,avoid_context_in_initstate_dispose,avoid_shrink_wrap_in_lists,prefer_widget_private_members,avoid_hardcoded_locale,require_ios_permission_description,avoid_getter_prefix,prefer_correct_callback_field_name,prefer_straight_apostrophe,prefer_curly_apostrophe,avoid_dynamic,no_empty_block.
3.1.0 - 2026-01-12 #
Enhancements #
- Rule name prefix in messages: All 1536 rules now prefix
problemMessagewith[rule_name]for visibility in VS Code's Problems panel.
Bug Fixes #
- AvoidContextAfterAwaitInStaticRule: Now recognizes
context.mountedguards to prevent false positives. - AvoidStoringContextRule: No longer flags function types that accept
BuildContextas a parameter (callback signatures). - RequireIntlPluralRulesRule: Only flags
== 1or!= 1patterns, not general int comparisons. - AvoidLongRunningIsolatesRule: Less aggressive on
compute()- skips when comments indicate foreground use or in StreamTransformer patterns.
3.0.2 - 2026-01-12 #
Bug Fixes #
Async Context Utils
- Compound
&&mounted checks: Fixed detection of mounted checks in compound conditions.if (mounted && otherCondition)now correctly protects the then-branch since short-circuit evaluation guaranteesmountedis true when the body executes. - Nested mounted guards: Fixed
ContextUsageFinderto recognize context usage inside nestedif (mounted)blocks. Previously, patterns likeif (someCondition) { if (context.mounted) context.doThing(); }would incorrectly flag the inner usage.
AvoidUnawaitedFutureRule
- Lifecycle method support: Extended safe fire-and-forget detection to include
didUpdateWidget()anddeactivate()in addition todispose(). These lifecycle methods are synchronous and subscription cleanup doesn't need to be awaited. - onDone callback support: Added support for
StreamController.close()inonDoneandonErrorcallbacks. TheonDoneparameter ofStream.listen()isvoid Function(), so you cannot await inside it - closing the controller here is standard cleanup for transformed streams.
PreferExplicitTypesRule
- No longer flags
dynamic: The rule now only flagsvarandfinalwithout explicit types.dynamicis an explicit type choice (commonly used for JSON handling), not implicit inference likevar.
PreferSnakeCaseFilesRule
- Multi-part extension support: Added recognition of common multi-part file extensions used in Dart/Flutter projects:
.io.dart,.dto.dart,.model.dart,.entity.dart,.service.dart,.repository.dart,.controller.dart,.provider.dart,.bloc.dart,.cubit.dart,.state.dart,.event.dart,.notifier.dart,.view.dart,.widget.dart,.screen.dart,.page.dart,.dialog.dart,.utils.dart,.helper.dart,.extension.dart,.mixin.dart,.test.dart,.mock.dart,.stub.dart,.fake.dart.
SaropaDiagnosticReporter
- Fixed zero-width highlight in
atToken: The built-inatTokenmethod had a bug whereendColumnequaledstartColumn, resulting in zero-width diagnostic highlights. Now usesatOffsetwith explicit length to ensure proper span highlighting.
3.0.1 - 2026-01-12 #
Performance Optimizations #
Content Pre-filtering
- New
requiredPatternsgetter: Rules can specify string patterns that must be present for the rule to run. - Fast string search: Checks for patterns BEFORE AST parsing, skipping irrelevant files instantly.
- Example usage: A rule checking
Timer.periodiccan return{'Timer.periodic'}to skip files without timers.
Skip Small Files
- New
minimumLineCountgetter: High-cost rules can skip files under a threshold line count. - Efficient counting: Uses fast character scan instead of splitting into lines.
- Example usage: Complex nested callback rules can set
minimumLineCount => 50to skip small files.
File Content Caching
- New
FileContentCacheclass: Tracks file content hashes to detect unchanged files. - Rule pass tracking: Records which rules passed on unchanged files to skip redundant analysis.
- Impact: Files that haven't changed between saves can skip re-running passing rules.
Documentation #
- Updated ROADMAP.md: Added "Future Optimizations" section with Batch AST Visitors and Lazy Rule Instantiation as planned major refactors.
3.0.0 - 2026-01-12 #
Performance Optimizations #
This release focuses on significant performance improvements for large codebases. custom_lint is notoriously slow with 1400+ rules, and these optimizations address the main bottlenecks.
Tier Set Caching
- Cached tier rule sets: Previously,
getRulesForTier()was rebuilding Set unions on EVERY file analysis. Now tier sets are computed once on first access and cached for all subsequent calls. - Impact: ~5-10x faster tier filtering after first access.
Rule Filtering Cache
- Cached filtered rule list: Previously, the 1400+ rule list was filtering on every file. Now the filtered list is computed once per analysis session and reused.
- Impact: Eliminates O(n) filtering on each of thousands of files.
Analyzer Excludes
- Added comprehensive analyzer excludes in
analysis_options.yaml:- Generated code (
*.g.dart,*.freezed.dart,*.gr.dart,*.gen.dart,*.mocks.dart,*.config.dart) - Build artifacts (
build/**,.dart_tool/**) - Example files (
example/**)
- Generated code (
- Impact: Skips files that can't be manually fixed, reducing analysis time significantly.
Rule Timing Instrumentation
- New
RuleTimingTracker: Tracks execution time of each rule to identify slow rules. - Enable profiling: Set
SAROPA_LINTS_PROFILE=trueenvironment variable. - Slow rule logging: Rules taking >10ms are logged immediately for investigation.
- Timing report: Access
RuleTimingTracker.summaryfor a report of the 20 slowest rules.
Rule Cost Classification
- New
RuleCostenum:trivial,low,medium,high,extreme - 1483 rules tagged: Every rule now has a
costgetter indicating execution cost. - Rule priority ordering: Rules are sorted by cost so fast rules run first.
- Impact: Expensive rules (type resolution, full AST traversal) run last, after quick wins.
File Type Filtering
- New
FileTypeenum:widget,test,bloc,provider,model,service,general - Early exit optimization: Rules can declare
applicableFileTypesto skip non-matching files entirely. - 377 rules with file type filtering: Widget rules skip non-widget files, test rules skip non-test files, etc.
FileTypeDetector: Caches file type detection per file path for fast repeated access.- Impact: Widget-specific rules skip ~80% of files in typical projects.
Project Context Caching
- New
ProjectContextclass: Caches project root detection and pubspec parsing. - One-time parsing: Pubspec.yaml is parsed once per project, not per file.
- Impact: Eliminates redundant file I/O across 1400+ rules.
Documentation #
- Added performance tips to README: Guidance on using lower tiers during development for faster iteration.
- Tier speed comparison: Documented the performance impact of each tier level.
- Updated CONTRIBUTING.md: Added rule author guidance for
costandapplicableFileTypesgetters.
New Rules #
prefer_expanded_at_call_site: Warns when a widget'sbuild()method returnsExpanded/Flexibledirectly. Returning these widgets couples the widget to Flex parents; if later wrapped with Padding etc., it will crash. Better to let the caller addExpandedwhere needed. Quick fix available: Adds HACK comment to mark for manual refactoring. (WARNING, recommended tier)
Improved Rules #
avoid_expanded_outside_flex: Enhanced documentation explaining false positive cases (widgets returning Expanded that are used directly in Flex) and design guidance for preferring Expanded at call sites.
Breaking Changes #
None. All changes are backwards-compatible performance improvements.
2.7.0 and Earlier #
For details on the initial release and versions 0.1.0 through 1.6.0, please refer to CHANGELOG_ARCHIVE.md.
[Unassigned Rules Notice] - 2026-01-13 #
The following 6 implemented rules are not currently assigned to any tier. They remain available for manual configuration and will be reviewed for tier assignment in a future release:
avoid_duplicate_test_assertionsavoid_real_network_calls_in_testsprefer_copy_with_for_stateprefer_where_or_nullrequire_error_case_testsrequire_test_isolation