This is a re-roll of [1] which teaches --chain-lint to detect broken &&-chains in subshells since the existing implementation[2] detects breakage only at the top-level. It is built atop 'es/test-fixes', which was split off of [1] and submitted separately[3], and which fixes many broken &&-chains. The major difference since v1 is that broken &&-chains in subshells are now detected by pure textual inspection rather than by merging subshell commands into the top-level &&-chain. Thus, v2 entirely sidesteps the primary objection[4] raised during v1 review of possible unintended side-effects of test code executing outside of the intended directory. The pure textual detection implemented by v2 can have no such side-effects. A second important difference since v1 is that the "linter" itself, a complex 'sed' script, now has its own tests to ensure correct behavior. Not only do the tests protect against regressions, but they help to document (for humans) expected behavior, which is important since 'sed' scripts can seem rather inscrutable due to looking like line-noise and due to the 'sed' "language" being stack-oriented (albeit with a very tiny stack) which, like other stack-oriented languages (Forth, Postscript, etc.) can be difficult to reason about. Although the 'sed' script in v1 was already well-commented, the comments have been improved in v2. More importantly, a high-level overview of the script's operation has been added at the top of the file to aid comprehension. Thanks to Elijah, Hannes, Jonathan Nieder, Jonathan Tan, Junio, Luke, Peff, and Stefan for comments on v1. [1]: https://public-inbox.org/git/20180626073001.6555-1-sunshine@xxxxxxxxxxxxxx/ [2]: https://public-inbox.org/git/20150320100429.GA17354@xxxxxxxx/ [3]: https://public-inbox.org/git/20180702002405.3042-1-sunshine@xxxxxxxxxxxxxx/ [4]: https://public-inbox.org/git/xmqqwouljr5e.fsf@xxxxxxxxxxxxxxxxxxxxxxxxx/ Eric Sunshine (10): t/test-lib: teach --chain-lint to detect broken &&-chains in subshells t/Makefile: add machinery to check correctness of chainlint.sed t/chainlint: add chainlint "basic" test cases t/chainlint: add chainlint "whitespace" test cases t/chainlint: add chainlint "one-liner" test cases t/chainlint: add chainlint "nested subshell" test cases t/chainlint: add chainlint "loop" and "conditional" test cases t/chainlint: add chainlint "cuddled" test cases t/chainlint: add chainlint "complex" test cases t/chainlint: add chainlint "specialized" test cases t/.gitignore | 1 + t/Makefile | 25 +- t/chainlint.sed | 346 ++++++++++++++++++ t/chainlint/arithmetic-expansion.expect | 9 + t/chainlint/arithmetic-expansion.test | 11 + t/chainlint/bash-array.expect | 10 + t/chainlint/bash-array.test | 12 + t/chainlint/blank-line.expect | 4 + t/chainlint/blank-line.test | 10 + t/chainlint/block.expect | 12 + t/chainlint/block.test | 15 + t/chainlint/broken-chain.expect | 6 + t/chainlint/broken-chain.test | 8 + t/chainlint/case.expect | 19 + t/chainlint/case.test | 23 ++ .../close-nested-and-parent-together.expect | 4 + .../close-nested-and-parent-together.test | 3 + t/chainlint/close-subshell.expect | 25 ++ t/chainlint/close-subshell.test | 27 ++ t/chainlint/command-substitution.expect | 9 + t/chainlint/command-substitution.test | 11 + t/chainlint/comment.expect | 4 + t/chainlint/comment.test | 11 + t/chainlint/complex-if-in-cuddled-loop.expect | 10 + t/chainlint/complex-if-in-cuddled-loop.test | 11 + t/chainlint/cuddled-if-then-else.expect | 7 + t/chainlint/cuddled-if-then-else.test | 7 + t/chainlint/cuddled-loop.expect | 5 + t/chainlint/cuddled-loop.test | 7 + t/chainlint/cuddled.expect | 21 ++ t/chainlint/cuddled.test | 23 ++ t/chainlint/exit-loop.expect | 24 ++ t/chainlint/exit-loop.test | 27 ++ t/chainlint/exit-subshell.expect | 5 + t/chainlint/exit-subshell.test | 6 + t/chainlint/for-loop.expect | 11 + t/chainlint/for-loop.test | 19 + t/chainlint/here-doc.expect | 3 + t/chainlint/here-doc.test | 16 + t/chainlint/if-in-loop.expect | 12 + t/chainlint/if-in-loop.test | 15 + t/chainlint/if-then-else.expect | 19 + t/chainlint/if-then-else.test | 28 ++ t/chainlint/incomplete-line.expect | 4 + t/chainlint/incomplete-line.test | 12 + t/chainlint/inline-comment.expect | 9 + t/chainlint/inline-comment.test | 12 + t/chainlint/loop-in-if.expect | 12 + t/chainlint/loop-in-if.test | 15 + ...ti-line-nested-command-substitution.expect | 9 + ...ulti-line-nested-command-substitution.test | 9 + t/chainlint/multi-line-string.expect | 9 + t/chainlint/multi-line-string.test | 15 + t/chainlint/negated-one-liner.expect | 5 + t/chainlint/negated-one-liner.test | 7 + t/chainlint/nested-cuddled-subshell.expect | 19 + t/chainlint/nested-cuddled-subshell.test | 31 ++ t/chainlint/nested-here-doc.expect | 5 + t/chainlint/nested-here-doc.test | 23 ++ t/chainlint/nested-subshell-comment.expect | 11 + t/chainlint/nested-subshell-comment.test | 13 + t/chainlint/nested-subshell.expect | 12 + t/chainlint/nested-subshell.test | 14 + t/chainlint/one-liner.expect | 9 + t/chainlint/one-liner.test | 12 + t/chainlint/p4-filespec.expect | 4 + t/chainlint/p4-filespec.test | 5 + t/chainlint/pipe.expect | 8 + t/chainlint/pipe.test | 12 + t/chainlint/semicolon.expect | 20 + t/chainlint/semicolon.test | 25 ++ t/chainlint/subshell-here-doc.expect | 5 + t/chainlint/subshell-here-doc.test | 23 ++ t/chainlint/subshell-one-liner.expect | 14 + t/chainlint/subshell-one-liner.test | 24 ++ t/chainlint/while-loop.expect | 11 + t/chainlint/while-loop.test | 19 + t/test-lib.sh | 3 +- 78 files changed, 1316 insertions(+), 5 deletions(-) create mode 100644 t/chainlint.sed create mode 100644 t/chainlint/arithmetic-expansion.expect create mode 100644 t/chainlint/arithmetic-expansion.test create mode 100644 t/chainlint/bash-array.expect create mode 100644 t/chainlint/bash-array.test create mode 100644 t/chainlint/blank-line.expect create mode 100644 t/chainlint/blank-line.test create mode 100644 t/chainlint/block.expect create mode 100644 t/chainlint/block.test create mode 100644 t/chainlint/broken-chain.expect create mode 100644 t/chainlint/broken-chain.test create mode 100644 t/chainlint/case.expect create mode 100644 t/chainlint/case.test create mode 100644 t/chainlint/close-nested-and-parent-together.expect create mode 100644 t/chainlint/close-nested-and-parent-together.test create mode 100644 t/chainlint/close-subshell.expect create mode 100644 t/chainlint/close-subshell.test create mode 100644 t/chainlint/command-substitution.expect create mode 100644 t/chainlint/command-substitution.test create mode 100644 t/chainlint/comment.expect create mode 100644 t/chainlint/comment.test create mode 100644 t/chainlint/complex-if-in-cuddled-loop.expect create mode 100644 t/chainlint/complex-if-in-cuddled-loop.test create mode 100644 t/chainlint/cuddled-if-then-else.expect create mode 100644 t/chainlint/cuddled-if-then-else.test create mode 100644 t/chainlint/cuddled-loop.expect create mode 100644 t/chainlint/cuddled-loop.test create mode 100644 t/chainlint/cuddled.expect create mode 100644 t/chainlint/cuddled.test create mode 100644 t/chainlint/exit-loop.expect create mode 100644 t/chainlint/exit-loop.test create mode 100644 t/chainlint/exit-subshell.expect create mode 100644 t/chainlint/exit-subshell.test create mode 100644 t/chainlint/for-loop.expect create mode 100644 t/chainlint/for-loop.test create mode 100644 t/chainlint/here-doc.expect create mode 100644 t/chainlint/here-doc.test create mode 100644 t/chainlint/if-in-loop.expect create mode 100644 t/chainlint/if-in-loop.test create mode 100644 t/chainlint/if-then-else.expect create mode 100644 t/chainlint/if-then-else.test create mode 100644 t/chainlint/incomplete-line.expect create mode 100644 t/chainlint/incomplete-line.test create mode 100644 t/chainlint/inline-comment.expect create mode 100644 t/chainlint/inline-comment.test create mode 100644 t/chainlint/loop-in-if.expect create mode 100644 t/chainlint/loop-in-if.test create mode 100644 t/chainlint/multi-line-nested-command-substitution.expect create mode 100644 t/chainlint/multi-line-nested-command-substitution.test create mode 100644 t/chainlint/multi-line-string.expect create mode 100644 t/chainlint/multi-line-string.test create mode 100644 t/chainlint/negated-one-liner.expect create mode 100644 t/chainlint/negated-one-liner.test create mode 100644 t/chainlint/nested-cuddled-subshell.expect create mode 100644 t/chainlint/nested-cuddled-subshell.test create mode 100644 t/chainlint/nested-here-doc.expect create mode 100644 t/chainlint/nested-here-doc.test create mode 100644 t/chainlint/nested-subshell-comment.expect create mode 100644 t/chainlint/nested-subshell-comment.test create mode 100644 t/chainlint/nested-subshell.expect create mode 100644 t/chainlint/nested-subshell.test create mode 100644 t/chainlint/one-liner.expect create mode 100644 t/chainlint/one-liner.test create mode 100644 t/chainlint/p4-filespec.expect create mode 100644 t/chainlint/p4-filespec.test create mode 100644 t/chainlint/pipe.expect create mode 100644 t/chainlint/pipe.test create mode 100644 t/chainlint/semicolon.expect create mode 100644 t/chainlint/semicolon.test create mode 100644 t/chainlint/subshell-here-doc.expect create mode 100644 t/chainlint/subshell-here-doc.test create mode 100644 t/chainlint/subshell-one-liner.expect create mode 100644 t/chainlint/subshell-one-liner.test create mode 100644 t/chainlint/while-loop.expect create mode 100644 t/chainlint/while-loop.test -- 2.18.0.203.gfac676dfb9