anal 0.3.0
anal: ^0.3.0 copied to clipboard
A pluggable static analysis framework for Dart and Flutter projects with an extensible runner, rule registry, and CLI.
anal #
anal is a pluggable static analysis framework for Dart and Flutter projects.
It provides the contracts, registry, runner, built-in rules, and CLI that
custom analyzer rules plug into, so teams can implement project-specific checks
as plain Dart classes without writing a full analyzer plugin.
This release ships the framework plus the first built-in rule:
unused_function. Additional built-in rules, including broader unused
declaration detection and const suggestions, are planned for future releases.
Status #
anal is pre-1.0.0. Public APIs may change between minor versions while the
framework matures. Pin a specific version in your pubspec.yaml if you need
repeatable behavior.
Installation #
Add anal as a development dependency:
flutter pub add --dev anal
Or edit pubspec.yaml directly:
dev_dependencies:
anal: ^0.2.0
CLI Usage #
Run the analyzer against the current project:
dart run anal [options] [paths...]
When no paths are provided, anal inspects lib/, bin/, and test/.
Options:
--help,-h: print usage and exit.--version: print the package version and exit.--rules <id,id,...>: run only the listed rule ids.--exclude <glob>: exclude matching paths. Repeat for multiple patterns. Custom excludes are added on top of the built-in defaults.--no-default-excludes: disable the built-in default exclude patterns. Use--excludeto list any patterns you still want excluded.
By default, anal excludes generated files matching *.g.dart and
*.freezed.dart. Exclude patterns are matched against the file's basename,
its path relative to the current working directory, and its absolute path;
any match excludes the file. To opt out of the defaults entirely:
dart run anal --no-default-excludes
Exit codes:
0: no diagnostics withSeverity.error.1: at least one error diagnostic was emitted.64: command-line usage error.
Built-In Rules #
anal ships with the following rules enabled by default. To turn one off, pass
--rules with a list that omits it.
unused_function #
- Id:
unused_function - Default severity:
warning
Flags file-local function declarations that are never referenced:
- top-level private functions, whose names begin with
_, in libraries that have nopartfiles; - local function declarations inside another function or method body.
Both direct calls, such as _foo(), and tear-offs, such as _foo, count as a
use.
Deliberately not flagged in this release:
- public top-level functions;
- the library's
mainfunction; - methods, constructors, getters, setters, and operators;
externalfunctions;- functions annotated with
@pragma('vm:entry-point'); - files belonging to libraries that have
partfiles.
unused_class #
- Id:
unused_class - Default severity:
warning
Flags file-local declarations whose names begin with _ that are never
referenced within the same compilation unit:
classdeclarations (includingabstract,base,final,sealed, andinterfacemodifiers);mixindeclarations;enumdeclarations;extension typedeclarations.
Any reference to the declaration counts as a use, including type
annotations, constructor invocations, extends/implements/with/on
clauses, is/as checks, static-member access, enum-value access, and
constructor or static tear-offs.
Deliberately not flagged in this release:
- public top-level classes, mixins, enums, and extension types;
class _Foo = A with B;typedef-style class declarations (ClassTypeAlias);extensiondeclarations (the non-typeextension _Ext on T {}form);- declarations annotated with
@pragma('vm:entry-point'); - files belonging to libraries that have
partfiles.
unused_source_file #
- Id:
unused_source_file - Default severity:
warning
A cross-file rule that flags Dart source files in the analyzed set that
are never reached from any entry point. Reachability is computed by
walking import, export, and part directives whose resolved URI
lands inside the analyzed set; URIs that resolve to dart: libraries,
package dependencies, or files excluded from the run are ignored.
Entry points (always considered "reached") are:
- files under
bin/; - files under
test/; - files that declare a top-level
mainfunction; lib/<package>.dartand any other file sitting directly underlib/(i.e. not nested inside a subdirectory such aslib/src/), which together form the package's public surface.
A non-entry-point file counts as "used" if it is reachable, directly or
transitively, from an entry point via an import, export, or part
directive.
Deliberately not flagged in this release:
- entry-point files themselves (files under
bin/ortest/, files with a top-levelmain, andlib/*.dartfiles outsidelib/src/); - any file reached, directly or transitively, by an
import,export, orpartdirective from an entry point; - generated-file basenames such as
*.g.dartand*.freezed.dart, which are skipped defensively even if the runner's default excludes are turned off.
Custom Rules #
Implement AnalyzerRule, register it with a RuleRegistry, and pass the
registry to AnalysisRunner:
import 'package:anal/anal.dart';
class MyRule extends AnalyzerRule {
@override
String get id => 'my_rule';
@override
String get description => 'Flags a project-specific pattern.';
@override
Severity get defaultSeverity => Severity.warning;
@override
Iterable<Diagnostic> analyze(AnalysisContext context) sync* {
// Inspect context.unit and yield Diagnostic instances.
}
}
Future<void> main() async {
final registry = RuleRegistry()..register(MyRule());
const options = AnalOptions.defaults();
final runner = AnalysisRunner(registry: registry, options: options);
final diagnostics = await runner.run();
for (final diagnostic in diagnostics) {
print(diagnostic);
}
}
AnalyzerRule is dispatched once per file and remains the right contract
for file-local checks: implementations see a single resolved compilation
unit at a time and cannot observe sibling files.
For checks that must reason across files, implement MultiFileAnalyzerRule
instead. A multi-file rule is invoked once per run with the full set of
resolved compilation units, so it can build cross-file structures such as
import graphs or symbol indices. Register multi-file rules with the same
RuleRegistry used for AnalyzerRule implementations; the built-in
unused_source_file rule is implemented this way. Existing AnalyzerRule
implementations continue to work unchanged.
Development #
This repository uses FVM to pin Flutter. Install the configured SDK before working locally:
fvm install
fvm flutter pub get
Before opening a pull request, run the same checks as CI:
fvm dart format .
fvm dart analyze --fatal-infos --fatal-warnings
fvm flutter test --coverage
fvm dart pub publish --dry-run
See CONTRIBUTING.md for contribution workflow details and SECURITY.md for vulnerability reporting.
License #
anal is available under the MIT License.