Git fork

chainlint: make error messages self-explanatory

The annotations emitted by chainlint to indicate detected problems are
overly terse, so much so that developers new to the project -- those who
should most benefit from the linting -- may find them baffling. For
instance, although the author of chainlint and seasoned Git developers
may understand that "?!AMP?!" is an abbreviation of "ampersand" and
indicates a break in the &&-chain, this may not be obvious to newcomers.

The "?!LOOP?!" case is particularly serious because that terse single
word does nothing to convey that the loop body should end with
"|| return 1" (or "|| exit 1" in a subshell) to ensure that a failing
command in the body aborts the loop immediately. Moreover, unlike
&&-chaining which is ubiquitous in Git tests, the "|| return 1" idiom is
relatively infrequent, thus may be harder for a newcomer to discover by
consulting nearby code.

Address these shortcomings by emitting human-readable messages which
both explain the problem and give a strong hint about how to correct it.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>

authored by

Eric Sunshine and committed by
Junio C Hamano
e44f15ba 588ef84e

+104 -90
+22 -8
t/chainlint.pl
··· 9 9 # Input arguments are pathnames of shell scripts containing test definitions, 10 10 # or globs referencing a collection of scripts. For each problem discovered, 11 11 # the pathname of the script containing the test is printed along with the test 12 - # name and the test body with a `?!FOO?!` annotation at the location of each 13 - # detected problem, where "FOO" is a tag such as "AMP" which indicates a broken 14 - # &&-chain. Returns zero if no problems are discovered, otherwise non-zero. 12 + # name and the test body with a `?!LINT: ...?!` annotation at the location of 13 + # each detected problem, where "..." is an explanation of the problem. Returns 14 + # zero if no problems are discovered, otherwise non-zero. 15 15 16 16 use warnings; 17 17 use strict; ··· 181 181 $self->{lineno} += () = $body =~ /\n/sg; 182 182 next; 183 183 } 184 - push(@{$self->{parser}->{problems}}, ['UNCLOSED-HEREDOC', $tag]); 184 + push(@{$self->{parser}->{problems}}, ['HEREDOC', $tag]); 185 185 $$b =~ /(?:\G|\n).*\z/gc; # consume rest of input 186 186 my $body = substr($$b, $start, pos($$b) - $start); 187 187 $self->{lineno} += () = $body =~ /\n/sg; ··· 238 238 stop => [], 239 239 output => [], 240 240 heredocs => {}, 241 + insubshell => 0, 241 242 } => $class; 242 243 $self->{lexer} = Lexer->new($self, $s); 243 244 return $self; ··· 296 297 297 298 sub parse_subshell { 298 299 my $self = shift @_; 299 - return ($self->parse(qr/^\)$/), 300 - $self->expect(')')); 300 + $self->{insubshell}++; 301 + my @tokens = ($self->parse(qr/^\)$/), 302 + $self->expect(')')); 303 + $self->{insubshell}--; 304 + return @tokens; 301 305 } 302 306 303 307 sub parse_case_pattern { ··· 528 532 return @tokens if ends_with(\@tokens, [qr/^\|\|$/, "\n", qr/^echo$/, qr/^.+$/]); 529 533 # flag missing "return/exit" handling explicit failure in loop body 530 534 my $n = find_non_nl(\@tokens); 531 - push(@{$self->{problems}}, ['LOOP', $tokens[$n]]); 535 + push(@{$self->{problems}}, [$self->{insubshell} ? 'LOOPEXIT' : 'LOOPRETURN', $tokens[$n]]); 532 536 return @tokens; 533 537 } 534 538 ··· 620 624 return $s 621 625 } 622 626 627 + sub format_problem { 628 + local $_ = shift; 629 + /^AMP$/ && return "missing '&&'"; 630 + /^LOOPRETURN$/ && return "missing '|| return 1'"; 631 + /^LOOPEXIT$/ && return "missing '|| exit 1'"; 632 + /^HEREDOC$/ && return 'unclosed heredoc'; 633 + die("unrecognized problem type '$_'\n"); 634 + } 635 + 623 636 sub check_test { 624 637 my $self = shift @_; 625 638 my $title = unwrap(shift @_); ··· 643 656 for (sort {$a->[1]->[2] <=> $b->[1]->[2]} @$problems) { 644 657 my ($label, $token) = @$_; 645 658 my $pos = $token->[2]; 659 + my $err = format_problem($label); 646 660 $checked .= substr($body, $start, $pos - $start); 647 661 $checked .= ' ' unless $checked =~ /\s$/; 648 - $checked .= "$c->{rev}$c->{red}?!$label?!$c->{reset}"; 662 + $checked .= "$c->{rev}$c->{red}?!LINT: $err?!$c->{reset}"; 649 663 $checked .= ' ' unless $pos >= length($body) || 650 664 substr($body, $pos, 1) =~ /^\s/; 651 665 $start = $pos;
+1 -1
t/chainlint/arithmetic-expansion.expect
··· 4 4 5 baz 5 5 6 ) && 6 6 7 ( 7 - 8 bar=$((42 + 1)) ?!AMP?! 7 + 8 bar=$((42 + 1)) ?!LINT: missing '&&'?! 8 8 9 baz 9 9 10 )
+4 -4
t/chainlint/block.expect
··· 1 1 2 ( 2 2 3 foo && 3 3 4 { 4 - 5 echo a ?!AMP?! 4 + 5 echo a ?!LINT: missing '&&'?! 5 5 6 echo b 6 6 7 } && 7 7 8 bar && 8 8 9 { 9 9 10 echo c 10 - 11 } ?!AMP?! 10 + 11 } ?!LINT: missing '&&'?! 11 11 12 baz 12 12 13 ) && 13 13 14 14 14 15 { 15 - 16 echo a; ?!AMP?! echo b 15 + 16 echo a; ?!LINT: missing '&&'?! echo b 16 16 17 } && 17 - 18 { echo a; ?!AMP?! echo b; } && 17 + 18 { echo a; ?!LINT: missing '&&'?! echo b; } && 18 18 19 19 19 20 { 20 20 21 echo "${var}9" &&
+1 -1
t/chainlint/broken-chain.expect
··· 1 1 2 ( 2 2 3 foo && 3 - 4 bar ?!AMP?! 3 + 4 bar ?!LINT: missing '&&'?! 4 4 5 baz && 5 5 6 wop 6 6 7 )
+2 -2
t/chainlint/case.expect
··· 9 9 10 case "$x" in 10 10 11 x) foo ;; 11 11 12 *) bar ;; 12 - 13 esac ?!AMP?! 12 + 13 esac ?!LINT: missing '&&'?! 13 13 14 foobar 14 14 15 ) && 15 15 16 ( 16 16 17 case "$x" in 1) true;; esac && 17 - 18 case "$y" in 2) false;; esac ?!AMP?! 17 + 18 case "$y" in 2) false;; esac ?!LINT: missing '&&'?! 18 18 19 foobar 19 19 20 )
+1 -1
t/chainlint/chain-break-false.expect
··· 4 4 5 echo failed! 5 5 6 false 6 6 7 else 7 - 8 echo it went okay ?!AMP?! 7 + 8 echo it went okay ?!LINT: missing '&&'?! 8 8 9 congratulate user 9 9 10 fi
+1 -1
t/chainlint/chained-block.expect
··· 1 1 2 echo nobody home && { 2 - 3 test the doohicky ?!AMP?! 2 + 3 test the doohicky ?!LINT: missing '&&'?! 3 3 4 right now 4 4 5 } && 5 5 6
+2 -2
t/chainlint/chained-subshell.expect
··· 1 1 2 mkdir sub && ( 2 2 3 cd sub && 3 - 4 foo the bar ?!AMP?! 3 + 4 foo the bar ?!LINT: missing '&&'?! 4 4 5 nuff said 5 5 6 ) && 6 6 7 7 7 8 cut "-d " -f actual | (read s1 s2 s3 && 8 - 9 test -f $s1 ?!AMP?! 8 + 9 test -f $s1 ?!LINT: missing '&&'?! 9 9 10 test $(cat $s2) = tree2path1 && 10 10 11 test $(cat $s3) = tree3path1)
+1 -1
t/chainlint/command-substitution.expect
··· 4 4 5 baz 5 5 6 ) && 6 6 7 ( 7 - 8 bar=$(gobble blocks) ?!AMP?! 7 + 8 bar=$(gobble blocks) ?!LINT: missing '&&'?! 8 8 9 baz 9 9 10 )
+1 -1
t/chainlint/complex-if-in-cuddled-loop.expect
··· 4 4 5 : 5 5 6 else 6 6 7 echo >file 7 - 8 fi ?!LOOP?! 7 + 8 fi ?!LINT: missing '|| exit 1'?! 8 8 9 done) && 9 9 10 test ! -f file
+2 -2
t/chainlint/cuddled.expect
··· 2 2 3 bar 3 3 4 ) && 4 4 5 5 - 6 (cd foo ?!AMP?! 5 + 6 (cd foo ?!LINT: missing '&&'?! 6 6 7 bar 7 7 8 ) && 8 8 9 ··· 13 13 14 (cd foo && 14 14 15 bar) && 15 15 16 16 - 17 (cd foo ?!AMP?! 16 + 17 (cd foo ?!LINT: missing '&&'?! 17 17 18 bar)
+4 -4
t/chainlint/for-loop.expect
··· 1 1 2 ( 2 2 3 for i in a b c 3 3 4 do 4 - 5 echo $i ?!AMP?! 5 - 6 cat <<-\EOF ?!LOOP?! 4 + 5 echo $i ?!LINT: missing '&&'?! 5 + 6 cat <<-\EOF ?!LINT: missing '|| exit 1'?! 6 6 7 bar 7 7 8 EOF 8 - 9 done ?!AMP?! 8 + 9 done ?!LINT: missing '&&'?! 9 9 10 10 10 11 for i in a b c; do 11 11 12 echo $i && 12 - 13 cat $i ?!LOOP?! 12 + 13 cat $i ?!LINT: missing '|| exit 1'?! 13 13 14 done 14 14 15 )
+2 -2
t/chainlint/function.expect
··· 4 4 5 5 5 6 remove_object() { 6 6 7 file=$(sha1_file "$*") && 7 - 8 test -e "$file" ?!AMP?! 7 + 8 test -e "$file" ?!LINT: missing '&&'?! 8 8 9 rm -f "$file" 9 - 10 } ?!AMP?! 9 + 10 } ?!LINT: missing '&&'?! 10 10 11 11 11 12 sha1_file arg && remove_object arg
+1 -1
t/chainlint/here-doc-body-indent.expect
··· 1 - 2 echo "we should find this" ?!AMP?! 1 + 2 echo "we should find this" ?!LINT: missing '&&'?! 2 2 3 echo "even though our heredoc has its indent stripped"
+2 -2
t/chainlint/here-doc-body-pathological.expect
··· 1 - 2 echo "outer here-doc does not allow indented end-tag" ?!AMP?! 1 + 2 echo "outer here-doc does not allow indented end-tag" ?!LINT: missing '&&'?! 2 2 3 cat >file <<-\EOF && 3 3 4 but this inner here-doc 4 4 5 does allow indented EOF 5 5 6 EOF 6 - 7 echo "missing chain after" ?!AMP?! 6 + 7 echo "missing chain after" ?!LINT: missing '&&'?! 7 7 8 echo "but this line is OK because it's the end"
+2 -2
t/chainlint/here-doc-body.expect
··· 1 - 2 echo "missing chain before" ?!AMP?! 1 + 2 echo "missing chain before" ?!LINT: missing '&&'?! 2 2 3 cat >file <<-\EOF && 3 3 4 inside inner here-doc 4 4 5 these are not shell commands 5 5 6 EOF 6 - 7 echo "missing chain after" ?!AMP?! 6 + 7 echo "missing chain after" ?!LINT: missing '&&'?! 7 7 8 echo "but this line is OK because it's the end"
+1 -1
t/chainlint/here-doc-double.expect
··· 1 - 8 echo "actual test commands" ?!AMP?! 1 + 8 echo "actual test commands" ?!LINT: missing '&&'?! 2 2 9 echo "that should be checked"
+1 -1
t/chainlint/here-doc-indent-operator.expect
··· 4 4 5 chunks: oid_fanout oid_lookup commit_metadata generation_data bloom_indexes bloom_data 5 5 6 EOF 6 6 7 7 - 8 cat >expect << -EOF ?!AMP?! 7 + 8 cat >expect << -EOF ?!LINT: missing '&&'?! 8 8 9 this is not indented 9 9 10 -EOF 10 10 11
+1 -1
t/chainlint/here-doc-multi-line-command-subst.expect
··· 3 3 4 fossil 4 4 5 vegetable 5 5 6 END 6 - 7 wiffle) ?!AMP?! 6 + 7 wiffle) ?!LINT: missing '&&'?! 7 7 8 echo $x 8 8 9 )
+1 -1
t/chainlint/here-doc-multi-line-string.expect
··· 1 1 2 ( 2 2 3 cat <<-\TXT && echo "multi-line 3 - 4 string" ?!AMP?! 3 + 4 string" ?!LINT: missing '&&'?! 4 4 5 fizzle 5 5 6 TXT 6 6 7 bap
+1 -1
t/chainlint/if-condition-split.expect
··· 2 2 3 marcia || 3 3 4 kevin 4 4 5 then 5 - 6 echo "nomads" ?!AMP?! 5 + 6 echo "nomads" ?!LINT: missing '&&'?! 6 6 7 echo "for sure" 7 7 8 fi
+2 -2
t/chainlint/if-in-loop.expect
··· 5 5 6 then 6 6 7 echo "err" 7 7 8 exit 1 8 - 9 fi ?!AMP?! 8 + 9 fi ?!LINT: missing '&&'?! 9 9 10 foo 10 - 11 done ?!AMP?! 10 + 11 done ?!LINT: missing '&&'?! 11 11 12 bar 12 12 13 )
+2 -2
t/chainlint/if-then-else.expect
··· 1 1 2 ( 2 2 3 if test -n "" 3 3 4 then 4 - 5 echo very ?!AMP?! 4 + 5 echo very ?!LINT: missing '&&'?! 5 5 6 echo empty 6 6 7 elif test -z "" 7 7 8 then ··· 11 11 12 cat <<-\EOF 12 12 13 bar 13 13 14 EOF 14 - 15 fi ?!AMP?! 14 + 15 fi ?!LINT: missing '&&'?! 15 15 16 echo poodle 16 16 17 ) && 17 17 18 (
+1 -1
t/chainlint/inline-comment.expect
··· 1 1 2 ( 2 2 3 foobar && # comment 1 3 - 4 barfoo ?!AMP?! # wrong position for && 3 + 4 barfoo ?!LINT: missing '&&'?! # wrong position for && 4 4 5 flibble "not a # comment" 5 5 6 ) && 6 6 7
+1 -1
t/chainlint/loop-detect-failure.expect
··· 11 11 12 do 12 12 13 printf "%"$n"s" X > r2/large.$n && 13 13 14 git -C r2 add large.$n && 14 - 15 git -C r2 commit -m "$n" ?!LOOP?! 14 + 15 git -C r2 commit -m "$n" ?!LINT: missing '|| return 1'?! 15 15 16 done
+4 -4
t/chainlint/loop-in-if.expect
··· 3 3 4 then 4 4 5 while true 5 5 6 do 6 - 7 echo "pop" ?!AMP?! 7 - 8 echo "glup" ?!LOOP?! 8 - 9 done ?!AMP?! 6 + 7 echo "pop" ?!LINT: missing '&&'?! 7 + 8 echo "glup" ?!LINT: missing '|| exit 1'?! 8 + 9 done ?!LINT: missing '&&'?! 9 9 10 foo 10 - 11 fi ?!AMP?! 10 + 11 fi ?!LINT: missing '&&'?! 11 11 12 bar 12 12 13 )
+1 -1
t/chainlint/multi-line-string.expect
··· 3 3 4 line 2 4 4 5 line 3" && 5 5 6 y="line 1 6 - 7 line2" ?!AMP?! 6 + 7 line2" ?!LINT: missing '&&'?! 7 7 8 foobar 8 8 9 ) && 9 9 10 (
+2 -2
t/chainlint/negated-one-liner.expect
··· 1 1 2 ! (foo && bar) && 2 2 3 ! (foo && bar) >baz && 3 3 4 4 - 5 ! (foo; ?!AMP?! bar) && 5 - 6 ! (foo; ?!AMP?! bar) >baz 4 + 5 ! (foo; ?!LINT: missing '&&'?! bar) && 5 + 6 ! (foo; ?!LINT: missing '&&'?! bar) >baz
+3 -3
t/chainlint/nested-cuddled-subshell.expect
··· 5 5 6 6 6 7 (cd foo && 7 7 8 bar 8 - 9 ) ?!AMP?! 8 + 9 ) ?!LINT: missing '&&'?! 9 9 10 10 10 11 ( 11 11 12 cd foo && ··· 13 13 14 14 14 15 ( 15 15 16 cd foo && 16 - 17 bar) ?!AMP?! 16 + 17 bar) ?!LINT: missing '&&'?! 17 17 18 18 18 19 (cd foo && 19 19 20 bar) && 20 20 21 21 21 22 (cd foo && 22 - 23 bar) ?!AMP?! 22 + 23 bar) ?!LINT: missing '&&'?! 23 23 24 24 24 25 foobar 25 25 26 )
+1 -1
t/chainlint/nested-here-doc.expect
··· 18 18 19 toink 19 19 20 INPUT_END 20 20 21 21 - 22 cat <<-\EOT ?!AMP?! 21 + 22 cat <<-\EOT ?!LINT: missing '&&'?! 22 22 23 text goes here 23 23 24 data <<EOF 24 24 25 data goes here
+3 -3
t/chainlint/nested-loop-detect-failure.expect
··· 2 2 3 do 3 3 4 for j in 0 1 2 3 4 5 6 7 8 9; 4 4 5 do 5 - 6 echo "$i$j" >"path$i$j" ?!LOOP?! 6 - 7 done ?!LOOP?! 5 + 6 echo "$i$j" >"path$i$j" ?!LINT: missing '|| return 1'?! 6 + 7 done ?!LINT: missing '|| return 1'?! 7 7 8 done && 8 8 9 9 9 10 for i in 0 1 2 3 4 5 6 7 8 9; ··· 18 18 19 do 19 19 20 for j in 0 1 2 3 4 5 6 7 8 9; 20 20 21 do 21 - 22 echo "$i$j" >"path$i$j" ?!LOOP?! 21 + 22 echo "$i$j" >"path$i$j" ?!LINT: missing '|| return 1'?! 22 22 23 done || return 1 23 23 24 done && 24 24 25
+1 -1
t/chainlint/nested-subshell-comment.expect
··· 6 6 7 # minor numbers of cows (or do they?) 7 7 8 baz && 8 8 9 snaff 9 - 10 ) ?!AMP?! 9 + 10 ) ?!LINT: missing '&&'?! 10 10 11 fuzzy 11 11 12 )
+1 -1
t/chainlint/nested-subshell.expect
··· 7 7 8 8 8 9 cd foo && 9 9 10 ( 10 - 11 echo a ?!AMP?! 10 + 11 echo a ?!LINT: missing '&&'?! 11 11 12 echo b 12 12 13 ) >file 13 13 14 )
+1 -1
t/chainlint/not-heredoc.expect
··· 9 9 10 echo ourside && 10 10 11 echo "=======" && 11 11 12 echo theirside && 12 - 13 echo ">>>>>>> theirs" ?!AMP?! 12 + 13 echo ">>>>>>> theirs" ?!LINT: missing '&&'?! 13 13 14 poodle 14 14 15 ) >merged
+1 -1
t/chainlint/one-liner-for-loop.expect
··· 3 3 4 cd dir-rename-and-content && 4 4 5 test_write_lines 1 2 3 4 5 >foo && 5 5 6 mkdir olddir && 6 - 7 for i in a b c; do echo $i >olddir/$i; ?!LOOP?! done ?!AMP?! 6 + 7 for i in a b c; do echo $i >olddir/$i; ?!LINT: missing '|| exit 1'?! done ?!LINT: missing '&&'?! 7 7 8 git add foo olddir && 8 8 9 git commit -m "original" && 9 9 10 )
+3 -3
t/chainlint/one-liner.expect
··· 2 2 3 (foo && bar) | 3 3 4 (foo && bar) >baz && 4 4 5 5 - 6 (foo; ?!AMP?! bar) && 6 - 7 (foo; ?!AMP?! bar) | 7 - 8 (foo; ?!AMP?! bar) >baz && 5 + 6 (foo; ?!LINT: missing '&&'?! bar) && 6 + 7 (foo; ?!LINT: missing '&&'?! bar) | 7 + 8 (foo; ?!LINT: missing '&&'?! bar) >baz && 8 8 9 9 9 10 (foo "bar; baz")
+1 -1
t/chainlint/pipe.expect
··· 4 4 5 baz && 5 5 6 6 6 7 fish | 7 - 8 cow ?!AMP?! 7 + 8 cow ?!LINT: missing '&&'?! 8 8 9 9 9 10 sunder 10 10 11 )
+6 -6
t/chainlint/semicolon.expect
··· 1 1 2 ( 2 - 3 cat foo ; ?!AMP?! echo bar ?!AMP?! 3 - 4 cat foo ; ?!AMP?! echo bar 2 + 3 cat foo ; ?!LINT: missing '&&'?! echo bar ?!LINT: missing '&&'?! 3 + 4 cat foo ; ?!LINT: missing '&&'?! echo bar 4 4 5 ) && 5 5 6 ( 6 - 7 cat foo ; ?!AMP?! echo bar && 7 - 8 cat foo ; ?!AMP?! echo bar 6 + 7 cat foo ; ?!LINT: missing '&&'?! echo bar && 7 + 8 cat foo ; ?!LINT: missing '&&'?! echo bar 8 8 9 ) && 9 9 10 ( 10 10 11 echo "foo; bar" && 11 - 12 cat foo; ?!AMP?! echo bar 11 + 12 cat foo; ?!LINT: missing '&&'?! echo bar 12 12 13 ) && 13 13 14 ( 14 14 15 foo; 15 15 16 ) && 16 16 17 (cd foo && 17 17 18 for i in a b c; do 18 - 19 echo; ?!LOOP?! 18 + 19 echo; ?!LINT: missing '|| exit 1'?! 19 19 20 done)
+1 -1
t/chainlint/subshell-here-doc.expect
··· 6 6 7 nevermore... 7 7 8 EOF 8 8 9 9 - 10 cat <<EOF >bip ?!AMP?! 9 + 10 cat <<EOF >bip ?!LINT: missing '&&'?! 10 10 11 fish fly high 11 11 12 EOF 12 12 13
+5 -5
t/chainlint/subshell-one-liner.expect
··· 3 3 4 (foo && bar) | 4 4 5 (foo && bar) >baz && 5 5 6 6 - 7 (foo; ?!AMP?! bar) && 7 - 8 (foo; ?!AMP?! bar) | 8 - 9 (foo; ?!AMP?! bar) >baz && 6 + 7 (foo; ?!LINT: missing '&&'?! bar) && 7 + 8 (foo; ?!LINT: missing '&&'?! bar) | 8 + 9 (foo; ?!LINT: missing '&&'?! bar) >baz && 9 9 10 10 10 11 (foo || exit 1) && 11 11 12 (foo || exit 1) | 12 12 13 (foo || exit 1) >baz && 13 13 14 14 - 15 (foo && bar) ?!AMP?! 14 + 15 (foo && bar) ?!LINT: missing '&&'?! 15 15 16 16 - 17 (foo && bar; ?!AMP?! baz) ?!AMP?! 16 + 17 (foo && bar; ?!LINT: missing '&&'?! baz) ?!LINT: missing '&&'?! 17 17 18 18 18 19 foobar 19 19 20 )
+4 -4
t/chainlint/token-pasting.expect
··· 2 2 3 git config filter.rot13.clean ./rot13.sh && 3 3 4 4 4 5 { 5 - 6 echo "*.t filter=rot13" ?!AMP?! 5 + 6 echo "*.t filter=rot13" ?!LINT: missing '&&'?! 6 6 7 echo "*.i ident" 7 7 8 } >.gitattributes && 8 8 9 9 9 10 { 10 - 11 echo a b c d e f g h i j k l m ?!AMP?! 11 - 12 echo n o p q r s t u v w x y z ?!AMP?! 10 + 11 echo a b c d e f g h i j k l m ?!LINT: missing '&&'?! 11 + 12 echo n o p q r s t u v w x y z ?!LINT: missing '&&'?! 12 12 13 echo '$Id$' 13 13 14 } >test && 14 14 15 cat test >test.t && ··· 19 19 20 git checkout -- test test.t test.i && 20 20 21 21 21 22 echo "content-test2" >test2.o && 22 - 23 echo "content-test3 - filename with special characters" >"test3 'sq',$x=.o" ?!AMP?! 22 + 23 echo "content-test3 - filename with special characters" >"test3 'sq',$x=.o" ?!LINT: missing '&&'?! 23 23 24 24 24 25 downstream_url_for_sed=$( 25 25 26 printf "%sn" "$downstream_url" |
+1 -1
t/chainlint/unclosed-here-doc-indent.expect
··· 1 1 2 command_which_is_run && 2 - 3 cat >expect <<-\EOF ?!UNCLOSED-HEREDOC?! && 2 + 3 cat >expect <<-\EOF ?!LINT: unclosed heredoc?! && 3 3 4 we forget to end the here-doc 4 4 5 command_which_is_gobbled
+1 -1
t/chainlint/unclosed-here-doc.expect
··· 1 1 2 command_which_is_run && 2 - 3 cat >expect <<\EOF ?!UNCLOSED-HEREDOC?! && 2 + 3 cat >expect <<\EOF ?!LINT: unclosed heredoc?! && 3 3 4 we try to end the here-doc below, 4 4 5 but the indentation throws us off 5 5 6 since the operator is not "<<-".
+4 -4
t/chainlint/while-loop.expect
··· 1 1 2 ( 2 2 3 while true 3 3 4 do 4 - 5 echo foo ?!AMP?! 5 - 6 cat <<-\EOF ?!LOOP?! 4 + 5 echo foo ?!LINT: missing '&&'?! 5 + 6 cat <<-\EOF ?!LINT: missing '|| exit 1'?! 6 6 7 bar 7 7 8 EOF 8 - 9 done ?!AMP?! 8 + 9 done ?!LINT: missing '&&'?! 9 9 10 10 10 11 while true; do 11 11 12 echo foo && 12 - 13 cat bar ?!LOOP?! 12 + 13 cat bar ?!LINT: missing '|| exit 1'?! 13 13 14 done 14 14 15 )