What Go is doing
Go code normally does not show semicolons, but semicolons did not disappear from the language. They moved into the lexer.
The formal Go grammar uses semicolons as statement terminators. Ordinary Go source omits most of them because the lexer inserts them automatically while breaking the program into tokens. By the time parsing happens, the parser can still work with a grammar that has statement terminators.
That distinction matters. This is not quite the same as a parser generously accepting sloppy punctuation. It is a lexical rule: newlines sometimes become semicolons before the parser decides whether the program is syntactically valid.
The newline rule
The rule is intentionally small. When a line ends with a token that can plausibly end a statement, Go inserts a semicolon after that token.
That includes identifiers, basic literals, a few terminating keywords such as return, break, continue, and fallthrough, and closing or postfix tokens such as ++, --, ), ], and }.
So this source:
package main
import "fmt"
func main() {
fmt.Println("hello")
}
is effectively understood with statement terminators in the places Go's scanner can infer:
package main;
import "fmt";
func main() {
fmt.Println("hello");
};
Go programmers do not normally write the second version. The point is that the language can keep a compact surface syntax while still having a simple statement boundary rule underneath.
Why the brace goes on the same line
The surprising part appears when a programmer brings brace habits from C, JavaScript, Java, or C# and writes the opening brace on the next line.
func main()
{
fmt.Println("hello")
}
The token before the newline is ). That is one of the tokens that triggers semicolon insertion. So Go sees something much closer to this:
func main();
{
fmt.Println("hello");
};
That is not a valid function declaration. The same problem is why Go style keeps the opening brace of if, for, switch, and select on the same line as the control header:
if err != nil {
return err
}
not:
if err != nil
{
return err
}
Why this feels weird
It feels weird because Go's visual style looks relaxed: there are almost no semicolons on the page. But the rule is actually strict. Newlines are not always just whitespace. In specific positions, they are syntax.
That creates a small mismatch for programmers coming from languages where brace placement is mostly a style preference. In Go, brace placement is partly style and partly grammar. The common Go layout is not merely the house style chosen by gofmt. It is the layout that fits the language rule.
The upside is consistency. Go avoids an endless argument about brace placement by making one convention line up with the lexer. The downside is that the first encounter can feel arbitrary if nobody explains the semicolon rule.
Where goimports fits
goimports is a formatting and import-maintenance tool. It adds missing imports, removes unused imports, and formats code in the same style as gofmt. It is often used as an editor save hook.
It does not make invalid brace placement valid. If the code is already syntactically broken by semicolon insertion, the formatter cannot reinterpret the program into something else. The source has to obey Go's statement-boundary rule first.
That is why the usual workflow is simple: write ordinary Go shape, let the tool normalize whitespace and imports, and do not fight the brace position.
go install golang.org/x/tools/cmd/goimports@latest
goimports -l -w .
The -l flag lists files whose formatting would change, and -w writes the changes back to disk.
The practical memory
The short version is:
- Go's grammar still has semicolons.
- The lexer inserts most semicolons automatically.
- A newline after
),},], identifiers, literals, and some keywords can end a statement. - Opening braces for functions and control structures belong on the same line as the header.
gofmtandgoimportsreinforce the normal shape, but they are not a substitute for valid syntax.
Once that rule is internalized, the weirdness mostly disappears. Go is not being vague about punctuation. It is being very specific about where punctuation can be inferred.