This site is being rebuilt and some pages are out of date. For current details, write to [email protected]. This notice goes away when the rebuild is done.

No analytics unless you allow it, no tracking. This site keeps in your browser the language you pick, the theme, its colour, which site you chose, the currency on the pricing page and that you closed this notice; signing in adds session cookies. The legal page has the details.

Sign in

Radar · Go · Archive · Week 46 · Nov 10 – 16, 2025

go/ast: add BasicLit.ValueEnd field

Worth knowingstdlib

What changed
ast.BasicLit now has a ValueEnd field that records the actual end position of a literal, and BasicLit.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/parser and inspect the ValueEnd of the resulting BasicLit.
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

Todaygo
// 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