Skip to content

Commit d73f5cf

Browse files
sunshinecogitster
authored andcommitted
chainlint.sed: stop splitting "(..." into separate lines "(" and "..."
Because `sed` is line-oriented, for ease of implementation, when chainlint.sed encounters an opening subshell in which the first command is cuddled with the "(", it splits the line into two lines: one containing only "(", and the other containing whatever follows "(". This allows chainlint.sed to get by with a single set of regular expressions for matching shell statements rather than having to duplicate each expression (one set for matching non-cuddled statements, and one set for matching cuddled statements). However, although syntactically and semantically immaterial, this transformation has no value to test authors and might even confuse them into thinking that the linter is misbehaving by inserting (whitespace) line-noise into the shell code it is validating. Moreover, it also allows an implementation detail of chainlint.sed to seep into the chainlint self-test "expect" files, which potentially makes it difficult to reuse the self-tests should a more capable chainlint ever be developed. To address these concerns, stop splitting cuddled "(..." into two lines. Note that, as an implementation artifact, due to sed's line-oriented nature, this change inserts a blank line at output time just before the "(..." line is emitted. It would be possible to suppress this blank line but doing so would add a fair bit of complexity to chainlint.sed. Therefore, rather than suppressing the extra blank line, the Makefile's `check-chainlint` target which runs the chainlint self-tests is instead modified to ignore blank lines when comparing chainlint output against the self-test "expect" output. This is a reasonable compromise for two reasons. First, the purpose of the chainlint self-tests is to verify that the ?!AMP?! annotations are being correctly added; precise whitespace is immaterial. Second, by necessity, chainlint.sed itself already throws away all blank lines within subshells since, when checking for a broken &&-chain, it needs to check the final _statement_ in a subshell, not the final _line_ (which might be blank), thus it has never made any attempt to precisely reproduce blank lines in its output. Signed-off-by: Eric Sunshine <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 31da22d commit d73f5cf

9 files changed

+37
-35
lines changed

t/Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@ clean-chainlint:
7272
check-chainlint:
7373
@mkdir -p '$(CHAINLINTTMP_SQ)' && \
7474
sed -e '/^# LINT: /d' $(patsubst %,chainlint/%.test,$(CHAINLINTTESTS)) >'$(CHAINLINTTMP_SQ)'/tests && \
75-
cat $(patsubst %,chainlint/%.expect,$(CHAINLINTTESTS)) >'$(CHAINLINTTMP_SQ)'/expect && \
76-
$(CHAINLINT) '$(CHAINLINTTMP_SQ)'/tests >'$(CHAINLINTTMP_SQ)'/actual && \
75+
sed -e '/^[ ]*$$/d' $(patsubst %,chainlint/%.expect,$(CHAINLINTTESTS)) >'$(CHAINLINTTMP_SQ)'/expect && \
76+
$(CHAINLINT) '$(CHAINLINTTMP_SQ)'/tests | grep -v '^[ ]*$$' >'$(CHAINLINTTMP_SQ)'/actual && \
7777
diff -u '$(CHAINLINTTMP_SQ)'/expect '$(CHAINLINTTMP_SQ)'/actual
7878

7979
test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax \

t/chainlint.sed

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -131,11 +131,15 @@ b
131131
h
132132
bnextln
133133
}
134-
# "(..." line -- split off and stash "(", then process "..." as its own line
134+
# "(..." line -- "(" opening subshell cuddled with command; temporarily replace
135+
# "(" with sentinel "^" and process the line as if "(" had been seen solo on
136+
# the preceding line; this temporary replacement prevents several rules from
137+
# accidentally thinking "(" introduces a nested subshell; "^" is changed back
138+
# to "(" at output time
135139
x
136-
s/.*/(/
140+
s/.*//
137141
x
138-
s/(//
142+
s/(/^/
139143
bslurp
140144

141145
:nextln
@@ -168,12 +172,12 @@ s/.*\n//
168172
/"[^"]*#[^"]*"/!s/[ ]#.*$//
169173
}
170174
# one-liner "case ... esac"
171-
/^[ ]*case[ ]*..*esac/bchkchn
175+
/^[ ^]*case[ ]*..*esac/bchkchn
172176
# multi-line "case ... esac"
173-
/^[ ]*case[ ]..*[ ]in/bcase
177+
/^[ ^]*case[ ]..*[ ]in/bcase
174178
# multi-line "for ... done" or "while ... done"
175-
/^[ ]*for[ ]..*[ ]in/bcont
176-
/^[ ]*while[ ]/bcont
179+
/^[ ^]*for[ ]..*[ ]in/bcont
180+
/^[ ^]*while[ ]/bcont
177181
/^[ ]*do[ ]/bcont
178182
/^[ ]*do[ ]*$/bcont
179183
/;[ ]*do/bcont
@@ -184,7 +188,7 @@ s/.*\n//
184188
/||[ ]*exit[ ]/bcont
185189
/||[ ]*exit[ ]*$/bcont
186190
# multi-line "if...elsif...else...fi"
187-
/^[ ]*if[ ]/bcont
191+
/^[ ^]*if[ ]/bcont
188192
/^[ ]*then[ ]/bcont
189193
/^[ ]*then[ ]*$/bcont
190194
/;[ ]*then/bcont
@@ -197,15 +201,15 @@ s/.*\n//
197201
/^[ ]*fi[ ]*[<>|]/bdone
198202
/^[ ]*fi[ ]*)/bdone
199203
# nested one-liner "(...) &&"
200-
/^[ ]*(.*)[ ]*&&[ ]*$/bchkchn
204+
/^[ ^]*(.*)[ ]*&&[ ]*$/bchkchn
201205
# nested one-liner "(...)"
202-
/^[ ]*(.*)[ ]*$/bchkchn
206+
/^[ ^]*(.*)[ ]*$/bchkchn
203207
# nested one-liner "(...) >x" (or "2>x" or "<x" or "|x")
204-
/^[ ]*(.*)[ ]*[0-9]*[<>|]/bchkchn
208+
/^[ ^]*(.*)[ ]*[0-9]*[<>|]/bchkchn
205209
# nested multi-line "(...\n...)"
206-
/^[ ]*(/bnest
210+
/^[ ^]*(/bnest
207211
# multi-line "{...\n...}"
208-
/^[ ]*{/bblock
212+
/^[ ^]*{/bblock
209213
# closing ")" on own line -- exit subshell
210214
/^[ ]*)/bclssolo
211215
# "$((...))" -- arithmetic expansion; not closing ")"
@@ -237,6 +241,7 @@ s/.*\n//
237241
:cont
238242
# retrieve and print previous line
239243
x
244+
s/^\([ ]*\)^/\1(/
240245
s/?!HERE?!/<</g
241246
n
242247
bslurp
@@ -292,6 +297,7 @@ bfolded
292297
# found "case ... in" -- pass through untouched
293298
:case
294299
x
300+
s/^\([ ]*\)^/\1(/
295301
s/?!HERE?!/<</g
296302
n
297303
:cascom
@@ -326,6 +332,7 @@ bchkchn
326332
:nest
327333
x
328334
:nstslrp
335+
s/^\([ ]*\)^/\1(/
329336
s/?!HERE?!/<</g
330337
n
331338
:nstcom
@@ -354,6 +361,7 @@ bchkchn
354361
# found multi-line "{...\n...}" block -- pass through untouched
355362
:block
356363
x
364+
s/^\([ ]*\)^/\1(/
357365
s/?!HERE?!/<</g
358366
n
359367
:blkcom
@@ -371,17 +379,21 @@ bblock
371379
:clssolo
372380
x
373381
s/\( ?!AMP?!\)* ?!AMP?!$//
382+
s/^\([ ]*\)^/\1(/
374383
s/?!HERE?!/<</g
375384
p
376385
x
386+
s/^\([ ]*\)^/\1(/
377387
s/?!HERE?!/<</g
378388
b
379389

380390
# found closing "...)" -- exit subshell loop
381391
:close
382392
x
393+
s/^\([ ]*\)^/\1(/
383394
s/?!HERE?!/<</g
384395
p
385396
x
397+
s/^\([ ]*\)^/\1(/
386398
s/?!HERE?!/<</g
387399
b
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
(
2-
cd foo &&
1+
(cd foo &&
32
(bar &&
43
baz))

t/chainlint/complex-if-in-cuddled-loop.expect

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
(
2-
for i in a b c; do
1+
(for i in a b c; do
32
if test "$(echo $(waffle bat))" = "eleventeen" &&
43
test "$x" = "$y"; then
54
:

t/chainlint/cuddled-if-then-else.expect

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
(
2-
if test -z ""; then
1+
(if test -z ""; then
32
echo empty
43
else
54
echo bizzy

t/chainlint/cuddled-loop.expect

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
(
2-
while read x
1+
( while read x
32
do foobar bop || exit 1
43
done <file ) &&
54
outside subshell

t/chainlint/cuddled.expect

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,17 @@
1-
(
2-
cd foo &&
1+
(cd foo &&
32
bar
43
) &&
54

6-
(
7-
cd foo ?!AMP?!
5+
(cd foo ?!AMP?!
86
bar
97
) &&
108

119
(
1210
cd foo &&
1311
bar) &&
1412

15-
(
16-
cd foo &&
13+
(cd foo &&
1714
bar) &&
1815

19-
(
20-
cd foo ?!AMP?!
16+
(cd foo ?!AMP?!
2117
bar)

t/chainlint/inline-comment.expect

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,5 @@
44
flibble "not a # comment"
55
) &&
66

7-
(
8-
cd foo &&
7+
(cd foo &&
98
flibble "not a # comment")

t/chainlint/semicolon.expect

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@
1313
(
1414
foo;
1515
) &&
16-
(
17-
cd foo &&
16+
(cd foo &&
1817
for i in a b c; do
1918
echo;
2019
done)

0 commit comments

Comments
 (0)