Directives¶
A directive is a line comment //declscope:name, with a lowercase name, no spaces, and any argument after a space. Any other comment starting with declscope: is reported as malformed and has no effect. A trailing // reason is ignored.
| Directive | Level | Effect |
|---|---|---|
//declscope:package, //declscope:private |
Declaration or file | States the scope. On a type it also reaches the type's fields, but not its methods |
//declscope:ignore |
Declaration or file | Silences every rule |
//declscope:ignore <rules> |
Declaration or file | Silences the named rules, comma-separated |
//declscope:core |
File | Joins the file to the core namespace |
//declscope:namespace <name> |
File | Joins the file to a shared namespace |
Placement¶
A declaration-level directive goes in the doc comment, directly above the declaration. A file-level directive goes above the package clause.
A directive on a block reaches every spec in it. A directive on one spec overrides the block's.
A directive on a type reaches its fields and its interface method names. It does not reach its methods, which take their own file's level.
Where a file-level directive may sit
//declscope:namespace must come before the package clause. Three placements are accepted there.
| Placement | |
|---|---|
A blank line between the directive and package |
What this README writes |
The directive directly above package |
Accepted |
At the bottom of the package doc comment, after a blank // line |
Go's own convention for a directive in a doc comment |
Go excludes a //tool:name comment from a doc comment, so none of them reaches the rendered documentation.
Ignore levels¶
- Every level is consulted. An ignore counts as used whenever it covers a rule that would have fired, so overlapping ignores never make one another look unused.
- Ignores are consulted before the baseline. A suppression the baseline would also have absorbed still counts as used.
Unused and malformed directives¶
A directive that decides nothing is reported. Neither a suppression nor a claim of intent should outlive what justified it.
A scope directive binds a declaration when the scope it names is one the declaration could not have had under any configuration.
| The declaration | //declscope:package |
//declscope:private |
|---|---|---|
| Unexported | Binds. The default may be either scope | Binds, for the same reason |
| Exported | Inert. It has no boundary under any configuration | Binds. It narrows something nothing else would |
Because the test covers every configuration, a directive that names today's default is never reported. One line of .declscope.yaml never turns into hundreds of diagnostics.
Accounting is per physical directive. One written on a var (...) block counts as used as soon as any spec needed it, and is reported once when none did.
These reports carry the directive rule, so //declscope:ignore directive silences one. A bare //declscope:ignore covers it at the file level, but not on the declaration carrying it. An ignore must not exempt itself from the report written to catch it.
What each unused-directive report means
| Report | Meaning |
|---|---|
unused //declscope:ignore boundary on userSeed, limit |
No named declaration needed it |
unused file-level //declscope:ignore qualify |
Nothing in the file needed it |
unused //declscope:ignore: no checked declaration carries it |
Written on something declscope does not check, such as init or _ |
unused //declscope:package: every declaration it reaches states its own scope |
A block's directive that every spec overrode |
unused //declscope:package on Helper: nothing it reaches takes a scope |
Everything it reaches is exported |
unused file-level //declscope:package |
Every declaration in the file states its own scope, or is out of the subject |
Malformed directives
| Directive | Report |
|---|---|
//declscope:foo |
unknown directive declscope:foo |
// declscope:package, /*declscope:package*/, or any other comment starting with declscope: that is not //declscope:name |
malformed declscope directive: write it as //declscope:name |
//declscope:package x |
//declscope:package takes no argument |
//declscope:private and //declscope:package together |
conflicting scope directives: ... |
//declscope:core and //declscope:namespace together |
conflicting namespace directives: a core file's namespace is the core |
//declscope:ignore foo |
unknown rule "foo" in declscope:ignore (want one of boundary, qualify, surplus, directive, filter) |
//declscope:namespace after the package clause |
declscope:namespace must appear before the package clause |
Which variant reports an unused directive
An ignore is called unused only by a pass that sees every reference in the package. An ignore needed only by a test would otherwise be unused in one variant and necessary in another. A scope directive reads no references, so it is judged in every variant.
| Variant | Unused-ignore reports | Unused-scope reports |
|---|---|---|
| Package with no in-package tests | Yes | Yes |
| Ordinary variant, package with in-package tests | Deferred to the test variant | Yes |
| Test variant | Yes | Yes |
-test=false, package with in-package tests |
None | Yes |