mitch_analyzer_plugin 0.2.1 copy "mitch_analyzer_plugin: ^0.2.1" to clipboard
mitch_analyzer_plugin: ^0.2.1 copied to clipboard

Security-focused Dart analyzer plugin with opinionated rules for safer, more predictable codebases.

mitch_analyzer_plugin #

mitch_analyzer_plugin is a security-minded Dart analyzer plugin that adds custom warnings, quick fixes, and architectural checks for Dart and Flutter projects.

It is built on top of the Dart 3.10+ analyzer plugin API and works in IDEs, in dart analyze, and in flutter analyze.

Installation #

Enable the published plugin in the analysis_options.yaml file located at the root of your package:

plugins:
  mitch_analyzer_plugin: ^0.2.1

When developing the plugin locally, use a path instead:

plugins:
  mitch_analyzer_plugin:
    path: ../mitch_analyzer_plugin

After updating the plugins: section, restart the Dart Analysis Server so the new plugin configuration is picked up.

Configuration #

Disable specific diagnostics #

Plugin diagnostics are enabled by default. You can selectively disable a rule under the plugin diagnostics section:

plugins:
  mitch_analyzer_plugin:
    path: ../mitch_analyzer_plugin
    diagnostics:
      avoid_print_usage: false

Exclude paths #

You can skip plugin diagnostics for globbed paths:

plugins:
  mitch_analyzer_plugin:
    path: ../mitch_analyzer_plugin
    exclude:
      global:
        - tool/**
      avoid_substring:
        - legacy/**
        - !legacy/allowlisted/**

Notes:

  • exclude.global applies to every rule.
  • exclude.<rule_name> applies to one rule.
  • ! re-includes a path after a broader exclusion.
  • Legacy exclude.* remains supported as a global alias.

Ignore a single occurrence #

// ignore: mitch_analyzer_plugin/avoid_print_usage
print('debug');

Ignore an entire file #

// ignore_for_file: mitch_analyzer_plugin/avoid_print_usage

Rule Catalog #

Rule Summary Quick fix Scope
avoid_conditions_with_boolean_literals Flags boolean literals in conditions and boolean operators. no Dart / Flutter
avoid_dynamic Flags dynamic declarations and type arguments. yes Dart / Flutter
avoid_empty_spread Flags spreads of empty collection literals. no Dart / Flutter
avoid_explicit_type_declaration Flags redundant local type annotations with stable inference. no Dart / Flutter
avoid_explicit_pattern_field_name Flags redundant explicit field names in patterns. no Dart / Flutter
avoid_inferrable_type_arguments Flags explicit type arguments that inference can recover. no Dart / Flutter
avoid_instantiating_in_value_provider Flags new instances passed directly to Provider.value. no Flutter / provider-like patterns
avoid_late_keyword Flags late variables and fields. no Dart / Flutter
avoid_non_null_assertion Flags postfix ! null assertions. no Dart / Flutter
avoid_non_nullable_provider_context_access Flags non-nullable context.read/watch/select provider generics. yes Flutter / provider-like patterns
avoid_print_usage Flags print and debugPrint calls. yes Dart / Flutter
avoid_substring Flags direct String.substring() usage. no Dart / Flutter
avoid_type_casts Flags as casts in local code. no Dart / Flutter
avoid_unnecessary_overrides Flags trivial overrides that only forward to super. no Dart / Flutter
avoid_unused_assignment Flags overwritten local writes with no read in between. no Dart / Flutter
avoid_unused_generics Flags unused generic type parameters in local declarations. no Dart / Flutter
avoid_unused_instances Flags discarded instance creation expressions. no Dart / Flutter
avoid_unused_parameters Flags unused parameters in bounded private and local scopes. no Dart / Flutter
avoid_unsafe_collection_methods Flags unsafe indexed, getter, pattern, and *Where collection access. no Dart / Flutter
avoid_unsafe_reduce Flags reduce calls without a preceding emptiness guard. no Dart / Flutter

Example Projects #

This repository includes two showcase projects:

  • example/dart_demo: demonstrates the non-Flutter rules.
  • example/flutter_demo: demonstrates the shared Dart / Flutter rules plus the provider-oriented Flutter rules in a runnable app.

To validate both examples from the repository root:

dart test test/example_validation_test.dart

Rule Reference #

avoid_dynamic #

Why: Avoids erasing type information with dynamic.

DON'T:

dynamic value = fetchValue();
List<dynamic> values = [];
dynamic parse(dynamic input) => input;

DO:

Object? value = fetchValue();
List<Object?> values = [];
Object? parse(Object? input) => input;

Quick fix: Replaces dynamic with Object?.

avoid_empty_spread #

Why: Empty spreads add noise without changing the resulting collection literal.

DON'T:

final values = [...[]];
final uniqueValues = {...<int>{}};
final entries = {...<String, int>{}};

DO:

final values = <Object?>[];
final uniqueValues = <int>{1, 2, 3};
final entries = <String, int>{'answer': 42};

Quick fix: None.

avoid_explicit_type_declaration #

Why: Explicit local variable types are redundant when a stable initializer already spells out the same readable type.

DON'T:

String name = 'mitch';
List<int> values = <int>[1, 2, 3];

DO:

final name = 'mitch';
final values = <int>[1, 2, 3];

Quick fix: None.

avoid_explicit_pattern_field_name #

Why: Pattern shorthand keeps destructuring fields smaller when the local variable already matches the field name.

DON'T:

if (value case Box(width: var width)) {
  print(width);
}

DO:

if (value case Box(:var width)) {
  print(width);
}

Quick fix: None.

avoid_conditions_with_boolean_literals #

Why: Boolean literals inside conditions usually mean dead or redundant logic.

DON'T:

if (true) {
  run();
}

while (enabled && true) {
  tick();
}

DO:

run();

while (enabled) {
  tick();
}

Quick fix: None.

avoid_inferrable_type_arguments #

Why: Explicit type arguments add noise when local arguments or literal elements already force the same inferred type.

DON'T:

final value = identity<int>(1);
final numbers = <int>[1, 2, 3];

DO:

final value = identity(1);
final numbers = [1, 2, 3];

Quick fix: None.

avoid_instantiating_in_value_provider #

Why: Provider.value and related constructors should reuse an existing instance.

DON'T:

Provider<MyService>.value(value: MyService());
ChangeNotifierProvider<MyNotifier>.value(value: MyNotifier());

DO:

final service = MyService();
Provider<MyService>.value(value: service);

final notifier = MyNotifier();
ChangeNotifierProvider<MyNotifier>.value(value: notifier);

Quick fix: None.

avoid_late_keyword #

Why: late shifts initialization errors to runtime.

DON'T:

late String token = '';

class Session {
  late int retries = 0;
}

DO:

String token = '';

class Session {
  int retries = 0;
}

Quick fix: None.

avoid_non_null_assertion #

Why: Postfix null assertions can throw at runtime and hide missing null handling.

DON'T:

final name = user.name!;

DO:

final name = user.name ?? 'unknown';

Quick fix: None.

avoid_non_nullable_provider_context_access #

Why: Using non-nullable provider generics with context.read/watch/select hides missing provider wiring until runtime.

DON'T:

context.read<MyService>();
context.watch<MyService>();
context.select<MyService, int>((service) => service.count);

DO:

context.read<MyService?>();
context.watch<MyService?>();
context.select<MyService?, int>((service) => service?.count ?? 0);

Quick fix: Makes the provider generic type nullable when a safe transformation is available.

avoid_print_usage #

Why: print() and debugPrint() bypass structured logging.

DON'T:

print('loading');
debugPrint('request failed');

DO:

Logger().info('loading');
Logger().info('request failed');

Quick fix: Replaces print and debugPrint with Logger().info(...).

avoid_substring #

Why: String.substring() is often brittle and obscures intent.

DON'T:

final code = value.substring(0, 3);

DO:

final isPrefix = value.startsWith('ABC');
final updated = value.replaceRange(0, 3, 'ABC');

Quick fix: None.

avoid_type_casts #

Why: as casts defer type safety to runtime and often hide missing local checks.

DON'T:

final user = value as User;

DO:

if (value is User) {
  return value;
}
return null;

Quick fix: None.

avoid_unnecessary_overrides #

Why: Trivial overrides duplicate inherited behavior without adding local value. This plugin only reports method overrides whose entire body is one super method call forwarding the same parameters in the same shape.

DON'T:

class Base {
  String format(String value) => value;
}

class Derived extends Base {
  @override
  String format(String value) => super.format(value);
}

DO:

class Base {
  String format(String value) => value;
}

class Derived extends Base {}

Quick fix: None.

avoid_unused_assignment #

Why: Overwritten local writes hide dead work. This rule intentionally stays inside straight-line block segments and skips branches, loops, switch, try, and closure-captured variables.

DON'T:

void update() {
  var retryCount = 0;
  retryCount = 1;
  retryCount = 2;
  print(retryCount);
}

DO:

void update() {
  var retryCount = 2;
  print(retryCount);
}

Quick fix: None.

avoid_unused_generics #

Why: Unused generic parameters make local APIs look more general than they are.

DON'T:

void parse<T, Format>(T value) {
  print(value);
}

DO:

void parse<T>(T value) {
  print(value);
}

Quick fix: None.

avoid_unused_instances #

Why: Creating an object and discarding it immediately usually means dead work or a missing use-site. This rule only reports bare instance creation statements.

DON'T:

void warmUp() {
  Session();
}

DO:

void warmUp() {
  final session = Session();
  consume(session);
}

Quick fix: None.

avoid_unused_parameters #

Why: Unused parameters add noise to local APIs and hide dead signature surface. This plugin intentionally limits the rule to private methods, private top-level functions, and local functions when no override or callback contract is detected in the current unit.

DON'T:

void _parse(String source, int unusedRetryCount) {
  if (source.isEmpty) return;
}

DO:

void _parse(String source) {
  if (source.isEmpty) return;
}

Quick fix: None.

avoid_unsafe_collection_methods #

Why: Unsafe collection access can throw RangeError or StateError at runtime. This rule covers unsafe getters, iterable [], object-pattern getter access, and *Where calls without orElse. Map[] stays allowed, reduce stays in avoid_unsafe_reduce, and flow-sensitive safe-index proofs are intentionally out of scope.

DON'T:

final first = values.first;
final second = values[1];
final selected = values.firstWhere((item) => item.isActive);
if (value case List(:final first)) {
  print(first);
}

DO:

final first = values.firstOrNull;
final second = values.elementAtOrNull(1);
final selected = values.firstWhereOrNull((item) => item.isActive);
if (scores case List(:final firstOrNull)) {
  print(firstOrNull);
}

Quick fix: None.

avoid_unsafe_reduce #

Why: reduce throws on empty iterables unless local code proves the iterable is not empty first.

DON'T:

int sum(Iterable<int> values) {
  return values.reduce((left, right) => left + right);
}

DO:

int sum(Iterable<int> values) {
  if (values.isEmpty) {
    return 0;
  }

  return values.reduce((left, right) => left + right);
}

Quick fix: None.

Per-rule Docs #

Each rule also has a dedicated markdown page under rules/ with rationale, config snippets, and focused examples.

Development #

Common commands from the repository root:

dart pub get
dart format --output=none --set-exit-if-changed .
dart analyze
dart test
dart test test/example_validation_test.dart
dart test test/repository_conventions_test.dart
dart run bin/benchmark_analyze.dart --iterations=5 --target=.

Publishing #

This repository includes:

  • a validation workflow at .github/workflows/validate.yml
  • a release workflow at .github/workflows/publish.yml

Expected release flow:

  1. Update pubspec.yaml and CHANGELOG.md.
  2. Merge to main.
  3. Push a tag like v0.2.1.
  4. Let GitHub Actions publish the package.

One-time pub.flutter-io.cn setup is still required before the automated workflow can publish:

  1. Publish the first version manually.
  2. Configure GitHub Actions publishing for this package on pub.flutter-io.cn.
  3. Set the tag pattern to v{{version}}.
  4. Optionally require the pub.flutter-io.cn GitHub environment for manual approval.

Changelog #

See CHANGELOG.md.

0
likes
140
points
5
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Security-focused Dart analyzer plugin with opinionated rules for safer, more predictable codebases.

Repository (GitHub)
View/report issues

Topics

#analyzer #lint #static-analysis #security #plugin

License

BSD-3-Clause (license)

Dependencies

analysis_server_plugin, analyzer, analyzer_plugin, collection, yaml

More

Packages that depend on mitch_analyzer_plugin