agda-unused
agda-unused
checks for unused code in an Agda project, including:
- variables
- definitions
- postulates
- data & record types
import
statements
open
statements
- pattern synonyms
agda-unused
can be run globally on a full project, or locally on a file and
its dependencies. Running agda-unused
globally requires an .agda-roots
file
in the project root directory, which specifies the "roots" of the project. The
"roots" of a project are identifiers that are considered used even if not used
within the project itself. You might think of the .agda-roots
file as a
specification of the public interface of your project.
Example
File ~/Test.agda
:
module Test where
open import Agda.Builtin.Bool
using (Bool; false; true)
open import Agda.Builtin.Unit
_∧_
: Bool
→ Bool
→ Bool
false ∧ x
= false
_ ∧ y
= y
Command:
$ agda-unused --local Test.agda
Output:
/home/user/Test.agda:4,23-27
unused imported item ‘true’
/home/user/Test.agda:5,1-30
unused import ‘Agda.Builtin.Unit’
/home/user/Test.agda:11,9-10
unused variable ‘x’
Roots
Running agda-unused
globally requires an .agda-roots
file in the project
root directory. The .agda-roots
format consists of:
- Module names, each followed by a list of (possibly qualified) identifiers.
- Module names in parentheses.
All given module names not in parentheses are checked, along with their
dependencies. The given identifiers are considered roots; they will not be
marked unused. Qualified identifiers refer to identifiers defined within inner
modules. If no identifiers are given for a module, all publicly accessible
identifiers are considered roots.
All given module names in parentheses are ignored when checking for unused
files. Such modules may still be checked if imported by another module.
Example .agda-roots
file:
Main
- main
Pythagorean.Core
- isTriple
- allTriples
(Pythagorean.Examples)
Pythagorean.Theorems
Pythagorean.Utils
- Print.prettyNat
Spacing and indentation are irrelevant.
Usage
Usage: agda-unused [-r|--root ROOT] [-l|--local FILE]
Check for unused code in project with root directory ROOT
Available options:
-h,--help Show this help text
-r,--root ROOT Path of project root directory
-l,--local FILE Path of file to check locally
-j,--json Format output as JSON
The project root directory is determined as follows:
- If the
--root
option is given, its value is the project root.
- If the
--local
option is given, the nearest ancestor with an .agda-roots
file is the project root, if any.
- If the current directory has an
.agda-roots
file, it is the project root.
- Otherwise, the nearest ancestor of the current directory with an
.agda-roots
file is the project root, if any.
- Otherwise, we take the current directory as the project root.
Approach
We make a single pass through each relevant Agda file:
- When a new item (variable, definition, etc.) appears, we mark it unused.
- When an existing item appears, we mark it used.
This means, for example, if we have three definitions (say f
, g
, h
), each
depending on the previous one, where h
is not a root, then f
and g
are
considered used, while h
is considered unused. If we remove h
and run
agda-unsed
again, it will now report that g
is unused. This behavior is
different from Haskell's built-in tool, which would report that all three
identifiers are unused on the first run.
For a global check, we check each file appearing in .agda-roots
and its
dependencies. For a local check, we check just the given file and its
dependencies, ignoring all publicly accessible definitions.
JSON
If the --json
switch is given, the output is a JSON object with two fields:
type
: One of "none"
, "unused"
, "error"
.
message
: A string, the same as the usual output of agda-unused
.
The "none"
type indicates that there is no unused code.
Limitations
We currently do not support the following Agda features:
agda-unused
will produce an error if your code uses these language features.