summaryrefslogtreecommitdiff
path: root/scripts/checkpatch.pl
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2016-10-14 03:25:40 +0300
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2016-10-14 03:25:40 +0300
commit4a7126a25b4dfd07d61c699f724118275acc0c25 (patch)
treec78b82bfaa96f330d412ad63e355906f963c3faf /scripts/checkpatch.pl
parent930e19248e9b61da36c967687ca79c4d5f977919 (diff)
parentc8d2bc9bc39ebea8437fd974fdbc21847bb897a3 (diff)
downloadlinux-4a7126a25b4dfd07d61c699f724118275acc0c25.tar.xz
Merge tag 'v4.8' into next
Sync up with mainline to bring in I2C host notify changes and other updates.
Diffstat (limited to 'scripts/checkpatch.pl')
-rwxr-xr-xscripts/checkpatch.pl180
1 files changed, 160 insertions, 20 deletions
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index d574d13ba963..206a6b346a8d 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -27,12 +27,15 @@ my $emacs = 0;
my $terse = 0;
my $showfile = 0;
my $file = 0;
+my $git = 0;
+my %git_commits = ();
my $check = 0;
my $check_orig = 0;
my $summary = 1;
my $mailback = 0;
my $summary_file = 0;
my $show_types = 0;
+my $list_types = 0;
my $fix = 0;
my $fix_inplace = 0;
my $root;
@@ -52,6 +55,7 @@ my $spelling_file = "$D/spelling.txt";
my $codespell = 0;
my $codespellfile = "/usr/share/codespell/dictionary.txt";
my $color = 1;
+my $allow_c99_comments = 1;
sub help {
my ($exitcode) = @_;
@@ -68,13 +72,24 @@ Options:
--emacs emacs compile window format
--terse one line per report
--showfile emit diffed file position, not input file position
+ -g, --git treat FILE as a single commit or git revision range
+ single git commit with:
+ <rev>
+ <rev>^
+ <rev>~n
+ multiple git commits with:
+ <rev1>..<rev2>
+ <rev1>...<rev2>
+ <rev>-<count>
+ git merges are ignored
-f, --file treat FILE as regular source file
--subjective, --strict enable more subjective tests
+ --list-types list the possible message types
--types TYPE(,TYPE2...) show only these comma separated message types
--ignore TYPE(,TYPE2...) ignore various comma separated message types
+ --show-types show the specific message type in the output
--max-line-length=n set the maximum line length, if exceeded, warn
--min-conf-desc-length=n set the min description length, if shorter, warn
- --show-types show the message "types" in the output
--root=PATH PATH to the kernel tree root
--no-summary suppress the per-file summary
--mailback only produce a report in case of warnings/errors
@@ -106,6 +121,37 @@ EOM
exit($exitcode);
}
+sub uniq {
+ my %seen;
+ return grep { !$seen{$_}++ } @_;
+}
+
+sub list_types {
+ my ($exitcode) = @_;
+
+ my $count = 0;
+
+ local $/ = undef;
+
+ open(my $script, '<', abs_path($P)) or
+ die "$P: Can't read '$P' $!\n";
+
+ my $text = <$script>;
+ close($script);
+
+ my @types = ();
+ for ($text =~ /\b(?:(?:CHK|WARN|ERROR)\s*\(\s*"([^"]+)")/g) {
+ push (@types, $_);
+ }
+ @types = sort(uniq(@types));
+ print("#\tMessage type\n\n");
+ foreach my $type (@types) {
+ print(++$count . "\t" . $type . "\n");
+ }
+
+ exit($exitcode);
+}
+
my $conf = which_conf($configuration_file);
if (-f $conf) {
my @conf_args;
@@ -141,11 +187,13 @@ GetOptions(
'terse!' => \$terse,
'showfile!' => \$showfile,
'f|file!' => \$file,
+ 'g|git!' => \$git,
'subjective!' => \$check,
'strict!' => \$check,
'ignore=s' => \@ignore,
'types=s' => \@use,
'show-types!' => \$show_types,
+ 'list-types!' => \$list_types,
'max-line-length=i' => \$max_line_length,
'min-conf-desc-length=i' => \$min_conf_desc_length,
'root=s' => \$root,
@@ -166,6 +214,8 @@ GetOptions(
help(0) if ($help);
+list_types(0) if ($list_types);
+
$fix = 1 if ($fix_inplace);
$check_orig = $check;
@@ -178,9 +228,9 @@ if ($^V && $^V lt $minimum_perl_version) {
}
}
+#if no filenames are given, push '-' to read patch from stdin
if ($#ARGV < 0) {
- print "$P: no input files\n";
- exit(1);
+ push(@ARGV, '-');
}
sub hash_save_array_words {
@@ -264,7 +314,6 @@ our $Sparse = qr{
__kernel|
__force|
__iomem|
- __pmem|
__must_check|
__init_refok|
__kprobes|
@@ -752,10 +801,42 @@ my @fixed_inserted = ();
my @fixed_deleted = ();
my $fixlinenr = -1;
+# If input is git commits, extract all commits from the commit expressions.
+# For example, HEAD-3 means we need check 'HEAD, HEAD~1, HEAD~2'.
+die "$P: No git repository found\n" if ($git && !-e ".git");
+
+if ($git) {
+ my @commits = ();
+ foreach my $commit_expr (@ARGV) {
+ my $git_range;
+ if ($commit_expr =~ m/^(.*)-(\d+)$/) {
+ $git_range = "-$2 $1";
+ } elsif ($commit_expr =~ m/\.\./) {
+ $git_range = "$commit_expr";
+ } else {
+ $git_range = "-1 $commit_expr";
+ }
+ my $lines = `git log --no-color --no-merges --pretty=format:'%H %s' $git_range`;
+ foreach my $line (split(/\n/, $lines)) {
+ $line =~ /^([0-9a-fA-F]{40,40}) (.*)$/;
+ next if (!defined($1) || !defined($2));
+ my $sha1 = $1;
+ my $subject = $2;
+ unshift(@commits, $sha1);
+ $git_commits{$sha1} = $subject;
+ }
+ }
+ die "$P: no git commits after extraction!\n" if (@commits == 0);
+ @ARGV = @commits;
+}
+
my $vname;
for my $filename (@ARGV) {
my $FILE;
- if ($file) {
+ if ($git) {
+ open($FILE, '-|', "git format-patch -M --stdout -1 $filename") ||
+ die "$P: $filename: git format-patch failed - $!\n";
+ } elsif ($file) {
open($FILE, '-|', "diff -u /dev/null $filename") ||
die "$P: $filename: diff failed - $!\n";
} elsif ($filename eq '-') {
@@ -766,6 +847,8 @@ for my $filename (@ARGV) {
}
if ($filename eq '-') {
$vname = 'Your patch';
+ } elsif ($git) {
+ $vname = "Commit " . substr($filename, 0, 12) . ' ("' . $git_commits{$filename} . '")';
} else {
$vname = $filename;
}
@@ -1062,6 +1145,11 @@ sub sanitise_line {
$res =~ s@(\#\s*(?:error|warning)\s+).*@$1$clean@;
}
+ if ($allow_c99_comments && $res =~ m@(//.*$)@) {
+ my $match = $1;
+ $res =~ s/\Q$match\E/"$;" x length($match)/e;
+ }
+
return $res;
}
@@ -1981,6 +2069,7 @@ sub process {
my $is_patch = 0;
my $in_header_lines = $file ? 0 : 1;
my $in_commit_log = 0; #Scanning lines before patch
+ my $has_commit_log = 0; #Encountered lines before patch
my $commit_log_possible_stack_dump = 0;
my $commit_log_long_line = 0;
my $commit_log_has_diff = 0;
@@ -2371,8 +2460,9 @@ sub process {
# Check for git id commit length and improperly formed commit descriptions
if ($in_commit_log && !$commit_log_possible_stack_dump &&
+ $line !~ /^\s*(?:Link|Patchwork|http|https|BugLink):/i &&
($line =~ /\bcommit\s+[0-9a-f]{5,}\b/i ||
- ($line =~ /\b[0-9a-f]{12,40}\b/i &&
+ ($line =~ /(?:\s|^)[0-9a-f]{12,40}(?:[\s"'\(\[]|$)/i &&
$line !~ /[\<\[][0-9a-f]{12,40}[\>\]]/i &&
$line !~ /\bfixes:\s*[0-9a-f]{12,40}/i))) {
my $init_char = "c";
@@ -2477,6 +2567,7 @@ sub process {
$rawline =~ /^(commit\b|from\b|[\w-]+:).*$/i)) {
$in_header_lines = 0;
$in_commit_log = 1;
+ $has_commit_log = 1;
}
# Check if there is UTF-8 in a commit log when a mail header has explicitly
@@ -2680,6 +2771,10 @@ sub process {
$line =~ /^\+\s*#\s*define\s+\w+\s+$String$/) {
$msg_type = "";
+ # EFI_GUID is another special case
+ } elsif ($line =~ /^\+.*\bEFI_GUID\s*\(/) {
+ $msg_type = "";
+
# Otherwise set the alternate message types
# a comment starts before $max_line_length
@@ -2755,6 +2850,19 @@ sub process {
"Logical continuations should be on the previous line\n" . $hereprev);
}
+# check indentation starts on a tab stop
+ if ($^V && $^V ge 5.10.0 &&
+ $sline =~ /^\+\t+( +)(?:$c90_Keywords\b|\{\s*$|\}\s*(?:else\b|while\b|\s*$))/) {
+ my $indent = length($1);
+ if ($indent % 8) {
+ if (WARN("TABSTOP",
+ "Statements should start on a tabstop\n" . $herecurr) &&
+ $fix) {
+ $fixed[$fixlinenr] =~ s@(^\+\t+) +@$1 . "\t" x ($indent/8)@e;
+ }
+ }
+ }
+
# check multi-line statement indentation matches previous line
if ($^V && $^V ge 5.10.0 &&
$prevline =~ /^\+([ \t]*)((?:$c90_Keywords(?:\s+if)\s*)|(?:$Declare\s*)?(?:$Ident|\(\s*\*\s*$Ident\s*\))\s*|$Ident\s*=\s*$Ident\s*)\(.*(\&\&|\|\||,)\s*$/) {
@@ -3241,7 +3349,7 @@ sub process {
next if ($line =~ /^[^\+]/);
# check for declarations of signed or unsigned without int
- while ($line =~ m{($Declare)\s*(?!char\b|short\b|int\b|long\b)\s*($Ident)?\s*[=,;\[\)\(]}g) {
+ while ($line =~ m{\b($Declare)\s*(?!char\b|short\b|int\b|long\b)\s*($Ident)?\s*[=,;\[\)\(]}g) {
my $type = $1;
my $var = $2;
$var = "" if (!defined $var);
@@ -3462,15 +3570,6 @@ sub process {
}
}
-# check for uses of DEFINE_PCI_DEVICE_TABLE
- if ($line =~ /\bDEFINE_PCI_DEVICE_TABLE\s*\(\s*(\w+)\s*\)\s*=/) {
- if (WARN("DEFINE_PCI_DEVICE_TABLE",
- "Prefer struct pci_device_id over deprecated DEFINE_PCI_DEVICE_TABLE\n" . $herecurr) &&
- $fix) {
- $fixed[$fixlinenr] =~ s/\b(?:static\s+|)DEFINE_PCI_DEVICE_TABLE\s*\(\s*(\w+)\s*\)\s*=\s*/static const struct pci_device_id $1\[\] = /;
- }
- }
-
# check for new typedefs, only function parameters and sparse annotations
# make sense.
if ($line =~ /\btypedef\s/ &&
@@ -4291,7 +4390,7 @@ sub process {
my $comp = $3;
my $to = $4;
my $newcomp = $comp;
- if ($lead !~ /$Operators\s*$/ &&
+ if ($lead !~ /(?:$Operators|\.)\s*$/ &&
$to !~ /^(?:Constant|[A-Z_][A-Z0-9_]*)$/ &&
WARN("CONSTANT_COMPARISON",
"Comparisons should place the constant on the right side of the test\n" . $herecurr) &&
@@ -5626,8 +5725,9 @@ sub process {
}
}
-# check for #defines like: 1 << <digit> that could be BIT(digit)
- if ($line =~ /#\s*define\s+\w+\s+\(?\s*1\s*([ulUL]*)\s*\<\<\s*(?:\d+|$Ident)\s*\)?/) {
+# check for #defines like: 1 << <digit> that could be BIT(digit), it is not exported to uapi
+ if ($realfile !~ m@^include/uapi/@ &&
+ $line =~ /#\s*define\s+\w+\s+\(?\s*1\s*([ulUL]*)\s*\<\<\s*(?:\d+|$Ident)\s*\)?/) {
my $ull = "";
$ull = "_ULL" if (defined($1) && $1 =~ /ll/i);
if (CHK("BIT_MACRO",
@@ -5637,6 +5737,16 @@ sub process {
}
}
+# check for #if defined CONFIG_<FOO> || defined CONFIG_<FOO>_MODULE
+ if ($line =~ /^\+\s*#\s*if\s+defined(?:\s*\(?\s*|\s+)(CONFIG_[A-Z_]+)\s*\)?\s*\|\|\s*defined(?:\s*\(?\s*|\s+)\1_MODULE\s*\)?\s*$/) {
+ my $config = $1;
+ if (WARN("PREFER_IS_ENABLED",
+ "Prefer IS_ENABLED(<FOO>) to CONFIG_<FOO> || CONFIG_<FOO>_MODULE\n" . $herecurr) &&
+ $fix) {
+ $fixed[$fixlinenr] = "\+#if IS_ENABLED($config)";
+ }
+ }
+
# check for case / default statements not preceded by break/fallthrough/switch
if ($line =~ /^.\s*(?:case\s+(?:$Ident|$Constant)\s*|default):/) {
my $has_break = 0;
@@ -5827,6 +5937,28 @@ sub process {
}
}
+# whine about ACCESS_ONCE
+ if ($^V && $^V ge 5.10.0 &&
+ $line =~ /\bACCESS_ONCE\s*$balanced_parens\s*(=(?!=))?\s*($FuncArg)?/) {
+ my $par = $1;
+ my $eq = $2;
+ my $fun = $3;
+ $par =~ s/^\(\s*(.*)\s*\)$/$1/;
+ if (defined($eq)) {
+ if (WARN("PREFER_WRITE_ONCE",
+ "Prefer WRITE_ONCE(<FOO>, <BAR>) over ACCESS_ONCE(<FOO>) = <BAR>\n" . $herecurr) &&
+ $fix) {
+ $fixed[$fixlinenr] =~ s/\bACCESS_ONCE\s*\(\s*\Q$par\E\s*\)\s*$eq\s*\Q$fun\E/WRITE_ONCE($par, $fun)/;
+ }
+ } else {
+ if (WARN("PREFER_READ_ONCE",
+ "Prefer READ_ONCE(<FOO>) over ACCESS_ONCE(<FOO>)\n" . $herecurr) &&
+ $fix) {
+ $fixed[$fixlinenr] =~ s/\bACCESS_ONCE\s*\(\s*\Q$par\E\s*\)/READ_ONCE($par)/;
+ }
+ }
+ }
+
# check for lockdep_set_novalidate_class
if ($line =~ /^.\s*lockdep_set_novalidate_class\s*\(/ ||
$line =~ /__lockdep_no_validate__\s*\)/ ) {
@@ -5916,7 +6048,7 @@ sub process {
ERROR("NOT_UNIFIED_DIFF",
"Does not appear to be a unified-diff format patch\n");
}
- if ($is_patch && $filename ne '-' && $chk_signoff && $signoff == 0) {
+ if ($is_patch && $has_commit_log && $chk_signoff && $signoff == 0) {
ERROR("MISSING_SIGN_OFF",
"Missing Signed-off-by: line(s)\n");
}
@@ -5930,6 +6062,14 @@ sub process {
}
if ($quiet == 0) {
+ # If there were any defects found and not already fixing them
+ if (!$clean and !$fix) {
+ print << "EOM"
+
+NOTE: For some of the reported defects, checkpatch may be able to
+ mechanically convert to the typical style using --fix or --fix-inplace.
+EOM
+ }
# If there were whitespace errors which cleanpatch can fix
# then suggest that.
if ($rpt_cleaners) {