Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/build_jsonv2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3475,7 +3475,7 @@ func TestBuildPlanJSONOutputBytes(t *testing.T) {

got := strings.ReplaceAll(string(planBytes), root, "TEMPDIR")

expected := `{"static":{"strings":[{"value":"result"},{"value":"p"},{"value":"1"},{"value":"test"}],"files":[{"value":"TEMPDIR/test.rego"}]},"plans":{"plans":[{"name":"test","blocks":[{"stmts":[{"type":"MakeObjectStmt","stmt":{"target":2,"file":0,"col":0,"row":0}},{"type":"BlockStmt","stmt":{"blocks":[{"stmts":[{"type":"CallStmt","stmt":{"func":"g0.data.test.p","args":[{"type":"local","value":0},{"type":"local","value":1}],"result":3,"file":0,"col":0,"row":0}},{"type":"ObjectInsertStmt","stmt":{"key":{"type":"string_index","value":1},"value":{"type":"local","value":3},"object":2,"file":0,"col":0,"row":0}}]}],"file":0,"col":0,"row":0}},{"type":"BlockStmt","stmt":{"blocks":[{"stmts":[{"type":"BlockStmt","stmt":{"blocks":[{"stmts":[{"type":"DotStmt","stmt":{"source":{"type":"local","value":1},"key":{"type":"string_index","value":3},"target":5,"file":0,"col":0,"row":0}},{"type":"ObjectMergeStmt","stmt":{"a":5,"b":2,"target":4,"file":0,"col":0,"row":0}},{"type":"BreakStmt","stmt":{"index":1,"file":0,"col":0,"row":0}}]}],"file":0,"col":0,"row":0}},{"type":"AssignVarStmt","stmt":{"source":{"type":"local","value":2},"target":4,"file":0,"col":0,"row":0}}]}],"file":0,"col":0,"row":0}},{"type":"AssignVarStmt","stmt":{"source":{"type":"local","value":4},"target":6,"file":0,"col":0,"row":0}},{"type":"MakeObjectStmt","stmt":{"target":7,"file":0,"col":0,"row":0}},{"type":"ObjectInsertStmt","stmt":{"key":{"type":"string_index","value":0},"value":{"type":"local","value":6},"object":7,"file":0,"col":0,"row":0}},{"type":"ResultSetAddStmt","stmt":{"value":7,"file":0,"col":0,"row":0}}]}]}]},"funcs":{"funcs":[{"name":"g0.data.test.p","params":[0,1],"return":2,"blocks":[{"stmts":[{"type":"ResetLocalStmt","stmt":{"target":3,"file":0,"col":4,"row":3}},{"type":"MakeNumberRefStmt","stmt":{"file":0,"col":4,"row":3,"index":2,"Index":2,"target":4}},{"type":"AssignVarOnceStmt","stmt":{"source":{"type":"local","value":4},"target":3,"file":0,"col":4,"row":3}}]},{"stmts":[{"type":"IsDefinedStmt","stmt":{"source":3,"file":0,"col":4,"row":3}},{"type":"AssignVarOnceStmt","stmt":{"source":{"type":"local","value":3},"target":2,"file":0,"col":4,"row":3}}]},{"stmts":[{"type":"ReturnLocalStmt","stmt":{"source":2,"file":0,"col":4,"row":3}}]}],"path":["g0","test","p"]}]}}`
expected := `{"static":{"strings":[{"value":"result"},{"value":"p"},{"value":"1"},{"value":"test"}],"files":[{"value":"TEMPDIR/test.rego"}]},"plans":{"plans":[{"name":"test","blocks":[{"stmts":[{"type":"MakeObjectStmt","stmt":{"target":2,"file":0,"col":0,"row":0,"end_col":0,"end_row":0}},{"type":"BlockStmt","stmt":{"blocks":[{"stmts":[{"type":"CallStmt","stmt":{"func":"g0.data.test.p","args":[{"type":"local","value":0},{"type":"local","value":1}],"result":3,"file":0,"col":0,"row":0,"end_col":0,"end_row":0}},{"type":"ObjectInsertStmt","stmt":{"key":{"type":"string_index","value":1},"value":{"type":"local","value":3},"object":2,"file":0,"col":0,"row":0,"end_col":0,"end_row":0}}]}],"file":0,"col":0,"row":0,"end_col":0,"end_row":0}},{"type":"BlockStmt","stmt":{"blocks":[{"stmts":[{"type":"BlockStmt","stmt":{"blocks":[{"stmts":[{"type":"DotStmt","stmt":{"source":{"type":"local","value":1},"key":{"type":"string_index","value":3},"target":5,"file":0,"col":0,"row":0,"end_col":0,"end_row":0}},{"type":"ObjectMergeStmt","stmt":{"a":5,"b":2,"target":4,"file":0,"col":0,"row":0,"end_col":0,"end_row":0}},{"type":"BreakStmt","stmt":{"index":1,"file":0,"col":0,"row":0,"end_col":0,"end_row":0}}]}],"file":0,"col":0,"row":0,"end_col":0,"end_row":0}},{"type":"AssignVarStmt","stmt":{"source":{"type":"local","value":2},"target":4,"file":0,"col":0,"row":0,"end_col":0,"end_row":0}}]}],"file":0,"col":0,"row":0,"end_col":0,"end_row":0}},{"type":"AssignVarStmt","stmt":{"source":{"type":"local","value":4},"target":6,"file":0,"col":0,"row":0,"end_col":0,"end_row":0}},{"type":"MakeObjectStmt","stmt":{"target":7,"file":0,"col":0,"row":0,"end_col":0,"end_row":0}},{"type":"ObjectInsertStmt","stmt":{"key":{"type":"string_index","value":0},"value":{"type":"local","value":6},"object":7,"file":0,"col":0,"row":0,"end_col":0,"end_row":0}},{"type":"ResultSetAddStmt","stmt":{"value":7,"file":0,"col":0,"row":0,"end_col":0,"end_row":0}}]}]}]},"funcs":{"funcs":[{"name":"g0.data.test.p","params":[0,1],"return":2,"blocks":[{"stmts":[{"type":"ResetLocalStmt","stmt":{"target":3,"file":0,"col":4,"row":3,"end_col":9,"end_row":3}},{"type":"MakeNumberRefStmt","stmt":{"file":0,"col":4,"row":3,"end_col":9,"end_row":3,"index":2,"Index":2,"target":4}},{"type":"AssignVarOnceStmt","stmt":{"source":{"type":"local","value":4},"target":3,"file":0,"col":4,"row":3,"end_col":9,"end_row":3}}]},{"stmts":[{"type":"IsDefinedStmt","stmt":{"source":3,"file":0,"col":4,"row":3,"end_col":9,"end_row":3}},{"type":"AssignVarOnceStmt","stmt":{"source":{"type":"local","value":3},"target":2,"file":0,"col":4,"row":3,"end_col":9,"end_row":3}}]},{"stmts":[{"type":"ReturnLocalStmt","stmt":{"source":2,"file":0,"col":4,"row":3,"end_col":9,"end_row":3}}]}],"path":["g0","test","p"]}]}}`

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These just add the new fields.


if diff := cmp.Diff(expected, got); diff != "" {
t.Errorf("unexpected result (-want, +got):\n%s", diff)
Expand Down
2 changes: 1 addition & 1 deletion cmd/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3475,7 +3475,7 @@ func TestBuildPlanJSONOutputBytes(t *testing.T) {

got := strings.ReplaceAll(string(planBytes), root, "TEMPDIR")

expected := `{"static":{"strings":[{"value":"result"},{"value":"p"},{"value":"1"},{"value":"test"}],"files":[{"value":"TEMPDIR/test.rego"}]},"plans":{"plans":[{"name":"test","blocks":[{"stmts":[{"type":"MakeObjectStmt","stmt":{"target":2,"file":0,"col":0,"row":0}},{"type":"BlockStmt","stmt":{"blocks":[{"stmts":[{"type":"CallStmt","stmt":{"func":"g0.data.test.p","args":[{"type":"local","value":0},{"type":"local","value":1}],"result":3,"file":0,"col":0,"row":0}},{"type":"ObjectInsertStmt","stmt":{"key":{"type":"string_index","value":1},"value":{"type":"local","value":3},"object":2,"file":0,"col":0,"row":0}}]}],"file":0,"col":0,"row":0}},{"type":"BlockStmt","stmt":{"blocks":[{"stmts":[{"type":"BlockStmt","stmt":{"blocks":[{"stmts":[{"type":"DotStmt","stmt":{"source":{"type":"local","value":1},"key":{"type":"string_index","value":3},"target":5,"file":0,"col":0,"row":0}},{"type":"ObjectMergeStmt","stmt":{"a":5,"b":2,"target":4,"file":0,"col":0,"row":0}},{"type":"BreakStmt","stmt":{"index":1,"file":0,"col":0,"row":0}}]}],"file":0,"col":0,"row":0}},{"type":"AssignVarStmt","stmt":{"source":{"type":"local","value":2},"target":4,"file":0,"col":0,"row":0}}]}],"file":0,"col":0,"row":0}},{"type":"AssignVarStmt","stmt":{"source":{"type":"local","value":4},"target":6,"file":0,"col":0,"row":0}},{"type":"MakeObjectStmt","stmt":{"target":7,"file":0,"col":0,"row":0}},{"type":"ObjectInsertStmt","stmt":{"key":{"type":"string_index","value":0},"value":{"type":"local","value":6},"object":7,"file":0,"col":0,"row":0}},{"type":"ResultSetAddStmt","stmt":{"value":7,"file":0,"col":0,"row":0}}]}]}]},"funcs":{"funcs":[{"name":"g0.data.test.p","params":[0,1],"return":2,"blocks":[{"stmts":[{"type":"ResetLocalStmt","stmt":{"target":3,"file":0,"col":4,"row":3}},{"type":"MakeNumberRefStmt","stmt":{"file":0,"col":4,"row":3,"index":2,"Index":2,"target":4}},{"type":"AssignVarOnceStmt","stmt":{"source":{"type":"local","value":4},"target":3,"file":0,"col":4,"row":3}}]},{"stmts":[{"type":"IsDefinedStmt","stmt":{"source":3,"file":0,"col":4,"row":3}},{"type":"AssignVarOnceStmt","stmt":{"source":{"type":"local","value":3},"target":2,"file":0,"col":4,"row":3}}]},{"stmts":[{"type":"ReturnLocalStmt","stmt":{"source":2,"file":0,"col":4,"row":3}}]}],"path":["g0","test","p"]}]}}`
expected := `{"static":{"strings":[{"value":"result"},{"value":"p"},{"value":"1"},{"value":"test"}],"files":[{"value":"TEMPDIR/test.rego"}]},"plans":{"plans":[{"name":"test","blocks":[{"stmts":[{"type":"MakeObjectStmt","stmt":{"target":2,"file":0,"col":0,"row":0,"end_col":0,"end_row":0}},{"type":"BlockStmt","stmt":{"blocks":[{"stmts":[{"type":"CallStmt","stmt":{"func":"g0.data.test.p","args":[{"type":"local","value":0},{"type":"local","value":1}],"result":3,"file":0,"col":0,"row":0,"end_col":0,"end_row":0}},{"type":"ObjectInsertStmt","stmt":{"key":{"type":"string_index","value":1},"value":{"type":"local","value":3},"object":2,"file":0,"col":0,"row":0,"end_col":0,"end_row":0}}]}],"file":0,"col":0,"row":0,"end_col":0,"end_row":0}},{"type":"BlockStmt","stmt":{"blocks":[{"stmts":[{"type":"BlockStmt","stmt":{"blocks":[{"stmts":[{"type":"DotStmt","stmt":{"source":{"type":"local","value":1},"key":{"type":"string_index","value":3},"target":5,"file":0,"col":0,"row":0,"end_col":0,"end_row":0}},{"type":"ObjectMergeStmt","stmt":{"a":5,"b":2,"target":4,"file":0,"col":0,"row":0,"end_col":0,"end_row":0}},{"type":"BreakStmt","stmt":{"index":1,"file":0,"col":0,"row":0,"end_col":0,"end_row":0}}]}],"file":0,"col":0,"row":0,"end_col":0,"end_row":0}},{"type":"AssignVarStmt","stmt":{"source":{"type":"local","value":2},"target":4,"file":0,"col":0,"row":0,"end_col":0,"end_row":0}}]}],"file":0,"col":0,"row":0,"end_col":0,"end_row":0}},{"type":"AssignVarStmt","stmt":{"source":{"type":"local","value":4},"target":6,"file":0,"col":0,"row":0,"end_col":0,"end_row":0}},{"type":"MakeObjectStmt","stmt":{"target":7,"file":0,"col":0,"row":0,"end_col":0,"end_row":0}},{"type":"ObjectInsertStmt","stmt":{"key":{"type":"string_index","value":0},"value":{"type":"local","value":6},"object":7,"file":0,"col":0,"row":0,"end_col":0,"end_row":0}},{"type":"ResultSetAddStmt","stmt":{"value":7,"file":0,"col":0,"row":0,"end_col":0,"end_row":0}}]}]}]},"funcs":{"funcs":[{"name":"g0.data.test.p","params":[0,1],"return":2,"blocks":[{"stmts":[{"type":"ResetLocalStmt","stmt":{"target":3,"file":0,"col":4,"row":3,"end_col":9,"end_row":3}},{"type":"MakeNumberRefStmt","stmt":{"file":0,"col":4,"row":3,"end_col":9,"end_row":3,"index":2,"Index":2,"target":4}},{"type":"AssignVarOnceStmt","stmt":{"source":{"type":"local","value":4},"target":3,"file":0,"col":4,"row":3,"end_col":9,"end_row":3}}]},{"stmts":[{"type":"IsDefinedStmt","stmt":{"source":3,"file":0,"col":4,"row":3,"end_col":9,"end_row":3}},{"type":"AssignVarOnceStmt","stmt":{"source":{"type":"local","value":3},"target":2,"file":0,"col":4,"row":3,"end_col":9,"end_row":3}}]},{"stmts":[{"type":"ReturnLocalStmt","stmt":{"source":2,"file":0,"col":4,"row":3,"end_col":9,"end_row":3}}]}],"path":["g0","test","p"]}]}}`

if diff := cmp.Diff(expected, got); diff != "" {
t.Errorf("unexpected result (-want, +got):\n%s", diff)
Expand Down
1 change: 1 addition & 0 deletions e2e/proto/plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ func TestPlanProtoRoundTripYAMLSuite(t *testing.T) {
func roundTripCmpOpts() []cmp.Option {
return []cmp.Option{
cmpopts.IgnoreUnexported(ir.Location{}),
cmpopts.IgnoreFields(ir.Location{}, "Text"),
cmpopts.IgnoreFields(ir.BuiltinFunc{}, "Decl"),
cmpopts.EquateEmpty(),
}
Expand Down
4 changes: 3 additions & 1 deletion internal/cmd/genplanschema/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,8 @@ func makeNumberRefStmtSchema() genjsonschema.OrderedMap {
"col", genjsonschema.Map("type", "integer"),
"file", genjsonschema.Map("type", "integer"),
"row", genjsonschema.Map("type", "integer"),
"end_col", genjsonschema.Map("type", "integer"),
"end_row", genjsonschema.Map("type", "integer"),
"index", genjsonschema.Map("type", "integer"),
"Index", genjsonschema.Map(
"type", "integer",
Expand All @@ -223,7 +225,7 @@ func makeNumberRefStmtSchema() genjsonschema.OrderedMap {
),
"target", genjsonschema.Map("type", "integer"),
),
"required", []string{"col", "file", "index", "row", "target"},
"required", []string{"col", "end_col", "end_row", "file", "index", "row", "target"},
"additionalProperties", false,
)
}
Expand Down
2 changes: 1 addition & 1 deletion internal/planner/planner.go
Original file line number Diff line number Diff line change
Expand Up @@ -2489,7 +2489,7 @@ func (p *Planner) appendStmtToBlock(s ir.Stmt, b *ir.Block) {
if str == "" {
str = `<query>`
}
s.SetLocation(p.getFileConst(str), p.loc.Row, p.loc.Col, str, string(p.loc.Text))
s.SetLocation(p.getFileConst(str), p.loc.Row, p.loc.Col, str, p.loc.Text)
}
b.Stmts = append(b.Stmts, s)
}
Expand Down
3 changes: 1 addition & 2 deletions internal/planner/planner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -474,9 +474,8 @@ func getLocation(x any) string {
v := reflect.ValueOf(x).Elem().FieldByName("Location")
li := v.Interface()
file := v.FieldByName("file").String()
text := v.FieldByName("text").String()
if loc, ok := li.(ir.Location); ok {
return fmt.Sprintf("%s:%d:%d: %s", file, loc.Row, loc.Col, text)
return fmt.Sprintf("%s:%d:%d: %s", file, loc.Row, loc.Col, loc.Text)
}
return "unknown"
}
Expand Down
28 changes: 15 additions & 13 deletions v1/ast/location/location.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,29 +97,31 @@ func (loc *Location) HasFile() bool {
return loc != nil && loc.File != ""
}

// End returns the (row, col) one past the last rune of loc.Text — an
// exclusive end matching the scanner's offset calculation, so [Start, End)
// covers the text. Columns are counted per rune. Returns (Row, Col) for
// empty text and (0, 0) for a nil receiver.
// End determines the end position of loc.
func (loc *Location) End() (row, col int) {
if loc == nil {
return 0, 0
}
return EndOf(loc.Row, loc.Col, loc.Text)
}

if len(loc.Text) == 0 {
return loc.Row, loc.Col
// EndOf returns the end (row, col) position reached by starting at (row, col)
// and advancing through text.
func EndOf(row, col int, text []byte) (endRow, endCol int) {
if len(text) == 0 {
return row, col
}

row = loc.Row + bytes.Count(loc.Text, []byte{'\n'})
col = loc.Col
endRow = row + bytes.Count(text, []byte{'\n'})
endCol = col

lastLine := loc.Text
if row != loc.Row {
col = 1
lastLine = loc.Text[bytes.LastIndex(loc.Text, []byte{'\n'})+1:]
lastLine := text
if endRow != row {
endCol = 1
lastLine = text[bytes.LastIndex(text, []byte{'\n'})+1:]
}

return row, col + utf8.RuneCount(lastLine)
return endRow, endCol + utf8.RuneCount(lastLine)
}

// Compare returns -1, 0, or 1 to indicate if this loc is less than, equal to,
Expand Down
49 changes: 39 additions & 10 deletions v1/ast/location/location_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,39 +243,68 @@ func TestLocationHasFile(t *testing.T) {
}
}

func TestLocationEnd(t *testing.T) {
func TestEndOf(t *testing.T) {
tests := map[string]struct {
loc *Location
expRow int
expCol int
row, col int
text []byte
expRow int
expCol int
}{
"single-line text": {
loc: &Location{Text: []byte("false"), Row: 3, Col: 10},
text: []byte("false"), row: 3, col: 10,
expRow: 3,
expCol: 15,
},
"multi-line text": {
loc: &Location{Text: []byte("a\nbc"), Row: 5, Col: 2},
text: []byte("a\nbc"), row: 5, col: 2,
expRow: 6,
expCol: 3,
},
"multi-byte runes count as one column each": {
// "café" is 5 bytes but 4 runes; the scanner advances Col
// per rune (see scanner.next), so End must too.
loc: &Location{Text: []byte("café"), Row: 1, Col: 1},
// per rune (see scanner.next), so EndOf must too.
text: []byte("café"), row: 1, col: 1,
expRow: 1,
expCol: 5,
},
"multi-byte runes across a newline": {
loc: &Location{Text: []byte("café\nñ"), Row: 1, Col: 1},
text: []byte("café\nñ"), row: 1, col: 1,
expRow: 2,
expCol: 2,
},
"single multi-byte rune": {
loc: &Location{Text: []byte("é"), Row: 1, Col: 1},
text: []byte("é"), row: 1, col: 1,
expRow: 1,
expCol: 2,
},
"empty text": {
text: nil, row: 4, col: 7,
expRow: 4,
expCol: 7,
},
}

for name, tc := range tests {
t.Run(name, func(t *testing.T) {
row, col := EndOf(tc.row, tc.col, tc.text)
if row != tc.expRow || col != tc.expCol {
t.Fatalf("Expected (%d, %d) but got (%d, %d)", tc.expRow, tc.expCol, row, col)
}
})
}
}

func TestLocationEnd(t *testing.T) {
tests := map[string]struct {
loc *Location
expRow int
expCol int
}{
"delegates to EndOf": {
loc: &Location{Text: []byte("a\nbc"), Row: 5, Col: 2},
expRow: 6,
expCol: 3,
},
"nil receiver": {
loc: nil,
expRow: 0,
Expand Down
2 changes: 1 addition & 1 deletion v1/bundle/v1pb/manifest.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions v1/format/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (

"github.com/open-policy-agent/opa/internal/future"
"github.com/open-policy-agent/opa/v1/ast"
"github.com/open-policy-agent/opa/v1/ast/location"
"github.com/open-policy-agent/opa/v1/types"
"github.com/open-policy-agent/opa/v1/util"
)
Expand Down Expand Up @@ -668,7 +669,7 @@ func (w *writer) writeRule(rule *ast.Rule, isElse bool, comments []*ast.Comment)
// Additionally, a single set term must not be stripped of the outer body
// braces, as that would semantically change the inner set to a body:
// `p if { { x } }` -> p if { x }
headEndRow := rule.Head.Location.Row + strings.Count(string(rule.Head.Location.Text), "\n")
headEndRow, _ := location.EndOf(rule.Head.Location.Row, rule.Head.Location.Col, rule.Head.Location.Text)
if rule.Body[0].Location.Row == headEndRow && !isSetTerm(rule.Body[0]) {
w.write(" ")
var err error
Expand Down Expand Up @@ -1056,7 +1057,8 @@ func exprTermsEndRow(expr *ast.Expr) int {
}
}
text = bytes.TrimRight(text, " \t\r\n")
return loc.Row + bytes.Count(text, []byte{'\n'})
endRow, _ := location.EndOf(loc.Row, loc.Col, text)
return endRow
}

// isSetTerm reports whether expr is a non-negated set term.
Expand Down Expand Up @@ -1343,7 +1345,7 @@ func (w *writer) writeTerm(term *ast.Term, comments []*ast.Comment) ([]*ast.Comm
// If beforeEnd refers to a comment within the source text range, clear it
// This prevents the comment from being written twice
if w.beforeEnd != nil && len(term.Location.Text) > 0 {
endRow := term.Location.Row + bytes.Count(term.Location.Text, []byte{'\n'})
endRow, _ := location.EndOf(term.Location.Row, term.Location.Col, term.Location.Text)
if w.beforeEnd.Location.Row >= term.Location.Row && w.beforeEnd.Location.Row <= endRow {
w.beforeEnd = nil
}
Expand Down
39 changes: 32 additions & 7 deletions v1/ir/ir.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ package ir
import (
"fmt"

"github.com/open-policy-agent/opa/v1/ast/location"
"github.com/open-policy-agent/opa/v1/types"
)

Expand Down Expand Up @@ -84,7 +85,7 @@ type (
}

locationStmt interface {
SetLocation(index, row, col int, file, text string)
SetLocation(index, row, col int, file string, text []byte)
GetLocation() *Location
}

Expand Down Expand Up @@ -467,21 +468,45 @@ type ResultSetAddStmt struct {
// Location records the filen index, and the row and column inside that file
// that a statement can be connected to.
type Location struct {
File int `json:"file"` // filename string constant index
Col int `json:"col"`
Row int `json:"row"`
file, text string // only used for debugging
File int `json:"file"` // filename string constant index
Col int `json:"col"`
Row int `json:"row"`
EndCol int `json:"end_col"`
EndRow int `json:"end_row"`

// Text is only used for location ranges and debug prints.
// A named type is used so that its String method is called during printing.
// String cannot be set on Location since it is embedded and impacts parent
// structs if registered here.
Text locationText `json:"-"`

file string // only used for debugging
}

type locationText []byte

func (d locationText) String() string {
return string(d)
}

// SetLocation sets the Location for a given Stmt.
func (l *Location) SetLocation(index, row, col int, file, text string) {
func (l *Location) SetLocation(index, row, col int, file string, text []byte) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a breaking change, but it seemed acceptable given:

  • IR plan integration point is intended to be not go.
  • text []byte is a simple change for any callers
  • text in location is []byte

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switching text to []byte changes what Pretty prints, pretty.go dumps the struct with %+v, and Go renders a []byte as numbers rather than text.

A String() method on ir.Location would fix it without giving up the []byte change, since %+v picks it up automatically.

@charlieegan3 charlieegan3 Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, interesting... I have added 26ade847c to address. it's not ideal but adding String() on the location doesn't work since it's embedded into Stmt and so String set there risks only showing the Location string representation.

*l = Location{
File: index,
Row: row,
Col: col,
Text: text,

file: file,
text: text,
}

l.EndRow, l.EndCol = location.EndOf(row, col, l.Text)
}

// End returns the end row and col of the location range, expected to be called
// after SetLocation or unmarshalling.
func (l *Location) End() (row, col int) {
return l.EndRow, l.EndCol
}

// GetLocation returns a Stmt's Location.
Expand Down
Loading
Loading