v0.4.3Dart 3.10+Apache-2.0

Dead code detector for Dart and Flutter.

Finds declarations nothing references and removes them for you. One command, no setup, backed by the Dart analysis server.

“Ciach!” /tɕax/ — Polish for the sound of a clean chop.

dart pub global activate ciach
my_app — ciach
$ ciach
lib/greeting.dart
  15:6  function  danglingFunction  (public)
  24:7  variable  unusedConstant  (public)

lib/orphans.dart
  8:7   class        UnusedClass  (public)
  22:7  class        FullyDeadClass  (public)
  31:3  constructor  ReferencedAsTypeOnly.new  (public)

Found 5 unused declarations in 2 files (scanned 13 files, 44 declarations, 0.5s).

--remove

Report it. Or ciach it.

One flag deletes what was found, doc comments included, after showing the list and asking first.

lib/greeting.dart
/// Referenced from bin/app.dart.
void registerHandlers() {
  _internalHelper();
}

void _internalHelper() {}

/// Nothing calls this any more.
void danglingFunction() {}

const usedConstant = 'hello';

/// Left behind after a refactor.
const unusedConstant = 'bye';

class UnusedClass {
  void orphanMethod() {}
}
ciach --remove
$ ciach --remove
lib/greeting.dart
  9:6   function  danglingFunction  (public)
  14:7  variable  unusedConstant  (public)
  16:7  class     UnusedClass  (public)
  17:8  method    UnusedClass.orphanMethod  (public)

Found 4 unused declarations in 1 file (scanned 1 file, 8 declarations, 0.4s).
Remove 4 unused declarations? [y/N] y
Removed 4 unused declarations from 1 file.

What --remove refuses to touch →

Features

Sharp, and careful about it.

  • Every declaration kind

    Classes, enums and their values, functions, methods, constructors, fields, getters, variables and more.

  • Backed by the real analyzer

    Reference queries go to the Dart analysis server: the same resolution your IDE uses, not a regex.

  • Built for CI

    Annotations inline on the pull request diff and a non-zero exit when anything turns up.

  • Safe defaults

    @override members, operators, entry points, generated files and toJson() are skipped unless you opt in.

  • Text, JSON or GitHub output

    Human-readable by default, machine-readable on request.

  • A config file, optionally

    Every flag can live in ciach.yaml. The command line always wins.

Output

Speaks human, machine and GitHub.

Pick the format with -f. Exit codes are the same in every one.

$ ciach
lib/orphans.dart
  8:7   class        UnusedClass  (public)
  10:8  method       UnusedClass.orphanMethod  (public)
  22:7  class        FullyDeadClass  (public)
  31:3  constructor  ReferencedAsTypeOnly.new  (public)

Found 4 unused declarations in 1 file (scanned 1 file, 6 declarations, 0.7s).
$ ciach -f json
{
  "summary": {
    "filesScanned": 1,
    "declarationsChecked": 6,
    "unusedCount": 4,
    "docOnlyCount": 0,
    "elapsedMs": 729
  },
  "unused": [
    {
      "name": "UnusedClass",
      "qualifiedName": "UnusedClass",
      "kind": "class",
      "file": "lib/orphans.dart",
      "line": 8,
      "column": 7,
      "isPrivate": false
    }
  ],
  "docOnly": []
}
$ ciach -f github
::warning file=lib/orphans.dart,line=8,col=7,title=Unused declaration::Unused class 'UnusedClass'
::warning file=lib/orphans.dart,line=10,col=8,title=Unused declaration::Unused method 'UnusedClass.orphanMethod'
::warning file=lib/orphans.dart,line=22,col=7,title=Unused declaration::Unused class 'FullyDeadClass'
::notice file=lib/greeting.dart,line=41,col=6,title=Referenced only from a doc comment::function '_docOnlyMentioned' has no code references, only a dartdoc link