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>
maint
Eric Sunshine 2024-09-10 00:10:12 -04:00 committed by Junio C Hamano
parent 588ef84ece
commit e44f15ba3e
44 changed files with 104 additions and 90 deletions

View File

@ -9,9 +9,9 @@
# Input arguments are pathnames of shell scripts containing test definitions, # Input arguments are pathnames of shell scripts containing test definitions,
# or globs referencing a collection of scripts. For each problem discovered, # or globs referencing a collection of scripts. For each problem discovered,
# the pathname of the script containing the test is printed along with the test # the pathname of the script containing the test is printed along with the test
# name and the test body with a `?!FOO?!` annotation at the location of each # name and the test body with a `?!LINT: ...?!` annotation at the location of
# detected problem, where "FOO" is a tag such as "AMP" which indicates a broken # each detected problem, where "..." is an explanation of the problem. Returns
# &&-chain. Returns zero if no problems are discovered, otherwise non-zero. # zero if no problems are discovered, otherwise non-zero.


use warnings; use warnings;
use strict; use strict;
@ -181,7 +181,7 @@ sub swallow_heredocs {
$self->{lineno} += () = $body =~ /\n/sg; $self->{lineno} += () = $body =~ /\n/sg;
next; next;
} }
push(@{$self->{parser}->{problems}}, ['UNCLOSED-HEREDOC', $tag]); push(@{$self->{parser}->{problems}}, ['HEREDOC', $tag]);
$$b =~ /(?:\G|\n).*\z/gc; # consume rest of input $$b =~ /(?:\G|\n).*\z/gc; # consume rest of input
my $body = substr($$b, $start, pos($$b) - $start); my $body = substr($$b, $start, pos($$b) - $start);
$self->{lineno} += () = $body =~ /\n/sg; $self->{lineno} += () = $body =~ /\n/sg;
@ -238,6 +238,7 @@ sub new {
stop => [], stop => [],
output => [], output => [],
heredocs => {}, heredocs => {},
insubshell => 0,
} => $class; } => $class;
$self->{lexer} = Lexer->new($self, $s); $self->{lexer} = Lexer->new($self, $s);
return $self; return $self;
@ -296,8 +297,11 @@ sub parse_group {


sub parse_subshell { sub parse_subshell {
my $self = shift @_; my $self = shift @_;
return ($self->parse(qr/^\)$/), $self->{insubshell}++;
$self->expect(')')); my @tokens = ($self->parse(qr/^\)$/),
$self->expect(')'));
$self->{insubshell}--;
return @tokens;
} }


sub parse_case_pattern { sub parse_case_pattern {
@ -528,7 +532,7 @@ sub parse_loop_body {
return @tokens if ends_with(\@tokens, [qr/^\|\|$/, "\n", qr/^echo$/, qr/^.+$/]); return @tokens if ends_with(\@tokens, [qr/^\|\|$/, "\n", qr/^echo$/, qr/^.+$/]);
# flag missing "return/exit" handling explicit failure in loop body # flag missing "return/exit" handling explicit failure in loop body
my $n = find_non_nl(\@tokens); my $n = find_non_nl(\@tokens);
push(@{$self->{problems}}, ['LOOP', $tokens[$n]]); push(@{$self->{problems}}, [$self->{insubshell} ? 'LOOPEXIT' : 'LOOPRETURN', $tokens[$n]]);
return @tokens; return @tokens;
} }


@ -620,6 +624,15 @@ sub unwrap {
return $s return $s
} }


sub format_problem {
local $_ = shift;
/^AMP$/ && return "missing '&&'";
/^LOOPRETURN$/ && return "missing '|| return 1'";
/^LOOPEXIT$/ && return "missing '|| exit 1'";
/^HEREDOC$/ && return 'unclosed heredoc';
die("unrecognized problem type '$_'\n");
}

sub check_test { sub check_test {
my $self = shift @_; my $self = shift @_;
my $title = unwrap(shift @_); my $title = unwrap(shift @_);
@ -643,9 +656,10 @@ sub check_test {
for (sort {$a->[1]->[2] <=> $b->[1]->[2]} @$problems) { for (sort {$a->[1]->[2] <=> $b->[1]->[2]} @$problems) {
my ($label, $token) = @$_; my ($label, $token) = @$_;
my $pos = $token->[2]; my $pos = $token->[2];
my $err = format_problem($label);
$checked .= substr($body, $start, $pos - $start); $checked .= substr($body, $start, $pos - $start);
$checked .= ' ' unless $checked =~ /\s$/; $checked .= ' ' unless $checked =~ /\s$/;
$checked .= "$c->{rev}$c->{red}?!$label?!$c->{reset}"; $checked .= "$c->{rev}$c->{red}?!LINT: $err?!$c->{reset}";
$checked .= ' ' unless $pos >= length($body) || $checked .= ' ' unless $pos >= length($body) ||
substr($body, $pos, 1) =~ /^\s/; substr($body, $pos, 1) =~ /^\s/;
$start = $pos; $start = $pos;

View File

@ -4,6 +4,6 @@
5 baz 5 baz
6 ) && 6 ) &&
7 ( 7 (
8 bar=$((42 + 1)) ?!AMP?! 8 bar=$((42 + 1)) ?!LINT: missing '&&'?!
9 baz 9 baz
10 ) 10 )

View File

@ -1,20 +1,20 @@
2 ( 2 (
3 foo && 3 foo &&
4 { 4 {
5 echo a ?!AMP?! 5 echo a ?!LINT: missing '&&'?!
6 echo b 6 echo b
7 } && 7 } &&
8 bar && 8 bar &&
9 { 9 {
10 echo c 10 echo c
11 } ?!AMP?! 11 } ?!LINT: missing '&&'?!
12 baz 12 baz
13 ) && 13 ) &&
14 14
15 { 15 {
16 echo a; ?!AMP?! echo b 16 echo a; ?!LINT: missing '&&'?! echo b
17 } && 17 } &&
18 { echo a; ?!AMP?! echo b; } && 18 { echo a; ?!LINT: missing '&&'?! echo b; } &&
19 19
20 { 20 {
21 echo "${var}9" && 21 echo "${var}9" &&

View File

@ -1,6 +1,6 @@
2 ( 2 (
3 foo && 3 foo &&
4 bar ?!AMP?! 4 bar ?!LINT: missing '&&'?!
5 baz && 5 baz &&
6 wop 6 wop
7 ) 7 )

View File

@ -9,11 +9,11 @@
10 case "$x" in 10 case "$x" in
11 x) foo ;; 11 x) foo ;;
12 *) bar ;; 12 *) bar ;;
13 esac ?!AMP?! 13 esac ?!LINT: missing '&&'?!
14 foobar 14 foobar
15 ) && 15 ) &&
16 ( 16 (
17 case "$x" in 1) true;; esac && 17 case "$x" in 1) true;; esac &&
18 case "$y" in 2) false;; esac ?!AMP?! 18 case "$y" in 2) false;; esac ?!LINT: missing '&&'?!
19 foobar 19 foobar
20 ) 20 )

View File

@ -4,6 +4,6 @@
5 echo failed! 5 echo failed!
6 false 6 false
7 else 7 else
8 echo it went okay ?!AMP?! 8 echo it went okay ?!LINT: missing '&&'?!
9 congratulate user 9 congratulate user
10 fi 10 fi

View File

@ -1,5 +1,5 @@
2 echo nobody home && { 2 echo nobody home && {
3 test the doohicky ?!AMP?! 3 test the doohicky ?!LINT: missing '&&'?!
4 right now 4 right now
5 } && 5 } &&
6 6

View File

@ -1,10 +1,10 @@
2 mkdir sub && ( 2 mkdir sub && (
3 cd sub && 3 cd sub &&
4 foo the bar ?!AMP?! 4 foo the bar ?!LINT: missing '&&'?!
5 nuff said 5 nuff said
6 ) && 6 ) &&
7 7
8 cut "-d " -f actual | (read s1 s2 s3 && 8 cut "-d " -f actual | (read s1 s2 s3 &&
9 test -f $s1 ?!AMP?! 9 test -f $s1 ?!LINT: missing '&&'?!
10 test $(cat $s2) = tree2path1 && 10 test $(cat $s2) = tree2path1 &&
11 test $(cat $s3) = tree3path1) 11 test $(cat $s3) = tree3path1)

View File

@ -4,6 +4,6 @@
5 baz 5 baz
6 ) && 6 ) &&
7 ( 7 (
8 bar=$(gobble blocks) ?!AMP?! 8 bar=$(gobble blocks) ?!LINT: missing '&&'?!
9 baz 9 baz
10 ) 10 )

View File

@ -4,6 +4,6 @@
5 : 5 :
6 else 6 else
7 echo >file 7 echo >file
8 fi ?!LOOP?! 8 fi ?!LINT: missing '|| exit 1'?!
9 done) && 9 done) &&
10 test ! -f file 10 test ! -f file

View File

@ -2,7 +2,7 @@
3 bar 3 bar
4 ) && 4 ) &&
5 5
6 (cd foo ?!AMP?! 6 (cd foo ?!LINT: missing '&&'?!
7 bar 7 bar
8 ) && 8 ) &&
9 9
@ -13,5 +13,5 @@
14 (cd foo && 14 (cd foo &&
15 bar) && 15 bar) &&
16 16
17 (cd foo ?!AMP?! 17 (cd foo ?!LINT: missing '&&'?!
18 bar) 18 bar)

View File

@ -1,14 +1,14 @@
2 ( 2 (
3 for i in a b c 3 for i in a b c
4 do 4 do
5 echo $i ?!AMP?! 5 echo $i ?!LINT: missing '&&'?!
6 cat <<-\EOF ?!LOOP?! 6 cat <<-\EOF ?!LINT: missing '|| exit 1'?!
7 bar 7 bar
8 EOF 8 EOF
9 done ?!AMP?! 9 done ?!LINT: missing '&&'?!
10 10
11 for i in a b c; do 11 for i in a b c; do
12 echo $i && 12 echo $i &&
13 cat $i ?!LOOP?! 13 cat $i ?!LINT: missing '|| exit 1'?!
14 done 14 done
15 ) 15 )

View File

@ -4,8 +4,8 @@
5 5
6 remove_object() { 6 remove_object() {
7 file=$(sha1_file "$*") && 7 file=$(sha1_file "$*") &&
8 test -e "$file" ?!AMP?! 8 test -e "$file" ?!LINT: missing '&&'?!
9 rm -f "$file" 9 rm -f "$file"
10 } ?!AMP?! 10 } ?!LINT: missing '&&'?!
11 11
12 sha1_file arg && remove_object arg 12 sha1_file arg && remove_object arg

View File

@ -1,2 +1,2 @@
2 echo "we should find this" ?!AMP?! 2 echo "we should find this" ?!LINT: missing '&&'?!
3 echo "even though our heredoc has its indent stripped" 3 echo "even though our heredoc has its indent stripped"

View File

@ -1,7 +1,7 @@
2 echo "outer here-doc does not allow indented end-tag" ?!AMP?! 2 echo "outer here-doc does not allow indented end-tag" ?!LINT: missing '&&'?!
3 cat >file <<-\EOF && 3 cat >file <<-\EOF &&
4 but this inner here-doc 4 but this inner here-doc
5 does allow indented EOF 5 does allow indented EOF
6 EOF 6 EOF
7 echo "missing chain after" ?!AMP?! 7 echo "missing chain after" ?!LINT: missing '&&'?!
8 echo "but this line is OK because it's the end" 8 echo "but this line is OK because it's the end"

View File

@ -1,7 +1,7 @@
2 echo "missing chain before" ?!AMP?! 2 echo "missing chain before" ?!LINT: missing '&&'?!
3 cat >file <<-\EOF && 3 cat >file <<-\EOF &&
4 inside inner here-doc 4 inside inner here-doc
5 these are not shell commands 5 these are not shell commands
6 EOF 6 EOF
7 echo "missing chain after" ?!AMP?! 7 echo "missing chain after" ?!LINT: missing '&&'?!
8 echo "but this line is OK because it's the end" 8 echo "but this line is OK because it's the end"

View File

@ -1,2 +1,2 @@
8 echo "actual test commands" ?!AMP?! 8 echo "actual test commands" ?!LINT: missing '&&'?!
9 echo "that should be checked" 9 echo "that should be checked"

View File

@ -4,7 +4,7 @@
5 chunks: oid_fanout oid_lookup commit_metadata generation_data bloom_indexes bloom_data 5 chunks: oid_fanout oid_lookup commit_metadata generation_data bloom_indexes bloom_data
6 EOF 6 EOF
7 7
8 cat >expect << -EOF ?!AMP?! 8 cat >expect << -EOF ?!LINT: missing '&&'?!
9 this is not indented 9 this is not indented
10 -EOF 10 -EOF
11 11

View File

@ -3,6 +3,6 @@
4 fossil 4 fossil
5 vegetable 5 vegetable
6 END 6 END
7 wiffle) ?!AMP?! 7 wiffle) ?!LINT: missing '&&'?!
8 echo $x 8 echo $x
9 ) 9 )

View File

@ -1,6 +1,6 @@
2 ( 2 (
3 cat <<-\TXT && echo "multi-line 3 cat <<-\TXT && echo "multi-line
4 string" ?!AMP?! 4 string" ?!LINT: missing '&&'?!
5 fizzle 5 fizzle
6 TXT 6 TXT
7 bap 7 bap

View File

@ -2,6 +2,6 @@
3 marcia || 3 marcia ||
4 kevin 4 kevin
5 then 5 then
6 echo "nomads" ?!AMP?! 6 echo "nomads" ?!LINT: missing '&&'?!
7 echo "for sure" 7 echo "for sure"
8 fi 8 fi

View File

@ -5,8 +5,8 @@
6 then 6 then
7 echo "err" 7 echo "err"
8 exit 1 8 exit 1
9 fi ?!AMP?! 9 fi ?!LINT: missing '&&'?!
10 foo 10 foo
11 done ?!AMP?! 11 done ?!LINT: missing '&&'?!
12 bar 12 bar
13 ) 13 )

View File

@ -1,7 +1,7 @@
2 ( 2 (
3 if test -n "" 3 if test -n ""
4 then 4 then
5 echo very ?!AMP?! 5 echo very ?!LINT: missing '&&'?!
6 echo empty 6 echo empty
7 elif test -z "" 7 elif test -z ""
8 then 8 then
@ -11,7 +11,7 @@
12 cat <<-\EOF 12 cat <<-\EOF
13 bar 13 bar
14 EOF 14 EOF
15 fi ?!AMP?! 15 fi ?!LINT: missing '&&'?!
16 echo poodle 16 echo poodle
17 ) && 17 ) &&
18 ( 18 (

View File

@ -1,6 +1,6 @@
2 ( 2 (
3 foobar && # comment 1 3 foobar && # comment 1
4 barfoo ?!AMP?! # wrong position for && 4 barfoo ?!LINT: missing '&&'?! # wrong position for &&
5 flibble "not a # comment" 5 flibble "not a # comment"
6 ) && 6 ) &&
7 7

View File

@ -11,5 +11,5 @@
12 do 12 do
13 printf "%"$n"s" X > r2/large.$n && 13 printf "%"$n"s" X > r2/large.$n &&
14 git -C r2 add large.$n && 14 git -C r2 add large.$n &&
15 git -C r2 commit -m "$n" ?!LOOP?! 15 git -C r2 commit -m "$n" ?!LINT: missing '|| return 1'?!
16 done 16 done

View File

@ -3,10 +3,10 @@
4 then 4 then
5 while true 5 while true
6 do 6 do
7 echo "pop" ?!AMP?! 7 echo "pop" ?!LINT: missing '&&'?!
8 echo "glup" ?!LOOP?! 8 echo "glup" ?!LINT: missing '|| exit 1'?!
9 done ?!AMP?! 9 done ?!LINT: missing '&&'?!
10 foo 10 foo
11 fi ?!AMP?! 11 fi ?!LINT: missing '&&'?!
12 bar 12 bar
13 ) 13 )

View File

@ -3,7 +3,7 @@
4 line 2 4 line 2
5 line 3" && 5 line 3" &&
6 y="line 1 6 y="line 1
7 line2" ?!AMP?! 7 line2" ?!LINT: missing '&&'?!
8 foobar 8 foobar
9 ) && 9 ) &&
10 ( 10 (

View File

@ -1,5 +1,5 @@
2 ! (foo && bar) && 2 ! (foo && bar) &&
3 ! (foo && bar) >baz && 3 ! (foo && bar) >baz &&
4 4
5 ! (foo; ?!AMP?! bar) && 5 ! (foo; ?!LINT: missing '&&'?! bar) &&
6 ! (foo; ?!AMP?! bar) >baz 6 ! (foo; ?!LINT: missing '&&'?! bar) >baz

View File

@ -5,7 +5,7 @@
6 6
7 (cd foo && 7 (cd foo &&
8 bar 8 bar
9 ) ?!AMP?! 9 ) ?!LINT: missing '&&'?!
10 10
11 ( 11 (
12 cd foo && 12 cd foo &&
@ -13,13 +13,13 @@
14 14
15 ( 15 (
16 cd foo && 16 cd foo &&
17 bar) ?!AMP?! 17 bar) ?!LINT: missing '&&'?!
18 18
19 (cd foo && 19 (cd foo &&
20 bar) && 20 bar) &&
21 21
22 (cd foo && 22 (cd foo &&
23 bar) ?!AMP?! 23 bar) ?!LINT: missing '&&'?!
24 24
25 foobar 25 foobar
26 ) 26 )

View File

@ -18,7 +18,7 @@
19 toink 19 toink
20 INPUT_END 20 INPUT_END
21 21
22 cat <<-\EOT ?!AMP?! 22 cat <<-\EOT ?!LINT: missing '&&'?!
23 text goes here 23 text goes here
24 data <<EOF 24 data <<EOF
25 data goes here 25 data goes here

View File

@ -2,8 +2,8 @@
3 do 3 do
4 for j in 0 1 2 3 4 5 6 7 8 9; 4 for j in 0 1 2 3 4 5 6 7 8 9;
5 do 5 do
6 echo "$i$j" >"path$i$j" ?!LOOP?! 6 echo "$i$j" >"path$i$j" ?!LINT: missing '|| return 1'?!
7 done ?!LOOP?! 7 done ?!LINT: missing '|| return 1'?!
8 done && 8 done &&
9 9
10 for i in 0 1 2 3 4 5 6 7 8 9; 10 for i in 0 1 2 3 4 5 6 7 8 9;
@ -18,7 +18,7 @@
19 do 19 do
20 for j in 0 1 2 3 4 5 6 7 8 9; 20 for j in 0 1 2 3 4 5 6 7 8 9;
21 do 21 do
22 echo "$i$j" >"path$i$j" ?!LOOP?! 22 echo "$i$j" >"path$i$j" ?!LINT: missing '|| return 1'?!
23 done || return 1 23 done || return 1
24 done && 24 done &&
25 25

View File

@ -6,6 +6,6 @@
7 # minor numbers of cows (or do they?) 7 # minor numbers of cows (or do they?)
8 baz && 8 baz &&
9 snaff 9 snaff
10 ) ?!AMP?! 10 ) ?!LINT: missing '&&'?!
11 fuzzy 11 fuzzy
12 ) 12 )

View File

@ -7,7 +7,7 @@
8 8
9 cd foo && 9 cd foo &&
10 ( 10 (
11 echo a ?!AMP?! 11 echo a ?!LINT: missing '&&'?!
12 echo b 12 echo b
13 ) >file 13 ) >file
14 ) 14 )

View File

@ -9,6 +9,6 @@
10 echo ourside && 10 echo ourside &&
11 echo "=======" && 11 echo "=======" &&
12 echo theirside && 12 echo theirside &&
13 echo ">>>>>>> theirs" ?!AMP?! 13 echo ">>>>>>> theirs" ?!LINT: missing '&&'?!
14 poodle 14 poodle
15 ) >merged 15 ) >merged

View File

@ -3,7 +3,7 @@
4 cd dir-rename-and-content && 4 cd dir-rename-and-content &&
5 test_write_lines 1 2 3 4 5 >foo && 5 test_write_lines 1 2 3 4 5 >foo &&
6 mkdir olddir && 6 mkdir olddir &&
7 for i in a b c; do echo $i >olddir/$i; ?!LOOP?! done ?!AMP?! 7 for i in a b c; do echo $i >olddir/$i; ?!LINT: missing '|| exit 1'?! done ?!LINT: missing '&&'?!
8 git add foo olddir && 8 git add foo olddir &&
9 git commit -m "original" && 9 git commit -m "original" &&
10 ) 10 )

View File

@ -2,8 +2,8 @@
3 (foo && bar) | 3 (foo && bar) |
4 (foo && bar) >baz && 4 (foo && bar) >baz &&
5 5
6 (foo; ?!AMP?! bar) && 6 (foo; ?!LINT: missing '&&'?! bar) &&
7 (foo; ?!AMP?! bar) | 7 (foo; ?!LINT: missing '&&'?! bar) |
8 (foo; ?!AMP?! bar) >baz && 8 (foo; ?!LINT: missing '&&'?! bar) >baz &&
9 9
10 (foo "bar; baz") 10 (foo "bar; baz")

View File

@ -4,7 +4,7 @@
5 baz && 5 baz &&
6 6
7 fish | 7 fish |
8 cow ?!AMP?! 8 cow ?!LINT: missing '&&'?!
9 9
10 sunder 10 sunder
11 ) 11 )

View File

@ -1,19 +1,19 @@
2 ( 2 (
3 cat foo ; ?!AMP?! echo bar ?!AMP?! 3 cat foo ; ?!LINT: missing '&&'?! echo bar ?!LINT: missing '&&'?!
4 cat foo ; ?!AMP?! echo bar 4 cat foo ; ?!LINT: missing '&&'?! echo bar
5 ) && 5 ) &&
6 ( 6 (
7 cat foo ; ?!AMP?! echo bar && 7 cat foo ; ?!LINT: missing '&&'?! echo bar &&
8 cat foo ; ?!AMP?! echo bar 8 cat foo ; ?!LINT: missing '&&'?! echo bar
9 ) && 9 ) &&
10 ( 10 (
11 echo "foo; bar" && 11 echo "foo; bar" &&
12 cat foo; ?!AMP?! echo bar 12 cat foo; ?!LINT: missing '&&'?! echo bar
13 ) && 13 ) &&
14 ( 14 (
15 foo; 15 foo;
16 ) && 16 ) &&
17 (cd foo && 17 (cd foo &&
18 for i in a b c; do 18 for i in a b c; do
19 echo; ?!LOOP?! 19 echo; ?!LINT: missing '|| exit 1'?!
20 done) 20 done)

View File

@ -6,7 +6,7 @@
7 nevermore... 7 nevermore...
8 EOF 8 EOF
9 9
10 cat <<EOF >bip ?!AMP?! 10 cat <<EOF >bip ?!LINT: missing '&&'?!
11 fish fly high 11 fish fly high
12 EOF 12 EOF
13 13

View File

@ -3,17 +3,17 @@
4 (foo && bar) | 4 (foo && bar) |
5 (foo && bar) >baz && 5 (foo && bar) >baz &&
6 6
7 (foo; ?!AMP?! bar) && 7 (foo; ?!LINT: missing '&&'?! bar) &&
8 (foo; ?!AMP?! bar) | 8 (foo; ?!LINT: missing '&&'?! bar) |
9 (foo; ?!AMP?! bar) >baz && 9 (foo; ?!LINT: missing '&&'?! bar) >baz &&
10 10
11 (foo || exit 1) && 11 (foo || exit 1) &&
12 (foo || exit 1) | 12 (foo || exit 1) |
13 (foo || exit 1) >baz && 13 (foo || exit 1) >baz &&
14 14
15 (foo && bar) ?!AMP?! 15 (foo && bar) ?!LINT: missing '&&'?!
16 16
17 (foo && bar; ?!AMP?! baz) ?!AMP?! 17 (foo && bar; ?!LINT: missing '&&'?! baz) ?!LINT: missing '&&'?!
18 18
19 foobar 19 foobar
20 ) 20 )

View File

@ -2,13 +2,13 @@
3 git config filter.rot13.clean ./rot13.sh && 3 git config filter.rot13.clean ./rot13.sh &&
4 4
5 { 5 {
6 echo "*.t filter=rot13" ?!AMP?! 6 echo "*.t filter=rot13" ?!LINT: missing '&&'?!
7 echo "*.i ident" 7 echo "*.i ident"
8 } >.gitattributes && 8 } >.gitattributes &&
9 9
10 { 10 {
11 echo a b c d e f g h i j k l m ?!AMP?! 11 echo a b c d e f g h i j k l m ?!LINT: missing '&&'?!
12 echo n o p q r s t u v w x y z ?!AMP?! 12 echo n o p q r s t u v w x y z ?!LINT: missing '&&'?!
13 echo '$Id$' 13 echo '$Id$'
14 } >test && 14 } >test &&
15 cat test >test.t && 15 cat test >test.t &&
@ -19,7 +19,7 @@
20 git checkout -- test test.t test.i && 20 git checkout -- test test.t test.i &&
21 21
22 echo "content-test2" >test2.o && 22 echo "content-test2" >test2.o &&
23 echo "content-test3 - filename with special characters" >"test3 'sq',$x=.o" ?!AMP?! 23 echo "content-test3 - filename with special characters" >"test3 'sq',$x=.o" ?!LINT: missing '&&'?!
24 24
25 downstream_url_for_sed=$( 25 downstream_url_for_sed=$(
26 printf "%sn" "$downstream_url" | 26 printf "%sn" "$downstream_url" |

View File

@ -1,4 +1,4 @@
2 command_which_is_run && 2 command_which_is_run &&
3 cat >expect <<-\EOF ?!UNCLOSED-HEREDOC?! && 3 cat >expect <<-\EOF ?!LINT: unclosed heredoc?! &&
4 we forget to end the here-doc 4 we forget to end the here-doc
5 command_which_is_gobbled 5 command_which_is_gobbled

View File

@ -1,5 +1,5 @@
2 command_which_is_run && 2 command_which_is_run &&
3 cat >expect <<\EOF ?!UNCLOSED-HEREDOC?! && 3 cat >expect <<\EOF ?!LINT: unclosed heredoc?! &&
4 we try to end the here-doc below, 4 we try to end the here-doc below,
5 but the indentation throws us off 5 but the indentation throws us off
6 since the operator is not "<<-". 6 since the operator is not "<<-".

View File

@ -1,14 +1,14 @@
2 ( 2 (
3 while true 3 while true
4 do 4 do
5 echo foo ?!AMP?! 5 echo foo ?!LINT: missing '&&'?!
6 cat <<-\EOF ?!LOOP?! 6 cat <<-\EOF ?!LINT: missing '|| exit 1'?!
7 bar 7 bar
8 EOF 8 EOF
9 done ?!AMP?! 9 done ?!LINT: missing '&&'?!
10 10
11 while true; do 11 while true; do
12 echo foo && 12 echo foo &&
13 cat bar ?!LOOP?! 13 cat bar ?!LINT: missing '|| exit 1'?!
14 done 14 done
15 ) 15 )