Skip to content

Commit 3384f3d

Browse files
committed
syntax: backquotes affect backslashes in single quotes too
I incorrectly understood that backquote command substitutions did not affect backslashes in single quotes. For example, $ echo `echo "1.2" | sed -e 's/\./\\\\./g'` 1\.2 would be formatted by shfmt as the following, with different output: $ echo $(echo "1.2" | sed -e 's/\./\\\\./g') 1\\.2 when in fact we do want to deduplicate the backslashes: $ echo $(echo "1.2" | sed -e 's/\./\\./g') 1\.2 Tweak the existing test which expected the wrong result. Thankfully, we simplify the code now with the fix. Fixes mvdan#1041.
1 parent 28117db commit 3384f3d

File tree

3 files changed

+10
-17
lines changed

3 files changed

+10
-17
lines changed

syntax/filetests_test.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1797,7 +1797,7 @@ var fileTests = []testCase{
17971797
{
17981798
Strs: []string{
17991799
`$(echo '\' 'a\b' "\\" "a\a")`,
1800-
"`" + `echo '\' 'a\b' "\\\\" "a\a"` + "`",
1800+
"`" + `echo '\' 'a\\b' "\\\\" "a\a"` + "`",
18011801
},
18021802
common: cmdSubst(stmt(call(
18031803
litWord("echo"),
@@ -4589,7 +4589,7 @@ func recursiveSanityCheck(tb testing.TB, src string, v any) {
45894589
return
45904590
}
45914591
}
4592-
tb.Errorf("Expected one of %q at %d in %q, found %q",
4592+
tb.Errorf("Expected one of %q at %s in %q, found %q",
45934593
strs, pos, src, gotErr)
45944594
}
45954595
checkNodePosEnd := func(n Node) {
@@ -4701,7 +4701,7 @@ func recursiveSanityCheck(tb testing.TB, src string, v any) {
47014701
tb.Errorf("Lit without newlines has Pos/End lines %d and %d",
47024702
posLine, endLine)
47034703
case strings.Contains(src, "`") && strings.Contains(src, "\\"):
4704-
// removed quotes inside backquote cmd substs
4704+
// removed backslashes inside backquote cmd substs
47054705
val = ""
47064706
case end < len(src) && (src[end] == '\n' || src[end] == '`'):
47074707
// heredoc literals that end with the
@@ -4794,7 +4794,12 @@ func recursiveSanityCheck(tb testing.TB, src string, v any) {
47944794
if x.Dollar {
47954795
valuePos = posAddCol(valuePos, 1)
47964796
}
4797-
checkPos(valuePos, x.Value)
4797+
val := x.Value
4798+
if strings.Contains(src, "`") && strings.Contains(src, "\\") {
4799+
// removed backslashes inside backquote cmd substs
4800+
val = ""
4801+
}
4802+
checkPos(valuePos, val)
47984803
if x.Dollar {
47994804
checkPos(x.Left, "$'")
48004805
} else {

syntax/lexer.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -412,11 +412,6 @@ func (p *Parser) peekByte(b byte) bool {
412412
func (p *Parser) regToken(r rune) token {
413413
switch r {
414414
case '\'':
415-
if p.openBquotes > 0 {
416-
// bury openBquotes
417-
p.buriedBquotes = p.openBquotes
418-
p.openBquotes = 0
419-
}
420415
p.rune()
421416
return sglQuote
422417
case '"':

syntax/parser.go

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -389,9 +389,6 @@ type Parser struct {
389389

390390
// lastBquoteEsc is how many times the last backquote token was escaped
391391
lastBquoteEsc int
392-
// buriedBquotes is like openBquotes, but saved for when the parser
393-
// comes out of single quotes
394-
buriedBquotes int
395392

396393
rxOpenParens int
397394
rxFirstPart bool
@@ -435,7 +432,7 @@ func (p *Parser) reset() {
435432
p.openStmts = 0
436433
p.heredocs, p.buriedHdocs = p.heredocs[:0], 0
437434
p.parsingDoc = false
438-
p.openBquotes, p.buriedBquotes = 0, 0
435+
p.openBquotes = 0
439436
p.accComs, p.curComs = nil, &p.accComs
440437
p.litBatch = nil
441438
p.wordBatch = nil
@@ -1124,10 +1121,6 @@ func (p *Parser) wordPart() WordPart {
11241121
sq.Right = p.nextPos()
11251122
sq.Value = p.endLit()
11261123

1127-
// restore openBquotes
1128-
p.openBquotes = p.buriedBquotes
1129-
p.buriedBquotes = 0
1130-
11311124
p.rune()
11321125
p.next()
11331126
return sq

0 commit comments

Comments
 (0)