Fix shortcode parser regression with quoted param values

This issue was introduced in `v0.102.0`.

In 223bf28004 we removed the byte source from the parsed page result, which
meant we had to preserve exact positioning for all elements. This introduced some new `TypeIgnore` tokens
which we, wrongly, assumed didn't matter where we put in the result slice (they should be ignored anyway).

But it seems that this broke the logic where we determine if it's positional or named params in the case
where the paramater value contains escaped quoutes.

This commit makes sure that these ignore tokens (the back slashes) are never sent back to the client, which is how it was before `v0.102.0`.

This commit also fixes some lost error information in that same commit.

Fixes #10236
This commit is contained in:
Bjørn Erik Pedersen 2022-09-01 09:26:27 +02:00
parent 5046a6c7ca
commit 8e5044d7f5
5 changed files with 43 additions and 11 deletions

View file

@ -39,8 +39,6 @@ import (
"github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/parser/metadecoders"
"errors"
"github.com/gohugoio/hugo/parser/pageparser"
"github.com/gohugoio/hugo/output"
@ -762,7 +760,7 @@ Loop:
case it.IsEOF():
break Loop
case it.IsError():
err := fail(errors.New(it.ValStr(result.Input())), it)
err := fail(it.Err, it)
currShortcode.err = err
return err

View file

@ -497,8 +497,6 @@ func (s *shortcodeHandler) renderShortcodesForPage(p *pageState, f output.Format
return rendered, hasVariants, nil
}
var errShortCodeIllegalState = errors.New("Illegal shortcode state")
func (s *shortcodeHandler) parseError(err error, input []byte, pos int) error {
if s.p != nil {
return s.p.parseError(err, input, pos)
@ -640,7 +638,7 @@ Loop:
if params, ok := sc.params.(map[string]any); ok {
params[currItem.ValStr(source)] = pt.Next().ValTyped(source)
} else {
return sc, errShortCodeIllegalState
return sc, fmt.Errorf("%s: invalid state: invalid param type %T for shortcode %q, expected a map", errorPrefix, params, sc.name)
}
}
} else {
@ -654,7 +652,7 @@ Loop:
params = append(params, currItem.ValTyped(source))
sc.params = params
} else {
return sc, errShortCodeIllegalState
return sc, fmt.Errorf("%s: invalid state: invalid param type %T for shortcode %q, expected a slice", errorPrefix, params, sc.name)
}
}
}

View file

@ -1055,3 +1055,35 @@ title: "p1"
`)
}
// Issue 10236.
func TestShortcodeParamEscapedQuote(t *testing.T) {
t.Parallel()
files := `
-- config.toml --
-- content/p1.md --
---
title: "p1"
---
{{< figure src="/media/spf13.jpg" title="Steve \"Francia\"." >}}
-- layouts/shortcodes/figure.html --
Title: {{ .Get "title" | safeHTML }}
-- layouts/_default/single.html --
{{ .Content }}
`
b := NewIntegrationTestBuilder(
IntegrationTestConfig{
T: t,
TxtarString: files,
Running: true,
Verbose: true,
},
).Build()
b.AssertFileContent("public/p1/index.html", `Title: Steve "Francia".`)
}

View file

@ -194,7 +194,12 @@ func (l *pageLexer) ignoreEscapesAndEmit(t ItemType, isString bool) {
if i > k {
segments = append(segments, lowHigh{k, i})
}
l.append(Item{Type: TypeIgnore, low: i, high: i + w})
// See issue #10236.
// We don't send the backslash back to the client,
// which makes the end parsing simpler.
// This means that we cannot render the AST back to be
// exactly the same as the input,
// but that was also the situation before we introduced the issue in #10236.
k = i + w
}
i += w

View file

@ -40,7 +40,6 @@ var (
tstParamFloat = nti(tScParam, "3.14")
tstVal = nti(tScParamVal, "Hello World")
tstText = nti(tText, "Hello World")
tstIgnoreEscape = nti(TypeIgnore, "\\")
)
var shortCodeLexerTests = []lexerTest{
@ -179,14 +178,14 @@ var shortCodeLexerTests = []lexerTest{
"escaped quotes inside nonescaped quotes",
`{{< sc1 param1="Hello \"escaped\" World" >}}`,
[]typeText{
tstLeftNoMD, tstSC1, tstParam1, tstIgnoreEscape, tstIgnoreEscape, nti(tScParamVal, `Hello "escaped" World`), tstRightNoMD, tstEOF,
tstLeftNoMD, tstSC1, tstParam1, nti(tScParamVal, `Hello "escaped" World`), tstRightNoMD, tstEOF,
},
},
{
"escaped quotes inside nonescaped quotes in positional param",
`{{< sc1 "Hello \"escaped\" World" >}}`,
[]typeText{
tstLeftNoMD, tstSC1, tstIgnoreEscape, tstIgnoreEscape, nti(tScParam, `Hello "escaped" World`), tstRightNoMD, tstEOF,
tstLeftNoMD, tstSC1, nti(tScParam, `Hello "escaped" World`), tstRightNoMD, tstEOF,
},
},
{"escaped raw string, named param", `{{< sc1 param1=` + `\` + "`" + "Hello World" + `\` + "`" + ` >}}`, []typeText{