fix: detect go package clause after leading comments - #1723
Conversation
`GO_PACKAGE_RE` is anchored at the start of the snippet, so a go block that opens with a comment — a file-name comment or a `//go:build` constraint — is treated as having no package clause. mdsf then prepends its temporary package and hands gofmt two package clauses, which fails with «expected declaration, found 'package'». The failure is quiet: mdsf prints `error running gofmt` and exits zero, so the block is not formatted and not reported as unformatted either. Anchor the pattern per line. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
| // TODO: check for multiline comments | ||
| pub static GO_PACKAGE_RE: std::sync::LazyLock<Regex> = | ||
| std::sync::LazyLock::new(|| Regex::new(r"^\s*package\s+\w").unwrap()); | ||
| std::sync::LazyLock::new(|| Regex::new(r"(?m)^\s*package\s+\w").unwrap()); |
There was a problem hiding this comment.
This enable multine matching, won't this break some use cases.
package is a reserved word in Go, so it sounds safe
But you may something odd like this snippet without a package
/*
I like my
package manager
*/
const foo = 1But I feel like there is no better solution without dealing with complicated regexp to remove comments
There was a problem hiding this comment.
Thanks, that example is a real hole and it pushed the fix to the right place.
Dropped the (?m) and matched Go's own rule instead: the package clause is the first token that is neither whitespace nor a comment. has_go_package skips leading // and /* */ and applies the existing anchored pattern to the remainder, so your snippet stays without a package clause and still gets the temporary one. No comment-removing regex needed, and the TODO: check for multiline comments above GO_PACKAGE_RE is gone with it.
Both directions are covered in a new test_has_go_package module, your snippet included.
A package clause is the first token of a Go file that is neither whitespace nor a comment. Match that rule directly instead of widening the anchored pattern to every line, so a block comment containing a line that starts with `package` is not mistaken for a package clause. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Problem
GO_PACKAGE_REis anchored at the start of the snippet, not per line. A go block that opens with a comment therefore looks like a snippet without a package clause, and mdsf prepends its temporary package:gofmt then sees two package clauses and fails with
expected declaration, found 'package'. Sinceerror running gofmtexits zero, the block is neither formatted nor reported as unformatted — it just silently drops out of the check. Both a file-name comment and a//go:buildconstraint hit this, and both are common in documentation.Change
A package clause is the first token of a Go file that is neither whitespace nor a comment, so
has_go_packageskips leading//and/* */comments and applies the existing anchored pattern to the remainder.GO_PACKAGE_REkeeps its original semantics, and theTODO: check for multiline commentsabove it is resolved: a block comment containing a line that starts withpackageis not mistaken for a package clause.Checks
cargo test -p mdsf --lib parserpasses, with a newtest_has_go_packagemodule covering both directions (leading file-name comment,//go:build, a block comment before a real package clause, and the same block comment without one).cargo clippy --all-targetsis clean. On a corpus of Go-heavy markdown, the block above goes fromerror running gofmtto being formatted normally.