chainlint: recognize multi-line quoted strings more robustly

chainlint.sed recognizes multi-line quoted strings within subshells: echo "abc def" >out && so it can avoid incorrectly classifying lines internal to the string as breaking the &&-chain. To identify the first line of a multi-line string, it checks if the line contains a single quote. However, this is fragile and can be easily fooled by a line containing multiple strings: echo "xyz" "abc def" >out && Make detection more robust by checking for an odd number of quotes rather than only a single one. (Escaped quotes are not handled, but support may be added later.) The original multi-line string recognizer rather cavalierly threw away all but the final quote, whereas the new one is careful to retain all quotes, so the "expected" output of a couple existing chainlint tests is updated to account for this new behavior. Signed-off-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Eric Sunshine committed Aug 13, 2018 at 04:47 UTC 22e3e0241ab5add065411d0d8d493f066764465e
4 files changed +43 -13
t/chainlint.sed
+22 -10
@@ -151,10 +151,10 @@ s/.*\n//
151 :slurp
152 # incomplete line "...\"
153 /\\$/bincomplete
154 -# multi-line quoted string "...\n..."
155 -/^[^"]*"[^"]*$/bdqstring
156 -# multi-line quoted string '...\n...' (but not contraction in string "it's so")
157 -/^[^']*'[^']*$/{
154 +# multi-line quoted string "...\n..."?
155 +/"/bdqstring
156 +# multi-line quoted string '...\n...'? (but not contraction in string "it's")
157 +/'/{
158 /"[^'"]*'[^'"]*"/!bsqstring
159 }
160 :folded
@@ -250,20 +250,32 @@ N
250 s/\\\n//
251 bslurp
252
253 -# found multi-line double-quoted string "...\n..." -- slurp until end of string
253 +# check for multi-line double-quoted string "...\n..." -- fold to one line
254 :dqstring
255 -s/"//g
255 +# remove all quote pairs
256 +s/"\([^"]*\)"/@!\1@!/g
257 +# done if no dangling quote
258 +/"/!bdqdone
259 +# otherwise, slurp next line and try again
260 N
261 s/\n//
258 -/"/!bdqstring
262 +bdqstring
263 +:dqdone
264 +s/@!/"/g
265 bfolded
266
261 -# found multi-line single-quoted string '...\n...' -- slurp until end of string
267 +# check for multi-line single-quoted string '...\n...' -- fold to one line
268 :sqstring
263 -s/'//g
269 +# remove all quote pairs
270 +s/'\([^']*\)'/@!\1@!/g
271 +# done if no dangling quote
272 +/'/!bsqdone
273 +# otherwise, slurp next line and try again
274 N
275 s/\n//
266 -/'/!bsqstring
276 +bsqstring
277 +:sqdone
278 +s/@!/'/g
279 bfolded
280
281 # found here-doc -- swallow it to avoid false hits within its body (but keep
t/chainlint/here-doc-multi-line-string.expect
+1 -1
@@ -1,4 +1,4 @@
1 (
2 -?!AMP?! cat && echo multi-line string"
2 +?!AMP?! cat && echo "multi-line string"
3 bap
4 >)
t/chainlint/multi-line-string.expect
+8 -2
@@ -1,9 +1,15 @@
1 (
2 - x=line 1 line 2 line 3" &&
3 -?!AMP?! y=line 1 line2'
2 + x="line 1 line 2 line 3" &&
3 +?!AMP?! y='line 1 line2'
4 foobar
5 >) &&
6 (
7 echo "there's nothing to see here" &&
8 exit
9 +>) &&
10 +(
11 + echo "xyz" "abc def ghi" &&
12 + echo 'xyz' 'abc def ghi' &&
13 + echo 'xyz' "abc def ghi" &&
14 + barfoo
15 >)
t/chainlint/multi-line-string.test
+12
@@ -12,4 +12,16 @@
12 # LINT: starting multi-line single-quoted string
13 echo "there's nothing to see here" &&
14 exit
15 +) &&
16 +(
17 + echo "xyz" "abc
18 + def
19 + ghi" &&
20 + echo 'xyz' 'abc
21 + def
22 + ghi' &&
23 + echo 'xyz' "abc
24 + def
25 + ghi" &&
26 + barfoo
27 )