2018-07-11 08:46:33 +02:00
|
|
|
#------------------------------------------------------------------------------
|
|
|
|
# Detect broken &&-chains in tests.
|
|
|
|
#
|
|
|
|
# At present, only &&-chains in subshells are examined by this linter;
|
|
|
|
# top-level &&-chains are instead checked directly by the test framework. Like
|
|
|
|
# the top-level &&-chain linter, the subshell linter (intentionally) does not
|
|
|
|
# check &&-chains within {...} blocks.
|
|
|
|
#
|
|
|
|
# Checking for &&-chain breakage is done line-by-line by pure textual
|
|
|
|
# inspection.
|
|
|
|
#
|
|
|
|
# Incomplete lines (those ending with "\") are stitched together with following
|
|
|
|
# lines to simplify processing, particularly of "one-liner" statements.
|
|
|
|
# Top-level here-docs are swallowed to avoid false positives within the
|
|
|
|
# here-doc body, although the statement to which the here-doc is attached is
|
|
|
|
# retained.
|
|
|
|
#
|
|
|
|
# Heuristics are used to detect end-of-subshell when the closing ")" is cuddled
|
|
|
|
# with the final subshell statement on the same line:
|
|
|
|
#
|
|
|
|
# (cd foo &&
|
|
|
|
# bar)
|
|
|
|
#
|
|
|
|
# in order to avoid misinterpreting the ")" in constructs such as "x=$(...)"
|
|
|
|
# and "case $x in *)" as ending the subshell.
|
|
|
|
#
|
chainlint.sed: drop unnecessary distinction between ?!AMP?! and ?!SEMI?!
>From inception, when chainlint.sed encountered a line using semicolon to
separate commands rather than `&&`, it would insert a ?!SEMI?!
annotation at the beginning of the line rather ?!AMP?! even though the
&&-chain is also broken by the semicolon. Given a line such as:
?!SEMI?! cmd1; cmd2 &&
the ?!SEMI?! annotation makes it easier to see what the problem is than
if the output had been:
?!AMP?! cmd1; cmd2 &&
which might confuse the test author into thinking that the linter is
broken (since the line clearly ends with `&&`).
However, now that the ?!AMP?! an ?!SEMI?! annotations are inserted at
the point of breakage rather than at the beginning of the line, and
taking into account that both represent a broken &&-chain, there is
little reason to distinguish between the two. Using ?!AMP?! alone is
sufficient to point the test author at the problem. For instance, in:
cmd1; ?!AMP?! cmd2 &&
cmd3
it is clear that the &&-chain is broken between `cmd1` and `cmd2`.
Likewise, in:
cmd1 && cmd2 ?!AMP?!
cmd3
it is clear that the &&-chain is broken between `cmd2` and `cmd3`.
Finally, in:
cmd1; ?!AMP?! cmd2 ?!AMP?!
cmd3
it is clear that the &&-chain is broken between each command.
Hence, there is no longer a good reason to make a distinction between a
broken &&-chain due to a semicolon and a broken chain due to a missing
`&&` at end-of-line. Therefore, drop the ?!SEMI?! annotation and use
?!AMP?! exclusively.
Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-13 07:30:53 +01:00
|
|
|
# Lines missing a final "&&" are flagged with "?!AMP?!", as are lines which
|
|
|
|
# chain commands with ";" internally rather than "&&". A line may be flagged
|
|
|
|
# for both violations.
|
2018-07-11 08:46:33 +02:00
|
|
|
#
|
|
|
|
# Detection of a missing &&-link in a multi-line subshell is complicated by the
|
|
|
|
# fact that the last statement before the closing ")" must not end with "&&".
|
|
|
|
# Since processing is line-by-line, it is not known whether a missing "&&" is
|
|
|
|
# legitimate or not until the _next_ line is seen. To accommodate this, within
|
|
|
|
# multi-line subshells, each line is stored in sed's "hold" area until after
|
|
|
|
# the next line is seen and processed. If the next line is a stand-alone ")",
|
|
|
|
# then a missing "&&" on the previous line is legitimate; otherwise a missing
|
|
|
|
# "&&" is a break in the &&-chain.
|
|
|
|
#
|
|
|
|
# (
|
|
|
|
# cd foo &&
|
|
|
|
# bar
|
|
|
|
# )
|
|
|
|
#
|
|
|
|
# In practical terms, when "bar" is encountered, it is flagged with "?!AMP?!",
|
|
|
|
# but when the stand-alone ")" line is seen which closes the subshell, the
|
|
|
|
# "?!AMP?!" violation is removed from the "bar" line (retrieved from the "hold"
|
|
|
|
# area) since the final statement of a subshell must not end with "&&". The
|
|
|
|
# final line of a subshell may still break the &&-chain by using ";" internally
|
chainlint.sed: drop unnecessary distinction between ?!AMP?! and ?!SEMI?!
>From inception, when chainlint.sed encountered a line using semicolon to
separate commands rather than `&&`, it would insert a ?!SEMI?!
annotation at the beginning of the line rather ?!AMP?! even though the
&&-chain is also broken by the semicolon. Given a line such as:
?!SEMI?! cmd1; cmd2 &&
the ?!SEMI?! annotation makes it easier to see what the problem is than
if the output had been:
?!AMP?! cmd1; cmd2 &&
which might confuse the test author into thinking that the linter is
broken (since the line clearly ends with `&&`).
However, now that the ?!AMP?! an ?!SEMI?! annotations are inserted at
the point of breakage rather than at the beginning of the line, and
taking into account that both represent a broken &&-chain, there is
little reason to distinguish between the two. Using ?!AMP?! alone is
sufficient to point the test author at the problem. For instance, in:
cmd1; ?!AMP?! cmd2 &&
cmd3
it is clear that the &&-chain is broken between `cmd1` and `cmd2`.
Likewise, in:
cmd1 && cmd2 ?!AMP?!
cmd3
it is clear that the &&-chain is broken between `cmd2` and `cmd3`.
Finally, in:
cmd1; ?!AMP?! cmd2 ?!AMP?!
cmd3
it is clear that the &&-chain is broken between each command.
Hence, there is no longer a good reason to make a distinction between a
broken &&-chain due to a semicolon and a broken chain due to a missing
`&&` at end-of-line. Therefore, drop the ?!SEMI?! annotation and use
?!AMP?! exclusively.
Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-13 07:30:53 +01:00
|
|
|
# to chain commands together rather than "&&", but an internal "?!AMP?!" is
|
|
|
|
# never removed from a line even though a line-ending "?!AMP?!" might be.
|
2018-07-11 08:46:33 +02:00
|
|
|
#
|
|
|
|
# Care is taken to recognize the last _statement_ of a multi-line subshell, not
|
|
|
|
# necessarily the last textual _line_ within the subshell, since &&-chaining
|
|
|
|
# applies to statements, not to lines. Consequently, blank lines, comment
|
|
|
|
# lines, and here-docs are swallowed (but not the command to which the here-doc
|
|
|
|
# is attached), leaving the last statement in the "hold" area, not the last
|
|
|
|
# line, thus simplifying &&-link checking.
|
|
|
|
#
|
|
|
|
# The final statement before "done" in for- and while-loops, and before "elif",
|
|
|
|
# "else", and "fi" in if-then-else likewise must not end with "&&", thus
|
|
|
|
# receives similar treatment.
|
|
|
|
#
|
chainlint: match arbitrary here-docs tags rather than hard-coded names
chainlint.sed swallows top-level here-docs to avoid being fooled by
content which might look like start-of-subshell. It likewise swallows
here-docs in subshells to avoid marking content lines as breaking the
&&-chain, and to avoid being fooled by content which might look like
end-of-subshell, start-of-nested-subshell, or other specially-recognized
constructs.
At the time of implementation, it was believed that it was not possible
to support arbitrary here-doc tag names since 'sed' provides no way to
stash the opening tag name in a variable for later comparison against a
line signaling end-of-here-doc. Consequently, tag names are hard-coded,
with "EOF" being the only tag recognized at the top-level, and only
"EOF", "EOT", and "INPUT_END" being recognized within subshells. Also,
special care was taken to avoid being confused by here-docs nested
within other here-docs.
In practice, this limited number of hard-coded tag names has been "good
enough" for the 13000+ existing Git test, despite many of those tests
using tags other than the recognized ones, since the bodies of those
here-docs do not contain content which would fool the linter.
Nevertheless, the situation is not ideal since someone writing new
tests, and choosing a name not in the "blessed" set could potentially
trigger a false-positive.
To address this shortcoming, upgrade chainlint.sed to handle arbitrary
here-doc tag names, both at the top-level and within subshells.
Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-13 10:47:34 +02:00
|
|
|
# Swallowing here-docs with arbitrary tags requires a bit of finesse. When a
|
2021-12-13 07:30:57 +01:00
|
|
|
# line such as "cat <<EOF" is seen, the here-doc tag is copied to the front of
|
|
|
|
# the line enclosed in angle brackets as a sentinel, giving "<EOF>cat <<EOF".
|
chainlint: match arbitrary here-docs tags rather than hard-coded names
chainlint.sed swallows top-level here-docs to avoid being fooled by
content which might look like start-of-subshell. It likewise swallows
here-docs in subshells to avoid marking content lines as breaking the
&&-chain, and to avoid being fooled by content which might look like
end-of-subshell, start-of-nested-subshell, or other specially-recognized
constructs.
At the time of implementation, it was believed that it was not possible
to support arbitrary here-doc tag names since 'sed' provides no way to
stash the opening tag name in a variable for later comparison against a
line signaling end-of-here-doc. Consequently, tag names are hard-coded,
with "EOF" being the only tag recognized at the top-level, and only
"EOF", "EOT", and "INPUT_END" being recognized within subshells. Also,
special care was taken to avoid being confused by here-docs nested
within other here-docs.
In practice, this limited number of hard-coded tag names has been "good
enough" for the 13000+ existing Git test, despite many of those tests
using tags other than the recognized ones, since the bodies of those
here-docs do not contain content which would fool the linter.
Nevertheless, the situation is not ideal since someone writing new
tests, and choosing a name not in the "blessed" set could potentially
trigger a false-positive.
To address this shortcoming, upgrade chainlint.sed to handle arbitrary
here-doc tag names, both at the top-level and within subshells.
Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-13 10:47:34 +02:00
|
|
|
# As each subsequent line is read, it is appended to the target line and a
|
|
|
|
# (whitespace-loose) back-reference match /^<(.*)>\n\1$/ is attempted to see if
|
|
|
|
# the content inside "<...>" matches the entirety of the newly-read line. For
|
|
|
|
# instance, if the next line read is "some data", when concatenated with the
|
2021-12-13 07:30:57 +01:00
|
|
|
# target line, it becomes "<EOF>cat <<EOF\nsome data", and a match is attempted
|
chainlint: match arbitrary here-docs tags rather than hard-coded names
chainlint.sed swallows top-level here-docs to avoid being fooled by
content which might look like start-of-subshell. It likewise swallows
here-docs in subshells to avoid marking content lines as breaking the
&&-chain, and to avoid being fooled by content which might look like
end-of-subshell, start-of-nested-subshell, or other specially-recognized
constructs.
At the time of implementation, it was believed that it was not possible
to support arbitrary here-doc tag names since 'sed' provides no way to
stash the opening tag name in a variable for later comparison against a
line signaling end-of-here-doc. Consequently, tag names are hard-coded,
with "EOF" being the only tag recognized at the top-level, and only
"EOF", "EOT", and "INPUT_END" being recognized within subshells. Also,
special care was taken to avoid being confused by here-docs nested
within other here-docs.
In practice, this limited number of hard-coded tag names has been "good
enough" for the 13000+ existing Git test, despite many of those tests
using tags other than the recognized ones, since the bodies of those
here-docs do not contain content which would fool the linter.
Nevertheless, the situation is not ideal since someone writing new
tests, and choosing a name not in the "blessed" set could potentially
trigger a false-positive.
To address this shortcoming, upgrade chainlint.sed to handle arbitrary
here-doc tag names, both at the top-level and within subshells.
Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-13 10:47:34 +02:00
|
|
|
# to see if "EOF" matches "some data". Since it doesn't, the next line is
|
|
|
|
# attempted. When a line consisting of only "EOF" (and possible whitespace) is
|
2021-12-13 07:30:57 +01:00
|
|
|
# encountered, it is appended to the target line giving "<EOF>cat <<EOF\nEOF",
|
chainlint: match arbitrary here-docs tags rather than hard-coded names
chainlint.sed swallows top-level here-docs to avoid being fooled by
content which might look like start-of-subshell. It likewise swallows
here-docs in subshells to avoid marking content lines as breaking the
&&-chain, and to avoid being fooled by content which might look like
end-of-subshell, start-of-nested-subshell, or other specially-recognized
constructs.
At the time of implementation, it was believed that it was not possible
to support arbitrary here-doc tag names since 'sed' provides no way to
stash the opening tag name in a variable for later comparison against a
line signaling end-of-here-doc. Consequently, tag names are hard-coded,
with "EOF" being the only tag recognized at the top-level, and only
"EOF", "EOT", and "INPUT_END" being recognized within subshells. Also,
special care was taken to avoid being confused by here-docs nested
within other here-docs.
In practice, this limited number of hard-coded tag names has been "good
enough" for the 13000+ existing Git test, despite many of those tests
using tags other than the recognized ones, since the bodies of those
here-docs do not contain content which would fool the linter.
Nevertheless, the situation is not ideal since someone writing new
tests, and choosing a name not in the "blessed" set could potentially
trigger a false-positive.
To address this shortcoming, upgrade chainlint.sed to handle arbitrary
here-doc tag names, both at the top-level and within subshells.
Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-13 10:47:34 +02:00
|
|
|
# in which case the "EOF" inside "<...>" does match the text following the
|
|
|
|
# newline, thus the closing here-doc tag has been found. The closing tag line
|
|
|
|
# and the "<...>" prefix on the target line are then discarded, leaving just
|
2021-12-13 07:30:57 +01:00
|
|
|
# the target line "cat <<EOF".
|
2018-07-11 08:46:33 +02:00
|
|
|
#------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
# incomplete line -- slurp up next line
|
|
|
|
:squash
|
|
|
|
/\\$/ {
|
2018-07-31 07:03:20 +02:00
|
|
|
N
|
|
|
|
s/\\\n//
|
|
|
|
bsquash
|
2018-07-11 08:46:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
# here-doc -- swallow it to avoid false hits within its body (but keep the
|
|
|
|
# command to which it was attached)
|
2021-12-13 07:30:55 +01:00
|
|
|
/<<-*[ ]*[\\'"]*[A-Za-z0-9_]/ {
|
2021-12-13 07:30:56 +01:00
|
|
|
/"[^"]*<<[^"]*"/bnotdoc
|
2021-12-13 07:30:57 +01:00
|
|
|
s/^\(.*<<-*[ ]*\)[\\'"]*\([A-Za-z0-9_][A-Za-z0-9_]*\)['"]*/<\2>\1\2/
|
2018-08-24 17:20:14 +02:00
|
|
|
:hered
|
2018-07-11 08:46:33 +02:00
|
|
|
N
|
chainlint: match arbitrary here-docs tags rather than hard-coded names
chainlint.sed swallows top-level here-docs to avoid being fooled by
content which might look like start-of-subshell. It likewise swallows
here-docs in subshells to avoid marking content lines as breaking the
&&-chain, and to avoid being fooled by content which might look like
end-of-subshell, start-of-nested-subshell, or other specially-recognized
constructs.
At the time of implementation, it was believed that it was not possible
to support arbitrary here-doc tag names since 'sed' provides no way to
stash the opening tag name in a variable for later comparison against a
line signaling end-of-here-doc. Consequently, tag names are hard-coded,
with "EOF" being the only tag recognized at the top-level, and only
"EOF", "EOT", and "INPUT_END" being recognized within subshells. Also,
special care was taken to avoid being confused by here-docs nested
within other here-docs.
In practice, this limited number of hard-coded tag names has been "good
enough" for the 13000+ existing Git test, despite many of those tests
using tags other than the recognized ones, since the bodies of those
here-docs do not contain content which would fool the linter.
Nevertheless, the situation is not ideal since someone writing new
tests, and choosing a name not in the "blessed" set could potentially
trigger a false-positive.
To address this shortcoming, upgrade chainlint.sed to handle arbitrary
here-doc tag names, both at the top-level and within subshells.
Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-13 10:47:34 +02:00
|
|
|
/^<\([^>]*\)>.*\n[ ]*\1[ ]*$/!{
|
|
|
|
s/\n.*$//
|
2018-08-24 17:20:14 +02:00
|
|
|
bhered
|
chainlint: match arbitrary here-docs tags rather than hard-coded names
chainlint.sed swallows top-level here-docs to avoid being fooled by
content which might look like start-of-subshell. It likewise swallows
here-docs in subshells to avoid marking content lines as breaking the
&&-chain, and to avoid being fooled by content which might look like
end-of-subshell, start-of-nested-subshell, or other specially-recognized
constructs.
At the time of implementation, it was believed that it was not possible
to support arbitrary here-doc tag names since 'sed' provides no way to
stash the opening tag name in a variable for later comparison against a
line signaling end-of-here-doc. Consequently, tag names are hard-coded,
with "EOF" being the only tag recognized at the top-level, and only
"EOF", "EOT", and "INPUT_END" being recognized within subshells. Also,
special care was taken to avoid being confused by here-docs nested
within other here-docs.
In practice, this limited number of hard-coded tag names has been "good
enough" for the 13000+ existing Git test, despite many of those tests
using tags other than the recognized ones, since the bodies of those
here-docs do not contain content which would fool the linter.
Nevertheless, the situation is not ideal since someone writing new
tests, and choosing a name not in the "blessed" set could potentially
trigger a false-positive.
To address this shortcoming, upgrade chainlint.sed to handle arbitrary
here-doc tag names, both at the top-level and within subshells.
Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-13 10:47:34 +02:00
|
|
|
}
|
|
|
|
s/^<[^>]*>//
|
|
|
|
s/\n.*$//
|
2018-07-11 08:46:33 +02:00
|
|
|
}
|
2021-12-13 07:30:56 +01:00
|
|
|
:notdoc
|
2018-07-11 08:46:33 +02:00
|
|
|
|
|
|
|
# one-liner "(...) &&"
|
|
|
|
/^[ ]*!*[ ]*(..*)[ ]*&&[ ]*$/boneline
|
|
|
|
|
|
|
|
# same as above but without trailing "&&"
|
|
|
|
/^[ ]*!*[ ]*(..*)[ ]*$/boneline
|
|
|
|
|
|
|
|
# one-liner "(...) >x" (or "2>x" or "<x" or "|x" or "&"
|
|
|
|
/^[ ]*!*[ ]*(..*)[ ]*[0-9]*[<>|&]/boneline
|
|
|
|
|
|
|
|
# multi-line "(...\n...)"
|
2020-09-10 04:17:41 +02:00
|
|
|
/^[ ]*(/bsubsh
|
2018-07-11 08:46:33 +02:00
|
|
|
|
|
|
|
# innocuous line -- print it and advance to next line
|
|
|
|
b
|
|
|
|
|
|
|
|
# found one-liner "(...)" -- mark suspect if it uses ";" internally rather than
|
|
|
|
# "&&" (but not ";" in a string)
|
|
|
|
:oneline
|
|
|
|
/;/{
|
chainlint.sed: drop unnecessary distinction between ?!AMP?! and ?!SEMI?!
>From inception, when chainlint.sed encountered a line using semicolon to
separate commands rather than `&&`, it would insert a ?!SEMI?!
annotation at the beginning of the line rather ?!AMP?! even though the
&&-chain is also broken by the semicolon. Given a line such as:
?!SEMI?! cmd1; cmd2 &&
the ?!SEMI?! annotation makes it easier to see what the problem is than
if the output had been:
?!AMP?! cmd1; cmd2 &&
which might confuse the test author into thinking that the linter is
broken (since the line clearly ends with `&&`).
However, now that the ?!AMP?! an ?!SEMI?! annotations are inserted at
the point of breakage rather than at the beginning of the line, and
taking into account that both represent a broken &&-chain, there is
little reason to distinguish between the two. Using ?!AMP?! alone is
sufficient to point the test author at the problem. For instance, in:
cmd1; ?!AMP?! cmd2 &&
cmd3
it is clear that the &&-chain is broken between `cmd1` and `cmd2`.
Likewise, in:
cmd1 && cmd2 ?!AMP?!
cmd3
it is clear that the &&-chain is broken between `cmd2` and `cmd3`.
Finally, in:
cmd1; ?!AMP?! cmd2 ?!AMP?!
cmd3
it is clear that the &&-chain is broken between each command.
Hence, there is no longer a good reason to make a distinction between a
broken &&-chain due to a semicolon and a broken chain due to a missing
`&&` at end-of-line. Therefore, drop the ?!SEMI?! annotation and use
?!AMP?! exclusively.
Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-13 07:30:53 +01:00
|
|
|
/"[^"]*;[^"]*"/!s/;/; ?!AMP?!/
|
2018-07-11 08:46:33 +02:00
|
|
|
}
|
|
|
|
b
|
|
|
|
|
2020-09-10 04:17:41 +02:00
|
|
|
:subsh
|
2018-08-24 17:20:13 +02:00
|
|
|
# bare "(" line? -- stash for later printing
|
2018-07-11 08:46:33 +02:00
|
|
|
/^[ ]*([ ]*$/ {
|
|
|
|
h
|
2020-09-10 04:17:41 +02:00
|
|
|
bnextln
|
2018-07-11 08:46:33 +02:00
|
|
|
}
|
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 <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-13 07:30:59 +01:00
|
|
|
# "(..." line -- "(" opening subshell cuddled with command; temporarily replace
|
|
|
|
# "(" with sentinel "^" and process the line as if "(" had been seen solo on
|
|
|
|
# the preceding line; this temporary replacement prevents several rules from
|
|
|
|
# accidentally thinking "(" introduces a nested subshell; "^" is changed back
|
|
|
|
# to "(" at output time
|
2018-07-11 08:46:33 +02:00
|
|
|
x
|
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 <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-13 07:30:59 +01:00
|
|
|
s/.*//
|
2018-07-11 08:46:33 +02:00
|
|
|
x
|
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 <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-13 07:30:59 +01:00
|
|
|
s/(/^/
|
2018-07-11 08:46:33 +02:00
|
|
|
bslurp
|
|
|
|
|
2020-09-10 04:17:41 +02:00
|
|
|
:nextln
|
2018-07-11 08:46:33 +02:00
|
|
|
N
|
|
|
|
s/.*\n//
|
|
|
|
|
|
|
|
:slurp
|
|
|
|
# incomplete line "...\"
|
2018-08-24 17:20:14 +02:00
|
|
|
/\\$/bicmplte
|
2018-08-13 10:47:38 +02:00
|
|
|
# multi-line quoted string "...\n..."?
|
2020-09-10 04:17:41 +02:00
|
|
|
/"/bdqstr
|
2018-08-13 10:47:38 +02:00
|
|
|
# multi-line quoted string '...\n...'? (but not contraction in string "it's")
|
|
|
|
/'/{
|
2020-09-10 04:17:41 +02:00
|
|
|
/"[^'"]*'[^'"]*"/!bsqstr
|
2018-07-11 08:46:33 +02:00
|
|
|
}
|
2018-08-13 10:47:37 +02:00
|
|
|
:folded
|
2021-12-13 07:30:56 +01:00
|
|
|
# here-doc -- swallow it (but not "<<" in a string)
|
|
|
|
/<<-*[ ]*[\\'"]*[A-Za-z0-9_]/{
|
|
|
|
/"[^"]*<<[^"]*"/!bheredoc
|
|
|
|
}
|
2018-07-11 08:46:33 +02:00
|
|
|
# comment or empty line -- discard since final non-comment, non-empty line
|
|
|
|
# before closing ")", "done", "elsif", "else", or "fi" will need to be
|
|
|
|
# re-visited to drop "suspect" marking since final line of those constructs
|
|
|
|
# legitimately lacks "&&", so "suspect" mark must be removed
|
2020-09-10 04:17:41 +02:00
|
|
|
/^[ ]*#/bnextln
|
|
|
|
/^[ ]*$/bnextln
|
2018-07-11 08:46:33 +02:00
|
|
|
# in-line comment -- strip it (but not "#" in a string, Bash ${#...} array
|
|
|
|
# length, or Perforce "//depot/path#42" revision in filespec)
|
|
|
|
/[ ]#/{
|
|
|
|
/"[^"]*#[^"]*"/!s/[ ]#.*$//
|
|
|
|
}
|
|
|
|
# one-liner "case ... esac"
|
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 <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-13 07:30:59 +01:00
|
|
|
/^[ ^]*case[ ]*..*esac/bchkchn
|
2018-07-11 08:46:33 +02:00
|
|
|
# multi-line "case ... esac"
|
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 <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-13 07:30:59 +01:00
|
|
|
/^[ ^]*case[ ]..*[ ]in/bcase
|
2018-07-11 08:46:33 +02:00
|
|
|
# multi-line "for ... done" or "while ... done"
|
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 <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-13 07:30:59 +01:00
|
|
|
/^[ ^]*for[ ]..*[ ]in/bcont
|
|
|
|
/^[ ^]*while[ ]/bcont
|
2020-09-10 04:17:41 +02:00
|
|
|
/^[ ]*do[ ]/bcont
|
|
|
|
/^[ ]*do[ ]*$/bcont
|
|
|
|
/;[ ]*do/bcont
|
2018-07-11 08:46:33 +02:00
|
|
|
/^[ ]*done[ ]*&&[ ]*$/bdone
|
|
|
|
/^[ ]*done[ ]*$/bdone
|
|
|
|
/^[ ]*done[ ]*[<>|]/bdone
|
|
|
|
/^[ ]*done[ ]*)/bdone
|
2020-09-10 04:17:41 +02:00
|
|
|
/||[ ]*exit[ ]/bcont
|
|
|
|
/||[ ]*exit[ ]*$/bcont
|
2018-07-11 08:46:33 +02:00
|
|
|
# multi-line "if...elsif...else...fi"
|
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 <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-13 07:30:59 +01:00
|
|
|
/^[ ^]*if[ ]/bcont
|
2020-09-10 04:17:41 +02:00
|
|
|
/^[ ]*then[ ]/bcont
|
|
|
|
/^[ ]*then[ ]*$/bcont
|
|
|
|
/;[ ]*then/bcont
|
2018-07-11 08:46:33 +02:00
|
|
|
/^[ ]*elif[ ]/belse
|
|
|
|
/^[ ]*elif[ ]*$/belse
|
|
|
|
/^[ ]*else[ ]/belse
|
|
|
|
/^[ ]*else[ ]*$/belse
|
|
|
|
/^[ ]*fi[ ]*&&[ ]*$/bdone
|
|
|
|
/^[ ]*fi[ ]*$/bdone
|
|
|
|
/^[ ]*fi[ ]*[<>|]/bdone
|
|
|
|
/^[ ]*fi[ ]*)/bdone
|
|
|
|
# nested one-liner "(...) &&"
|
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 <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-13 07:30:59 +01:00
|
|
|
/^[ ^]*(.*)[ ]*&&[ ]*$/bchkchn
|
2018-07-11 08:46:33 +02:00
|
|
|
# nested one-liner "(...)"
|
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 <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-13 07:30:59 +01:00
|
|
|
/^[ ^]*(.*)[ ]*$/bchkchn
|
2018-07-11 08:46:33 +02:00
|
|
|
# nested one-liner "(...) >x" (or "2>x" or "<x" or "|x")
|
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 <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-13 07:30:59 +01:00
|
|
|
/^[ ^]*(.*)[ ]*[0-9]*[<>|]/bchkchn
|
2018-07-11 08:46:33 +02:00
|
|
|
# nested multi-line "(...\n...)"
|
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 <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-13 07:30:59 +01:00
|
|
|
/^[ ^]*(/bnest
|
2018-07-11 08:46:33 +02:00
|
|
|
# multi-line "{...\n...}"
|
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 <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-13 07:30:59 +01:00
|
|
|
/^[ ^]*{/bblock
|
2018-07-11 08:46:33 +02:00
|
|
|
# closing ")" on own line -- exit subshell
|
2018-08-24 17:20:14 +02:00
|
|
|
/^[ ]*)/bclssolo
|
2018-07-11 08:46:33 +02:00
|
|
|
# "$((...))" -- arithmetic expansion; not closing ")"
|
2018-08-24 17:20:14 +02:00
|
|
|
/\$(([^)][^)]*))[^)]*$/bchkchn
|
2018-07-11 08:46:33 +02:00
|
|
|
# "$(...)" -- command substitution; not closing ")"
|
2018-08-24 17:20:14 +02:00
|
|
|
/\$([^)][^)]*)[^)]*$/bchkchn
|
2018-07-11 08:46:33 +02:00
|
|
|
# multi-line "$(...\n...)" -- command substitution; treat as nested subshell
|
2018-08-13 10:47:36 +02:00
|
|
|
/\$([^)]*$/bnest
|
2018-07-11 08:46:33 +02:00
|
|
|
# "=(...)" -- Bash array assignment; not closing ")"
|
2018-08-24 17:20:14 +02:00
|
|
|
/=(/bchkchn
|
2018-07-11 08:46:33 +02:00
|
|
|
# closing "...) &&"
|
|
|
|
/)[ ]*&&[ ]*$/bclose
|
|
|
|
# closing "...)"
|
|
|
|
/)[ ]*$/bclose
|
|
|
|
# closing "...) >x" (or "2>x" or "<x" or "|x")
|
|
|
|
/)[ ]*[<>|]/bclose
|
2018-08-24 17:20:14 +02:00
|
|
|
:chkchn
|
2018-07-11 08:46:33 +02:00
|
|
|
# mark suspect if line uses ";" internally rather than "&&" (but not ";" in a
|
|
|
|
# string and not ";;" in one-liner "case...esac")
|
|
|
|
/;/{
|
|
|
|
/;;/!{
|
chainlint.sed: drop unnecessary distinction between ?!AMP?! and ?!SEMI?!
>From inception, when chainlint.sed encountered a line using semicolon to
separate commands rather than `&&`, it would insert a ?!SEMI?!
annotation at the beginning of the line rather ?!AMP?! even though the
&&-chain is also broken by the semicolon. Given a line such as:
?!SEMI?! cmd1; cmd2 &&
the ?!SEMI?! annotation makes it easier to see what the problem is than
if the output had been:
?!AMP?! cmd1; cmd2 &&
which might confuse the test author into thinking that the linter is
broken (since the line clearly ends with `&&`).
However, now that the ?!AMP?! an ?!SEMI?! annotations are inserted at
the point of breakage rather than at the beginning of the line, and
taking into account that both represent a broken &&-chain, there is
little reason to distinguish between the two. Using ?!AMP?! alone is
sufficient to point the test author at the problem. For instance, in:
cmd1; ?!AMP?! cmd2 &&
cmd3
it is clear that the &&-chain is broken between `cmd1` and `cmd2`.
Likewise, in:
cmd1 && cmd2 ?!AMP?!
cmd3
it is clear that the &&-chain is broken between `cmd2` and `cmd3`.
Finally, in:
cmd1; ?!AMP?! cmd2 ?!AMP?!
cmd3
it is clear that the &&-chain is broken between each command.
Hence, there is no longer a good reason to make a distinction between a
broken &&-chain due to a semicolon and a broken chain due to a missing
`&&` at end-of-line. Therefore, drop the ?!SEMI?! annotation and use
?!AMP?! exclusively.
Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-13 07:30:53 +01:00
|
|
|
/"[^"]*;[^"]*"/!s/;/; ?!AMP?!/
|
2018-07-11 08:46:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
# line ends with pipe "...|" -- valid; not missing "&&"
|
2020-09-10 04:17:41 +02:00
|
|
|
/|[ ]*$/bcont
|
2018-07-11 08:46:33 +02:00
|
|
|
# missing end-of-line "&&" -- mark suspect
|
2021-12-13 07:30:50 +01:00
|
|
|
/&&[ ]*$/!s/$/ ?!AMP?!/
|
2020-09-10 04:17:41 +02:00
|
|
|
:cont
|
2018-07-11 08:46:33 +02:00
|
|
|
# retrieve and print previous line
|
|
|
|
x
|
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 <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-13 07:30:59 +01:00
|
|
|
s/^\([ ]*\)^/\1(/
|
2021-12-13 07:30:57 +01:00
|
|
|
s/?!HERE?!/<</g
|
2018-07-11 08:46:33 +02:00
|
|
|
n
|
|
|
|
bslurp
|
|
|
|
|
|
|
|
# found incomplete line "...\" -- slurp up next line
|
2018-08-24 17:20:14 +02:00
|
|
|
:icmplte
|
2018-07-11 08:46:33 +02:00
|
|
|
N
|
|
|
|
s/\\\n//
|
|
|
|
bslurp
|
|
|
|
|
2018-08-13 10:47:38 +02:00
|
|
|
# check for multi-line double-quoted string "...\n..." -- fold to one line
|
2020-09-10 04:17:41 +02:00
|
|
|
:dqstr
|
2018-08-13 10:47:38 +02:00
|
|
|
# remove all quote pairs
|
|
|
|
s/"\([^"]*\)"/@!\1@!/g
|
|
|
|
# done if no dangling quote
|
|
|
|
/"/!bdqdone
|
|
|
|
# otherwise, slurp next line and try again
|
2018-07-11 08:46:33 +02:00
|
|
|
N
|
|
|
|
s/\n//
|
2020-09-10 04:17:41 +02:00
|
|
|
bdqstr
|
2018-08-13 10:47:38 +02:00
|
|
|
:dqdone
|
|
|
|
s/@!/"/g
|
2018-08-13 10:47:37 +02:00
|
|
|
bfolded
|
2018-07-11 08:46:33 +02:00
|
|
|
|
2018-08-13 10:47:38 +02:00
|
|
|
# check for multi-line single-quoted string '...\n...' -- fold to one line
|
2020-09-10 04:17:41 +02:00
|
|
|
:sqstr
|
2018-08-13 10:47:38 +02:00
|
|
|
# remove all quote pairs
|
|
|
|
s/'\([^']*\)'/@!\1@!/g
|
|
|
|
# done if no dangling quote
|
|
|
|
/'/!bsqdone
|
|
|
|
# otherwise, slurp next line and try again
|
2018-07-11 08:46:33 +02:00
|
|
|
N
|
|
|
|
s/\n//
|
2020-09-10 04:17:41 +02:00
|
|
|
bsqstr
|
2018-08-13 10:47:38 +02:00
|
|
|
:sqdone
|
|
|
|
s/@!/'/g
|
2018-08-13 10:47:37 +02:00
|
|
|
bfolded
|
2018-07-11 08:46:33 +02:00
|
|
|
|
|
|
|
# found here-doc -- swallow it to avoid false hits within its body (but keep
|
chainlint: match arbitrary here-docs tags rather than hard-coded names
chainlint.sed swallows top-level here-docs to avoid being fooled by
content which might look like start-of-subshell. It likewise swallows
here-docs in subshells to avoid marking content lines as breaking the
&&-chain, and to avoid being fooled by content which might look like
end-of-subshell, start-of-nested-subshell, or other specially-recognized
constructs.
At the time of implementation, it was believed that it was not possible
to support arbitrary here-doc tag names since 'sed' provides no way to
stash the opening tag name in a variable for later comparison against a
line signaling end-of-here-doc. Consequently, tag names are hard-coded,
with "EOF" being the only tag recognized at the top-level, and only
"EOF", "EOT", and "INPUT_END" being recognized within subshells. Also,
special care was taken to avoid being confused by here-docs nested
within other here-docs.
In practice, this limited number of hard-coded tag names has been "good
enough" for the 13000+ existing Git test, despite many of those tests
using tags other than the recognized ones, since the bodies of those
here-docs do not contain content which would fool the linter.
Nevertheless, the situation is not ideal since someone writing new
tests, and choosing a name not in the "blessed" set could potentially
trigger a false-positive.
To address this shortcoming, upgrade chainlint.sed to handle arbitrary
here-doc tag names, both at the top-level and within subshells.
Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-13 10:47:34 +02:00
|
|
|
# the command to which it was attached)
|
2018-07-11 08:46:33 +02:00
|
|
|
:heredoc
|
2021-12-13 07:30:57 +01:00
|
|
|
s/^\(.*\)<<\(-*[ ]*\)[\\'"]*\([A-Za-z0-9_][A-Za-z0-9_]*\)['"]*/<\3>\1?!HERE?!\2\3/
|
2020-09-10 04:17:41 +02:00
|
|
|
:hdocsub
|
2018-07-11 08:46:33 +02:00
|
|
|
N
|
chainlint: match arbitrary here-docs tags rather than hard-coded names
chainlint.sed swallows top-level here-docs to avoid being fooled by
content which might look like start-of-subshell. It likewise swallows
here-docs in subshells to avoid marking content lines as breaking the
&&-chain, and to avoid being fooled by content which might look like
end-of-subshell, start-of-nested-subshell, or other specially-recognized
constructs.
At the time of implementation, it was believed that it was not possible
to support arbitrary here-doc tag names since 'sed' provides no way to
stash the opening tag name in a variable for later comparison against a
line signaling end-of-here-doc. Consequently, tag names are hard-coded,
with "EOF" being the only tag recognized at the top-level, and only
"EOF", "EOT", and "INPUT_END" being recognized within subshells. Also,
special care was taken to avoid being confused by here-docs nested
within other here-docs.
In practice, this limited number of hard-coded tag names has been "good
enough" for the 13000+ existing Git test, despite many of those tests
using tags other than the recognized ones, since the bodies of those
here-docs do not contain content which would fool the linter.
Nevertheless, the situation is not ideal since someone writing new
tests, and choosing a name not in the "blessed" set could potentially
trigger a false-positive.
To address this shortcoming, upgrade chainlint.sed to handle arbitrary
here-doc tag names, both at the top-level and within subshells.
Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-13 10:47:34 +02:00
|
|
|
/^<\([^>]*\)>.*\n[ ]*\1[ ]*$/!{
|
|
|
|
s/\n.*$//
|
2020-09-10 04:17:41 +02:00
|
|
|
bhdocsub
|
chainlint: match arbitrary here-docs tags rather than hard-coded names
chainlint.sed swallows top-level here-docs to avoid being fooled by
content which might look like start-of-subshell. It likewise swallows
here-docs in subshells to avoid marking content lines as breaking the
&&-chain, and to avoid being fooled by content which might look like
end-of-subshell, start-of-nested-subshell, or other specially-recognized
constructs.
At the time of implementation, it was believed that it was not possible
to support arbitrary here-doc tag names since 'sed' provides no way to
stash the opening tag name in a variable for later comparison against a
line signaling end-of-here-doc. Consequently, tag names are hard-coded,
with "EOF" being the only tag recognized at the top-level, and only
"EOF", "EOT", and "INPUT_END" being recognized within subshells. Also,
special care was taken to avoid being confused by here-docs nested
within other here-docs.
In practice, this limited number of hard-coded tag names has been "good
enough" for the 13000+ existing Git test, despite many of those tests
using tags other than the recognized ones, since the bodies of those
here-docs do not contain content which would fool the linter.
Nevertheless, the situation is not ideal since someone writing new
tests, and choosing a name not in the "blessed" set could potentially
trigger a false-positive.
To address this shortcoming, upgrade chainlint.sed to handle arbitrary
here-doc tag names, both at the top-level and within subshells.
Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-13 10:47:34 +02:00
|
|
|
}
|
|
|
|
s/^<[^>]*>//
|
2018-07-11 08:46:33 +02:00
|
|
|
s/\n.*$//
|
2018-08-13 10:47:37 +02:00
|
|
|
bfolded
|
2018-07-11 08:46:33 +02:00
|
|
|
|
|
|
|
# found "case ... in" -- pass through untouched
|
|
|
|
:case
|
|
|
|
x
|
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 <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-13 07:30:59 +01:00
|
|
|
s/^\([ ]*\)^/\1(/
|
2021-12-13 07:30:57 +01:00
|
|
|
s/?!HERE?!/<</g
|
2018-07-11 08:46:33 +02:00
|
|
|
n
|
chainlint.sed: swallow comments consistently
When checking for broken a &&-chain, chainlint.sed knows that the final
statement in a subshell should not end with `&&`, so it takes care to
make a distinction between the final line which is an actual statement
and any lines which may be mere comments preceding the closing ')'. As
such, it swallows comment lines so that they do not interfere with the
&&-chain check.
However, since `sed` does not provide any sort of real recursion,
chainlint.sed only checks &&-chains in subshells one level deep; it
doesn't do any checking in deeper subshells or in `{...}` blocks within
subshells. Furthermore, on account of potential implementation
complexity, it doesn't check &&-chains within `case` arms.
Due to an oversight, it also doesn't swallow comments inside deep
subshells, `{...}` blocks, or `case` statements, which makes its output
inconsistent (swallowing comments in some cases but not others).
Unfortunately, this inconsistency seeps 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. Therefore,
teach chainlint.sed to consistently swallow comments in all cases.
Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-13 07:30:58 +01:00
|
|
|
:cascom
|
|
|
|
/^[ ]*#/{
|
|
|
|
N
|
|
|
|
s/.*\n//
|
|
|
|
bcascom
|
|
|
|
}
|
2018-07-11 08:46:33 +02:00
|
|
|
/^[ ]*esac/bslurp
|
|
|
|
bcase
|
|
|
|
|
|
|
|
# found "else" or "elif" -- drop "suspect" from final line before "else" since
|
|
|
|
# that line legitimately lacks "&&"
|
|
|
|
:else
|
|
|
|
x
|
chainlint.sed: drop unnecessary distinction between ?!AMP?! and ?!SEMI?!
>From inception, when chainlint.sed encountered a line using semicolon to
separate commands rather than `&&`, it would insert a ?!SEMI?!
annotation at the beginning of the line rather ?!AMP?! even though the
&&-chain is also broken by the semicolon. Given a line such as:
?!SEMI?! cmd1; cmd2 &&
the ?!SEMI?! annotation makes it easier to see what the problem is than
if the output had been:
?!AMP?! cmd1; cmd2 &&
which might confuse the test author into thinking that the linter is
broken (since the line clearly ends with `&&`).
However, now that the ?!AMP?! an ?!SEMI?! annotations are inserted at
the point of breakage rather than at the beginning of the line, and
taking into account that both represent a broken &&-chain, there is
little reason to distinguish between the two. Using ?!AMP?! alone is
sufficient to point the test author at the problem. For instance, in:
cmd1; ?!AMP?! cmd2 &&
cmd3
it is clear that the &&-chain is broken between `cmd1` and `cmd2`.
Likewise, in:
cmd1 && cmd2 ?!AMP?!
cmd3
it is clear that the &&-chain is broken between `cmd2` and `cmd3`.
Finally, in:
cmd1; ?!AMP?! cmd2 ?!AMP?!
cmd3
it is clear that the &&-chain is broken between each command.
Hence, there is no longer a good reason to make a distinction between a
broken &&-chain due to a semicolon and a broken chain due to a missing
`&&` at end-of-line. Therefore, drop the ?!SEMI?! annotation and use
?!AMP?! exclusively.
Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-13 07:30:53 +01:00
|
|
|
s/\( ?!AMP?!\)* ?!AMP?!$//
|
2018-07-11 08:46:33 +02:00
|
|
|
x
|
2020-09-10 04:17:41 +02:00
|
|
|
bcont
|
2018-07-11 08:46:33 +02:00
|
|
|
|
|
|
|
# found "done" closing for-loop or while-loop, or "fi" closing if-then -- drop
|
|
|
|
# "suspect" from final contained line since that line legitimately lacks "&&"
|
|
|
|
:done
|
|
|
|
x
|
chainlint.sed: drop unnecessary distinction between ?!AMP?! and ?!SEMI?!
>From inception, when chainlint.sed encountered a line using semicolon to
separate commands rather than `&&`, it would insert a ?!SEMI?!
annotation at the beginning of the line rather ?!AMP?! even though the
&&-chain is also broken by the semicolon. Given a line such as:
?!SEMI?! cmd1; cmd2 &&
the ?!SEMI?! annotation makes it easier to see what the problem is than
if the output had been:
?!AMP?! cmd1; cmd2 &&
which might confuse the test author into thinking that the linter is
broken (since the line clearly ends with `&&`).
However, now that the ?!AMP?! an ?!SEMI?! annotations are inserted at
the point of breakage rather than at the beginning of the line, and
taking into account that both represent a broken &&-chain, there is
little reason to distinguish between the two. Using ?!AMP?! alone is
sufficient to point the test author at the problem. For instance, in:
cmd1; ?!AMP?! cmd2 &&
cmd3
it is clear that the &&-chain is broken between `cmd1` and `cmd2`.
Likewise, in:
cmd1 && cmd2 ?!AMP?!
cmd3
it is clear that the &&-chain is broken between `cmd2` and `cmd3`.
Finally, in:
cmd1; ?!AMP?! cmd2 ?!AMP?!
cmd3
it is clear that the &&-chain is broken between each command.
Hence, there is no longer a good reason to make a distinction between a
broken &&-chain due to a semicolon and a broken chain due to a missing
`&&` at end-of-line. Therefore, drop the ?!SEMI?! annotation and use
?!AMP?! exclusively.
Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-13 07:30:53 +01:00
|
|
|
s/\( ?!AMP?!\)* ?!AMP?!$//
|
2018-07-11 08:46:33 +02:00
|
|
|
x
|
|
|
|
# is 'done' or 'fi' cuddled with ")" to close subshell?
|
|
|
|
/done.*)/bclose
|
|
|
|
/fi.*)/bclose
|
2018-08-24 17:20:14 +02:00
|
|
|
bchkchn
|
2018-07-11 08:46:33 +02:00
|
|
|
|
|
|
|
# found nested multi-line "(...\n...)" -- pass through untouched
|
|
|
|
:nest
|
|
|
|
x
|
2020-09-10 04:17:41 +02:00
|
|
|
:nstslrp
|
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 <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-13 07:30:59 +01:00
|
|
|
s/^\([ ]*\)^/\1(/
|
2021-12-13 07:30:57 +01:00
|
|
|
s/?!HERE?!/<</g
|
2018-07-11 08:46:33 +02:00
|
|
|
n
|
chainlint.sed: swallow comments consistently
When checking for broken a &&-chain, chainlint.sed knows that the final
statement in a subshell should not end with `&&`, so it takes care to
make a distinction between the final line which is an actual statement
and any lines which may be mere comments preceding the closing ')'. As
such, it swallows comment lines so that they do not interfere with the
&&-chain check.
However, since `sed` does not provide any sort of real recursion,
chainlint.sed only checks &&-chains in subshells one level deep; it
doesn't do any checking in deeper subshells or in `{...}` blocks within
subshells. Furthermore, on account of potential implementation
complexity, it doesn't check &&-chains within `case` arms.
Due to an oversight, it also doesn't swallow comments inside deep
subshells, `{...}` blocks, or `case` statements, which makes its output
inconsistent (swallowing comments in some cases but not others).
Unfortunately, this inconsistency seeps 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. Therefore,
teach chainlint.sed to consistently swallow comments in all cases.
Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-13 07:30:58 +01:00
|
|
|
:nstcom
|
|
|
|
# comment -- not closing ")" if in comment
|
|
|
|
/^[ ]*#/{
|
|
|
|
N
|
|
|
|
s/.*\n//
|
|
|
|
bnstcom
|
|
|
|
}
|
2018-07-11 08:46:33 +02:00
|
|
|
# closing ")" on own line -- stop nested slurp
|
2020-09-10 04:17:41 +02:00
|
|
|
/^[ ]*)/bnstcl
|
2018-07-11 08:46:33 +02:00
|
|
|
# "$((...))" -- arithmetic expansion; not closing ")"
|
2018-08-24 17:20:14 +02:00
|
|
|
/\$(([^)][^)]*))[^)]*$/bnstcnt
|
2018-07-11 08:46:33 +02:00
|
|
|
# "$(...)" -- command substitution; not closing ")"
|
2018-08-24 17:20:14 +02:00
|
|
|
/\$([^)][^)]*)[^)]*$/bnstcnt
|
2018-07-11 08:46:33 +02:00
|
|
|
# closing "...)" -- stop nested slurp
|
2020-09-10 04:17:41 +02:00
|
|
|
/)/bnstcl
|
2018-08-24 17:20:14 +02:00
|
|
|
:nstcnt
|
2018-07-11 08:46:33 +02:00
|
|
|
x
|
2020-09-10 04:17:41 +02:00
|
|
|
bnstslrp
|
|
|
|
:nstcl
|
2018-07-11 08:46:33 +02:00
|
|
|
# is it "))" which closes nested and parent subshells?
|
|
|
|
/)[ ]*)/bslurp
|
2018-08-24 17:20:14 +02:00
|
|
|
bchkchn
|
2018-07-11 08:46:33 +02:00
|
|
|
|
|
|
|
# found multi-line "{...\n...}" block -- pass through untouched
|
|
|
|
:block
|
|
|
|
x
|
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 <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-13 07:30:59 +01:00
|
|
|
s/^\([ ]*\)^/\1(/
|
2021-12-13 07:30:57 +01:00
|
|
|
s/?!HERE?!/<</g
|
2018-07-11 08:46:33 +02:00
|
|
|
n
|
chainlint.sed: swallow comments consistently
When checking for broken a &&-chain, chainlint.sed knows that the final
statement in a subshell should not end with `&&`, so it takes care to
make a distinction between the final line which is an actual statement
and any lines which may be mere comments preceding the closing ')'. As
such, it swallows comment lines so that they do not interfere with the
&&-chain check.
However, since `sed` does not provide any sort of real recursion,
chainlint.sed only checks &&-chains in subshells one level deep; it
doesn't do any checking in deeper subshells or in `{...}` blocks within
subshells. Furthermore, on account of potential implementation
complexity, it doesn't check &&-chains within `case` arms.
Due to an oversight, it also doesn't swallow comments inside deep
subshells, `{...}` blocks, or `case` statements, which makes its output
inconsistent (swallowing comments in some cases but not others).
Unfortunately, this inconsistency seeps 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. Therefore,
teach chainlint.sed to consistently swallow comments in all cases.
Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-13 07:30:58 +01:00
|
|
|
:blkcom
|
|
|
|
/^[ ]*#/{
|
|
|
|
N
|
|
|
|
s/.*\n//
|
|
|
|
bblkcom
|
|
|
|
}
|
2018-07-11 08:46:33 +02:00
|
|
|
# closing "}" -- stop block slurp
|
2018-08-24 17:20:14 +02:00
|
|
|
/}/bchkchn
|
2018-07-11 08:46:33 +02:00
|
|
|
bblock
|
|
|
|
|
|
|
|
# found closing ")" on own line -- drop "suspect" from final line of subshell
|
|
|
|
# since that line legitimately lacks "&&" and exit subshell loop
|
2018-08-24 17:20:14 +02:00
|
|
|
:clssolo
|
2018-07-11 08:46:33 +02:00
|
|
|
x
|
chainlint.sed: drop unnecessary distinction between ?!AMP?! and ?!SEMI?!
>From inception, when chainlint.sed encountered a line using semicolon to
separate commands rather than `&&`, it would insert a ?!SEMI?!
annotation at the beginning of the line rather ?!AMP?! even though the
&&-chain is also broken by the semicolon. Given a line such as:
?!SEMI?! cmd1; cmd2 &&
the ?!SEMI?! annotation makes it easier to see what the problem is than
if the output had been:
?!AMP?! cmd1; cmd2 &&
which might confuse the test author into thinking that the linter is
broken (since the line clearly ends with `&&`).
However, now that the ?!AMP?! an ?!SEMI?! annotations are inserted at
the point of breakage rather than at the beginning of the line, and
taking into account that both represent a broken &&-chain, there is
little reason to distinguish between the two. Using ?!AMP?! alone is
sufficient to point the test author at the problem. For instance, in:
cmd1; ?!AMP?! cmd2 &&
cmd3
it is clear that the &&-chain is broken between `cmd1` and `cmd2`.
Likewise, in:
cmd1 && cmd2 ?!AMP?!
cmd3
it is clear that the &&-chain is broken between `cmd2` and `cmd3`.
Finally, in:
cmd1; ?!AMP?! cmd2 ?!AMP?!
cmd3
it is clear that the &&-chain is broken between each command.
Hence, there is no longer a good reason to make a distinction between a
broken &&-chain due to a semicolon and a broken chain due to a missing
`&&` at end-of-line. Therefore, drop the ?!SEMI?! annotation and use
?!AMP?! exclusively.
Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-13 07:30:53 +01:00
|
|
|
s/\( ?!AMP?!\)* ?!AMP?!$//
|
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 <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-13 07:30:59 +01:00
|
|
|
s/^\([ ]*\)^/\1(/
|
2021-12-13 07:30:57 +01:00
|
|
|
s/?!HERE?!/<</g
|
2018-07-11 08:46:33 +02:00
|
|
|
p
|
|
|
|
x
|
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 <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-13 07:30:59 +01:00
|
|
|
s/^\([ ]*\)^/\1(/
|
2021-12-13 07:30:57 +01:00
|
|
|
s/?!HERE?!/<</g
|
2018-07-11 08:46:33 +02:00
|
|
|
b
|
|
|
|
|
|
|
|
# found closing "...)" -- exit subshell loop
|
|
|
|
:close
|
|
|
|
x
|
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 <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-13 07:30:59 +01:00
|
|
|
s/^\([ ]*\)^/\1(/
|
2021-12-13 07:30:57 +01:00
|
|
|
s/?!HERE?!/<</g
|
2018-07-11 08:46:33 +02:00
|
|
|
p
|
|
|
|
x
|
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 <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-13 07:30:59 +01:00
|
|
|
s/^\([ ]*\)^/\1(/
|
2021-12-13 07:30:57 +01:00
|
|
|
s/?!HERE?!/<</g
|
2018-07-11 08:46:33 +02:00
|
|
|
b
|