go/analysis/passes/modernize: embedlit invalid fix
One of the text edits that the embedlit modernizer
produces is to delete the key value expression and
opening brace in a redundant field type specifier:
T{U: U{f: v, ...}}
-----
If the kv expression is on its own line, we need
to delete the whole line to avoid leaving white
space:
T{
U: U{
f: v,
}
}
Previously, we deleted the whole line after checking
that the previous element is on a different line
(prevLine < curLine) and the closing brace of the
inner literal is on a different line than its
opening brace. However, this did not take
into account the location of the inner elements.
If the first inner field assignment starts
on the same line as the kv expression, deleting
the entire line produces
invalid code:
B: B{b: 1
}
We should verify that the first inner element
starts on a subsequent line before we delete
the entire line.
Fixes golang/go#80197
Change-Id: Ia6623c8dc329e46626677c2b8157c58e2ee81d32
Reviewed-on: https://go-review.googlesource.com/c/tools/+/795441
Auto-Submit: Madeline Kalil <mkalil@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/go/analysis/passes/modernize/embedlit.go b/go/analysis/passes/modernize/embedlit.go
index da883d6..d2c71b5 100644
--- a/go/analysis/passes/modernize/embedlit.go
+++ b/go/analysis/passes/modernize/embedlit.go
@@ -84,14 +84,12 @@
// Can't promote an unkeyed field; would result in a syntax error.
if kv, ok := elt.(*ast.KeyValueExpr); ok {
if innerLit := isEmbeddedFieldLit(info, compLitType, kv); innerLit != nil {
+ // Inv: len(innerLit.Elts) > 0. We skip empty struct literals.
// Emit edits to delete the unnecessary embedded field type specifier
// and its closing brace.
- closingPos := innerLit.Rbrace
- if len(innerLit.Elts) > 0 {
- // Delete any inner trailing commas or white space. Extra trailing commas
- // would result in invalid code.
- closingPos = innerLit.Elts[len(innerLit.Elts)-1].End()
- }
+ // Delete any inner trailing commas or white space. Extra trailing commas
+ // would result in invalid code.
+ closingPos := innerLit.Elts[len(innerLit.Elts)-1].End()
file := astutil.EnclosingFile(curLit)
// Enable modernizer only for Go1.27.
if !analyzerutil.FileUsesGoVersion(pass, file, versions.Go1_27) {
@@ -132,11 +130,11 @@
}
// We can safely delete the entire line if the key value expression is
- // on a different line than the previous element, and the closing
- // brace of the inner literal is on a different line than its opening
- // brace.
+ // alone on its line: it starts on a new line relative to the previous
+ // element (prevLine < curLine), and the first element of the inner
+ // literal starts on a subsequent line.
if prevLine < curLine && curLine < tokFile.LineCount() && // (1-based)
- lineOf(innerLit.Lbrace) < lineOf(innerLit.Rbrace) {
+ lineOf(innerLit.Elts[0].Pos()) > curLine {
lineStart := tokFile.LineStart(curLine)
nextLineStart := tokFile.LineStart(curLine + 1)
// Check that there are no comments on the line we are going to delete.
diff --git a/go/analysis/passes/modernize/testdata/src/embedlit/embedlit_go127.go b/go/analysis/passes/modernize/testdata/src/embedlit/embedlit_go127.go
index 0b97443..39300a1 100644
--- a/go/analysis/passes/modernize/testdata/src/embedlit/embedlit_go127.go
+++ b/go/analysis/passes/modernize/testdata/src/embedlit/embedlit_go127.go
@@ -135,6 +135,12 @@
},
},
}
+
+ _ = E{ // want "embedded field type can be removed from struct literal"
+ e: 2,
+ F: F{f: 1,
+ },
+ }
)
func _() {
diff --git a/go/analysis/passes/modernize/testdata/src/embedlit/embedlit_go127.go.golden b/go/analysis/passes/modernize/testdata/src/embedlit/embedlit_go127.go.golden
index 4bad37a..69fe4ab 100644
--- a/go/analysis/passes/modernize/testdata/src/embedlit/embedlit_go127.go.golden
+++ b/go/analysis/passes/modernize/testdata/src/embedlit/embedlit_go127.go.golden
@@ -123,6 +123,11 @@
// comment here
c: 1,
}
+
+ _ = E{ // want "embedded field type can be removed from struct literal"
+ e: 2,
+ f: 1,
+ }
)
func _() {