Skip to content

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.

//declscope:package // shared with the reporting code
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.

//declscope:package

package database

//declscope:package
func userSeed() {}

A directive on a block reaches every spec in it. A directive on one spec overrides the block's.

//declscope:package
var (
    seed  = 1
    //declscope:private
    limit = 2 // this one is private
)

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