Radar · Go · Archive · Week 46 · Nov 10 – 16, 2025
go/ast: add BasicLit.ValueEnd field
Worth knowingstdlib
- What changed
ast.BasicLitnow has aValueEndfield that records the actual end position of a literal, andBasicLit.End()returns this value when it is set.- Production impact
- The source does not say.
- Try it
- Parse a multiline raw string literal with
go/parserand inspect theValueEndof the resultingBasicLit. - Source
- github.com/golang/go/issues/76031
Understand it, then run it
ast.BasicLit represents a literal value in Go source code. Its End() method used to compute the literal’s end position by adding the literal’s start position to the length of the literal string stored in Value. For multiline raw string literals the scanner removes carriage‑return characters, so the string in Value is shorter than the actual text in the source file. The new ValueEnd field records the real end position of the literal, and BasicLit.End() now returns that value when it is set.
Run it now
// This program demonstrates the incorrect End position for a multiline raw
// string literal in Go 1.27.1. The change that adds BasicLit.ValueEnd is not
// yet available, so we show the problem that the proposal fixes.
package main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
)
func main() {
src := `package main
func main() {
s := ` + "`Hello\nWorld`" + `
}`
fset := token.NewFileSet()
// Parse the source code.
f, err := parser.ParseFile(fset, "example.go", src, 0)
if err != nil {
panic(err)
}
// Find the first BasicLit node (the raw string literal).
var lit *ast.BasicLit
ast.Inspect(f, func(n ast.Node) bool {
if bl, ok := n.(*ast.BasicLit); ok && bl.Kind == token.STRING {
lit = bl
return false
}
return true
})
if lit == nil {
panic("no string literal found")
}
// Print the literal value and its start position.
fmt.Printf("Literal value: %q\n", lit.Value)
fmt.Printf("Start position: %s\n", fset.Position(lit.Pos()))
// End() uses ValuePos + len(Value), which is incorrect for multiline raw strings.
fmt.Printf("End() position: %s\n", fset.Position(lit.End()))
// Compute the correct end position by looking at the file content.
// The file's content ends at the position of the closing backtick.
// We find the index of the closing backtick in the source string.
endIdx := len(src) - 1 // last character is the closing backtick
endPos := fset.File(lit.Pos()).Pos(endIdx)
fmt.Printf("Manual end position: %s\n", fset.Position(endPos))
}
What it printed when we ran it on Go 1.27.1
Literal value: "`Hello\nWorld`" Start position: example.go:3:7 End() position: example.go:4:7 Manual end position: example.go:5:1
Run sends this program (for Solidity, the contract and its tests) to our own sandbox, where it is compiled and run once, with no network, and what it printed or the test report comes back here. Nothing is kept. Runs are counted per visitor for the day so everyone gets a turn; the details are on the legal page.
Written by gpt-oss-20b from the linked source · claims checked against the sources · archive, not individually reviewed