saropa_lints 5.0.0-beta.13
saropa_lints: ^5.0.0-beta.13 copied to clipboard
1729 custom lint rules with 293 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
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.