Detect when the first word of Go doc comments intended to reference the identifier, but had a typo.
docnametypo is a linter that doesn't require all functions to have doc comments, but checks for typos when they do. It analyzes the first word in doc comments to see whether it's attempting to reference the identifier name, and flags cases where it seems to intend to, but doesn't match (due to typos, renaming, etc). All other comment styles, or no comments at all, are freely allowed.
- Why docnametypo?
- Installation
- Usage
- golangci-lint Integration
- Examples & Configuration
- How It Works
- Troubleshooting
- License
Does your codebase sometimes start doc comments with the function or type name, but not always? Do you follow stricter doc comment rules with exported functions than unexported functions? Many codebases follow a relaxed documentation style that allows both, especially for unexported functions:
// parseConfig reads and validates the configuration file
func parseConfig(path string) error { ... }
// Reads the manifest and returns structured data
func parseManifest(path string) (*Manifest, error) { ... }If you're using linters like revive, godoc-lint or staticcheck, they may enforce strict // FunctionName does ... formatting for exported functions. But for unexported code, your codebase might not follow this rule consistently, and that's fine. docnametypo is designed to complement these existing linters and catch typos.
The problem: When you refactor code, it's easy to miss updating doc comments that referenced the old name:
// parseConfig reads and validates configuration <- Stale comment!
func parseManifest(path string) error { ... }
// ServerHTTP handles incoming requests <- Typo!
func ServeHTTP(w http.ResponseWriter, r *http.Request) { ... }The solution: docnametypo uses heuristics to understand the author's intent. It analyzes whether a comment appears to be trying to use the symbol name (and got it wrong), or not. The tool catches actual mistakes, while staying out of your way when you're writing freely. This lets your codebase maintain a loose practice for documenting code: sometimes using the function name as the first word, sometimes not. This practice can be limited to unexported functions and types, and optionally also include exported functions.
docnametypo uses multiple strategies:
- Damerau-Levenshtein distance: Catches typos and single-character transpositions (
confgurevsconfigure) - CamelCase analysis: Detects reordered words (
JSONEncodervsEncoderJSON) or missing chunks (TelemetryHistoryStatevsTelemetryHistory) - Capitalization patterns: Flags
NewHandlerin comments when the function isnewHandler - Narrative detection: Skips comments starting with verbs like
Creates,Initializes,Generates, etc. - Prefix handling: Allows configured prefixes like
opto be stripped before matching - Section headers & wildcards: Treats heading-style comments (
Metrics helpers, etc.) and tokens containing wildcards (likecommonPrefixLen*) as documentation sections instead of identifier references. - Plain-word vs camelCase (flagged): With
-skip-plain-word-camel(enabled by default), simple leading verbs such asDeleteorAddare treated as narrative when the function name contains extra camelCase chunks. - Distance gating: Even though
-maxdistdefaults to 5, matches only trigger when enough of the token overlaps (long shared prefix/suffix), preventing short English sentences from being misinterpreted as identifiers. - Camel chunk heuristics: Detects inserted/removed camelCase chunks and whole-word replacements (
handleVolumevshandleEphemeralVolume,processCIDRsvsvalidateCIDRs) without needing large edit distances.
These heuristics work together to distinguish probable typos from other types of comments.
go install github.com/cce/docnametypo/cmd/docnametypo@latestdocnametypo ./...The analyzer understands several flags:
| Flag | Default | Description |
|---|---|---|
-fix |
false |
Apply all suggested fixes to rewrite incorrect identifier tokens in doc comments. |
-test |
true |
Analyze test files in addition to regular source files. |
-maxdist |
5 |
Maximum Damerau-Levenshtein distance before a pair of words stops being considered a typo (guarded by a length/proportion gate to avoid matching whole sentences). |
-include-unexported |
true |
Check unexported functions/methods/types. This is the primary use case. |
-include-exported |
false |
Also check exported declarations. Enable this if you do not already enforce // Name ... elsewhere. |
-include-types |
false |
Extend the check to type declarations (honoring the exported/unexported switches above). |
-include-generated |
false |
Include files that carry the // Code generated ... DO NOT EDIT. header; off by default to avoid noisy generated code. |
-include-interface-methods |
false |
Check interface method declarations. Useful when interface docs must track implementation names. |
-allowed-leading-words |
(see note) | Comma-separated verbs treated as narrative intros (e.g. Create, Configure, Tests); matching comments are skipped. |
-allowed-prefixes |
`` | Comma-separated list of symbol prefixes (such as op) that may be stripped before comparing to the doc token. |
-skip-plain-word-camel |
true |
Skip simple leading words (e.g. Delete, Add) when the symbol contains camelCase segments to reduce narrative false positives. Set to false if you want to flag those cases. |
-max-camel-chunk-insert |
2 |
Maximum number of camelCase chunks that can be inserted or removed before comments stop being treated as typos. |
-max-camel-chunk-replace |
2 |
Maximum number of camelCase chunks that can be replaced before comments stop being treated as typos. |
Note: the default
-allowed-leading-wordslist iscreate,creates,creating,initialize,initializes,init,configure,configures,setup,setups,start,starts,read,reads,write,writes,send,sends,generate,generates,decode,decodes,encode,encodes,marshal,marshals,unmarshal,unmarshals,apply,applies,process,processes,make,makes,build,builds,test,tests.
docnametypo emits suggested fixes that rewrite the incorrect identifier token in the doc comment. Run:
docnametypo -fix ./...to automatically apply those edits. The golangci-lint module plugin also respects golangci-lint run --fix, which can configured to apply additional filtering on which paths to include or exclude.
docnametypo ships a golangci-lint module plugin. To integrate it:
-
Create
.custom-gcl.ymlnext to yourgo.mod:--- version: v2.5.0 name: custom-golangci-lint plugins: - module: github.com/cce/docnametypo import: github.com/cce/docnametypo/gclplugin version: main # or a tagged release
-
Run
golangci-lint customto build a local binary that bundles the plugin (the command uses.custom-gcl.yml). -
Reference the linter from your
.golangci.yml, for example:--- version: "2" linters: enable: - docnametypo settings: custom: docnametypo: type: module description: "docnametypo catches doc comments whose first token drifted from the actual name" original-url: "https://github.com/cce/docnametypo" settings: include-exported: true include-interface-methods: true include-types: true include-generated: false allowed-prefixes: op,ui allowed-leading-words: create,creates,setup,read maxdist: 2
Before:
// ServerHTTP handles incoming HTTP requests and routes them
// to the appropriate handler based on the request path.
func ServeHTTP(w http.ResponseWriter, r *http.Request) {
// implementation
}Output:
example.go:1:1: doc comment starts with 'ServerHTTP' but symbol is 'ServeHTTP' (possible typo or old name)
After fix:
// ServeHTTP handles incoming HTTP requests and routes them
// to the appropriate handler based on the request path.
func ServeHTTP(w http.ResponseWriter, r *http.Request) {
// implementation
}Levenshtein distance detection:
// confgure sets up the application <- Typo: "confgure" vs "configure"
func configure(app *App) error { ... }Prefix mismatch:
// newTelemetryHook creates a hook <- Should be "NewTelemetryHook"
func NewTelemetryHook() *Hook { ... }Narrative comments are allowed:
// Creates a new HTTP client <- This is fine (starts with "Creates")
func newHTTPClient() *Client { ... }
// This helper generates test fixtures <- This is fine (narrative)
func makeTestData() []byte { ... }
// Generates encryption keys for the session
func generateKeys() []byte { ... } <- This is fine (narrative verb)If your codebase uses narrative verbs like in the examples above, the default -allowed-leading-words list already covers common cases (create, generate, configure, etc.). If you use additional narrative verbs, add them:
docnametypo -allowed-leading-words=create,configure,setup,validate,process,handle ./...For codebases with consistent symbol prefixes (e.g., opThing, uiRegister):
// Thing operates on the UI to add things to the view
func opThing() { ... } <- Without -allowed-prefixes, this would be flagged
// Register the new operation with the UI
func uiRegister() { ... } <- Without -allowed-prefixes, this would be flaggedConfigure the allowed prefixes:
docnametypo -allowed-prefixes=op,ui ./...This allows doc comments to reference Thing in the first word when the function is opThing, without flagging it as a typo.
docnametypo uses multiple string matching algorithms to detect likely typos while avoiding false positives on legitimate narrative comments:
-
Extracts the first identifier-like token from doc comments, skipping labels such as
Deprecated:,TODO:,NOTE:, etc. -
Compares using multiple algorithms:
- Damerau-Levenshtein distance: Catches typos and single-character transpositions
- Example:
confgurevsconfigure(distance = 1)
- Example:
- CamelCase transposition detection: Catches reordered words in camelCase
- Example:
HTTPServervsServerHTTP(chunks swapped) - Example:
TelemetryHistoryStatevsTelemetryHistory(suffix difference)
- Example:
- Prefix/suffix heuristics: Catches capitalization mismatches
- Example:
newTelemetryFilteredHookvsNewTelemetryFilteredHook
- Example:
- Damerau-Levenshtein distance: Catches typos and single-character transpositions
-
Filters false positives by ignoring:
- Narrative comments starting with common verbs (
generates,creates,initializes, etc.) - Comments that clearly don't reference the symbol (diverge too much)
- Configured prefix variants (e.g.,
opThingvsThingwhenopis in-allowed-prefixes)
- Narrative comments starting with common verbs (
-
Works across all symbol types: functions, methods, types, and interface methods (based on configuration flags)
Because the analyzer is heuristic, the defaults stay conservative: only unexported symbols are checked out of the box so that it can complement, rather than duplicate, tools such as godoc-lint. Turn on -include-exported, -include-interface-methods, and -include-types when you want broader coverage.
Add common starting verbs to the allowed list:
docnametypo -allowed-leading-words=create,configure,setup,handle,process ./...Or extend the default list:
# Check current defaults
docnametypo -h | grep allowed-leading-words
# Add your own words to the list
docnametypo -allowed-leading-words=create,...,yourword ./...Generated code is excluded by default. If you're still seeing issues, ensure your generated files have the standard header:
// Code generated ... DO NOT EDIT.or use this linter as a golangci-lint plugin with the generated: lax setting to exclude more patterns.
If your codebase uses consistent prefixes (e.g., all UI helpers start with ui), use:
docnametypo -allowed-prefixes=ui ./...Add to your GitHub Actions workflow:
- name: Install docnametypo
run: go install github.com/cce/docnametypo/cmd/docnametypo@latest
- name: Run docnametypo
run: docnametypo ./...Or integrate via golangci-lint (see integration section).
This project is licensed under the BSD 3-Clause License.
Found a bug or have a feature request? Open an issue on GitHub.