Scopes¶
The subject of the analysis is a package's unexported surface: its unexported package-level declarations, and the unexported members of any type.
A scope is how far a declaration may be used. There are two.
| Scope | Meaning | Rust equivalent |
|---|---|---|
package |
Usable anywhere in the package | pub(super) |
private |
Usable only inside its own namespace | No modifier |
There is no public. Go already spells that with a capital letter, and declscope never sees a use beyond the package edge.
Scope resolution¶
A declaration's scope comes from the first row that applies.
| The declaration | Scope |
|---|---|
Carries //declscope:package or //declscope:private |
The directive's |
| Is contained by something that carries one | The container's |
| Sits in a file carrying a file-level directive | The file's |
| Is exported | package |
| Otherwise | defaults.unexported, which is private unless configured |
Containment means a field inside its type, or a spec inside its var, const or type block.
An unexported declaration is package only where something says so: a directive, or the defaults key. Its name plays no part. A prefix marks ownership and grants nothing.
Important
Exportedness decides the default, and nothing else. An exported field of an unexported type still resolves to package, so making the type unexported protects nothing.
State the boundary instead. //declscope:private on the type binds every field it reaches.
Members¶
A member is a name written inside a type's declaration: a struct's field, or an interface's method name. It is written inside its type, so its namespace is the type's file's, and the type's directive reaches it.
A method with a receiver is not a member. It is an ordinary top-level declaration, and its own file gives it its namespace.
| Package-level declaration | Member | Method with a receiver | |
|---|---|---|---|
| Bounding namespace | Its file's | Its type's file's | Its file's |
| Contained by | Its var, const or type block |
Its type | Nothing |
| Naming rule | Applies | Does not apply | Only when filed away from its type |
The naming rule skips members because a member is never read on its own. Every use writes its value first, so u.name hands the reader u to follow. A method is read the same way, through its receiver.
What Go lacks for a member is encapsulation. Every unexported field is visible to the whole package. The private scope supplies it.
A type's methods filed in another file
Splitting a type's methods across files is ordinary Go. A builder often declares its state in one file and its chainable methods in another.
// statement.go (namespace: statement)
package database
type Statement struct {
Table string
wheres []string
}
// query.go (namespace: query)
package database
func (s *Statement) Where(cond string) *Statement {
s.wheres = append(s.wheres, cond)
return s
}
Writing Where in query.go is not itself a crossing. The field it reaches is one, and the report lands on statement.go.
$ declscope ./...
statement.go:5:2: field Statement.wheres is private to namespace "statement", but is used from namespace "query"
query.go:4:4: used here, in namespace "query"
query.go:4:22: used here, in namespace "query"
The naming rule asks the method to carry query. A method is read through its receiver, and s.Where points the reader at Statement's unit, which does not hold it.
query.go:3:21: method Where does not carry namespace "query" anywhere in its name; rename it to QueryWhere, or to another name that carries "query"
Neither report asks for a rename here. The two files are one unit split in two, and //declscope:namespace statement on query.go settles both.
Tip
An unexported interface method is the sealed interface idiom. Only this package can spell the name, so only this package can implement the interface. declscope gives it file granularity.
Satisfying an interface is not a use of the name, because a method set is resolved, not written. Naming the method does cross.
Warning
Operations on the whole value name no field: copying it, comparing it, zeroing it. They are outside what declscope can see. See Limits.