summaryrefslogtreecommitdiff
path: root/scripts/checkpatch.pl
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/checkpatch.pl')
-rwxr-xr-xscripts/checkpatch.pl175
1 files changed, 96 insertions, 79 deletions
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index 9c4c4a61bc83..664f7b7a622c 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -28,6 +28,7 @@ my %verbose_messages = ();
my %verbose_emitted = ();
my $tree = 1;
my $chk_signoff = 1;
+my $chk_fixes_tag = 1;
my $chk_patch = 1;
my $tst_only;
my $emacs = 0;
@@ -88,6 +89,7 @@ Options:
-v, --verbose verbose mode
--no-tree run without a kernel tree
--no-signoff do not check for 'Signed-off-by' line
+ --no-fixes-tag do not check for 'Fixes:' tag
--patch treat FILE as patchfile (default)
--emacs emacs compile window format
--terse one line per report
@@ -111,7 +113,8 @@ Options:
--max-line-length=n set the maximum line length, (default $max_line_length)
if exceeded, warn on patches
requires --strict for use with --file
- --min-conf-desc-length=n set the min description length, if shorter, warn
+ --min-conf-desc-length=n set the minimum description length for config symbols
+ in lines, if shorter, warn (default $min_conf_desc_length)
--tab-size=n set the number of spaces for tab (default $tabsize)
--root=PATH PATH to the kernel tree root
--no-summary suppress the per-file summary
@@ -148,6 +151,24 @@ EOM
exit($exitcode);
}
+my $DO_WHILE_0_ADVICE = q{
+ do {} while (0) advice is over-stated in a few situations:
+
+ The more obvious case is macros, like MODULE_PARM_DESC, invoked at
+ file-scope, where C disallows code (it must be in functions). See
+ $exceptions if you have one to add by name.
+
+ More troublesome is declarative macros used at top of new scope,
+ like DECLARE_PER_CPU. These might just compile with a do-while-0
+ wrapper, but would be incorrect. Most of these are handled by
+ detecting struct,union,etc declaration primitives in $exceptions.
+
+ Theres also macros called inside an if (block), which "return" an
+ expression. These cannot do-while, and need a ({}) wrapper.
+
+ Enjoy this qualification while we work to improve our heuristics.
+};
+
sub uniq {
my %seen;
return grep { !$seen{$_}++ } @_;
@@ -295,6 +316,7 @@ GetOptions(
'v|verbose!' => \$verbose,
'tree!' => \$tree,
'signoff!' => \$chk_signoff,
+ 'fixes-tag!' => \$chk_fixes_tag,
'patch!' => \$chk_patch,
'emacs!' => \$emacs,
'terse!' => \$terse,
@@ -831,20 +853,12 @@ foreach my $entry (@mode_permission_funcs) {
$mode_perms_search = "(?:${mode_perms_search})";
our %deprecated_apis = (
- "synchronize_rcu_bh" => "synchronize_rcu",
- "synchronize_rcu_bh_expedited" => "synchronize_rcu_expedited",
- "call_rcu_bh" => "call_rcu",
- "rcu_barrier_bh" => "rcu_barrier",
- "synchronize_sched" => "synchronize_rcu",
- "synchronize_sched_expedited" => "synchronize_rcu_expedited",
- "call_rcu_sched" => "call_rcu",
- "rcu_barrier_sched" => "rcu_barrier",
- "get_state_synchronize_sched" => "get_state_synchronize_rcu",
- "cond_synchronize_sched" => "cond_synchronize_rcu",
"kmap" => "kmap_local_page",
"kunmap" => "kunmap_local",
"kmap_atomic" => "kmap_local_page",
"kunmap_atomic" => "kunmap_local",
+ "srcu_read_lock_lite" => "srcu_read_lock_fast",
+ "srcu_read_unlock_lite" => "srcu_read_unlock_fast",
);
#Create a search pattern for all these strings to speed up a loop below
@@ -1257,6 +1271,7 @@ sub git_commit_info {
}
$chk_signoff = 0 if ($file);
+$chk_fixes_tag = 0 if ($file);
my @rawlines = ();
my @lines = ();
@@ -2636,6 +2651,9 @@ sub process {
our $clean = 1;
my $signoff = 0;
+ my $fixes_tag = 0;
+ my $is_revert = 0;
+ my $needs_fixes_tag = "";
my $author = '';
my $authorsignoff = 0;
my $author_sob = '';
@@ -2868,7 +2886,7 @@ sub process {
if ($realfile =~ m@^include/asm/@) {
ERROR("MODIFIED_INCLUDE_ASM",
- "do not modify files in include/asm, change architecture specific files in include/asm-<architecture>\n" . "$here$rawline\n");
+ "do not modify files in include/asm, change architecture specific files in arch/<architecture>/include/asm\n" . "$here$rawline\n");
}
$found_file = 1;
}
@@ -3189,47 +3207,53 @@ sub process {
}
}
+# These indicate a bug fix
+ if (!$in_header_lines && !$is_patch &&
+ $line =~ /^This reverts commit/) {
+ $is_revert = 1;
+ }
+
+ if (!$in_header_lines && !$is_patch &&
+ $line =~ /((?:(?:BUG: K.|UB)SAN: |Call Trace:|stable\@|syzkaller))/) {
+ $needs_fixes_tag = $1;
+ }
# Check Fixes: styles is correct
if (!$in_header_lines &&
- $line =~ /^\s*fixes:?\s*(?:commit\s*)?[0-9a-f]{5,}\b/i) {
- my $orig_commit = "";
- my $id = "0123456789ab";
- my $title = "commit title";
- my $tag_case = 1;
- my $tag_space = 1;
- my $id_length = 1;
- my $id_case = 1;
+ $line =~ /^\s*(fixes:?)\s*(?:commit\s*)?([0-9a-f]{5,40})(?:\s*($balanced_parens))?/i) {
+ my $tag = $1;
+ my $orig_commit = $2;
+ my $title;
my $title_has_quotes = 0;
-
- if ($line =~ /(\s*fixes:?)\s+([0-9a-f]{5,})\s+($balanced_parens)/i) {
- my $tag = $1;
- $orig_commit = $2;
- $title = $3;
-
- $tag_case = 0 if $tag eq "Fixes:";
- $tag_space = 0 if ($line =~ /^fixes:? [0-9a-f]{5,} ($balanced_parens)/i);
-
- $id_length = 0 if ($orig_commit =~ /^[0-9a-f]{12}$/i);
- $id_case = 0 if ($orig_commit !~ /[A-F]/);
-
+ $fixes_tag = 1;
+ if (defined $3) {
# Always strip leading/trailing parens then double quotes if existing
- $title = substr($title, 1, -1);
+ $title = substr($3, 1, -1);
if ($title =~ /^".*"$/) {
$title = substr($title, 1, -1);
$title_has_quotes = 1;
}
+ } else {
+ $title = "commit title"
}
+
+ my $tag_case = not ($tag eq "Fixes:");
+ my $tag_space = not ($line =~ /^fixes:? [0-9a-f]{5,40} ($balanced_parens)/i);
+
+ my $id_length = not ($orig_commit =~ /^[0-9a-f]{12,40}$/i);
+ my $id_case = not ($orig_commit !~ /[A-F]/);
+
+ my $id = "0123456789ab";
my ($cid, $ctitle) = git_commit_info($orig_commit, $id,
$title);
- if ($ctitle ne $title || $tag_case || $tag_space ||
- $id_length || $id_case || !$title_has_quotes) {
+ if (defined($cid) && ($ctitle ne $title || $tag_case || $tag_space || $id_length || $id_case || !$title_has_quotes)) {
+ my $fixed = "Fixes: $cid (\"$ctitle\")";
if (WARN("BAD_FIXES_TAG",
- "Please use correct Fixes: style 'Fixes: <12 chars of sha1> (\"<title line>\")' - ie: 'Fixes: $cid (\"$ctitle\")'\n" . $herecurr) &&
+ "Please use correct Fixes: style 'Fixes: <12+ chars of sha1> (\"<title line>\")' - ie: '$fixed'\n" . $herecurr) &&
$fix) {
- $fixed[$fixlinenr] = "Fixes: $cid (\"$ctitle\")";
+ $fixed[$fixlinenr] = $fixed;
}
}
}
@@ -3642,7 +3666,7 @@ sub process {
$help_length < $min_conf_desc_length) {
my $stat_real = get_stat_real($linenr, $ln - 1);
WARN("CONFIG_DESCRIPTION",
- "please write a help paragraph that fully describes the config symbol\n" . "$here\n$stat_real\n");
+ "please write a help paragraph that fully describes the config symbol with at least $min_conf_desc_length lines\n" . "$here\n$stat_real\n");
}
}
@@ -3686,20 +3710,6 @@ sub process {
}
}
- if (($realfile =~ /Makefile.*/ || $realfile =~ /Kbuild.*/) &&
- ($line =~ /\+(EXTRA_[A-Z]+FLAGS).*/)) {
- my $flag = $1;
- my $replacement = {
- 'EXTRA_AFLAGS' => 'asflags-y',
- 'EXTRA_CFLAGS' => 'ccflags-y',
- 'EXTRA_CPPFLAGS' => 'cppflags-y',
- 'EXTRA_LDFLAGS' => 'ldflags-y',
- };
-
- WARN("DEPRECATED_VARIABLE",
- "Use of $flag is deprecated, please use \`$replacement->{$flag} instead.\n" . $herecurr) if ($replacement->{$flag});
- }
-
# check for DT compatible documentation
if (defined $root &&
(($realfile =~ /\.dtsi?$/ && $line =~ /^\+\s*compatible\s*=\s*\"/) ||
@@ -3858,7 +3868,7 @@ sub process {
}
if ($msg_type ne "" &&
- (show_type("LONG_LINE") || show_type($msg_type))) {
+ show_type("LONG_LINE") && show_type($msg_type)) {
my $msg_level = \&WARN;
$msg_level = \&CHK if ($file);
&{$msg_level}($msg_type,
@@ -3997,16 +4007,6 @@ sub process {
}
}
-# Block comment styles
-# Networking with an initial /*
- if ($realfile =~ m@^(drivers/net/|net/)@ &&
- $prevrawline =~ /^\+[ \t]*\/\*[ \t]*$/ &&
- $rawline =~ /^\+[ \t]*\*/ &&
- $realline > 3) { # Do not warn about the initial copyright comment block after SPDX-License-Identifier
- WARN("NETWORKING_BLOCK_COMMENT_STYLE",
- "networking block comments don't use an empty /* line, use /* Comment...\n" . $hereprev);
- }
-
# Block comments use * on subsequent lines
if ($prevline =~ /$;[ \t]*$/ && #ends in comment
$prevrawline =~ /^\+.*?\/\*/ && #starting /*
@@ -4824,7 +4824,7 @@ sub process {
}
# do not use BUG() or variants
- if ($line =~ /\b(?!AA_|BUILD_|DCCP_|IDA_|KVM_|RWLOCK_|snd_|SPIN_)(?:[a-zA-Z_]*_)?BUG(?:_ON)?(?:_[A-Z_]+)?\s*\(/) {
+ if ($line =~ /\b(?!AA_|BUILD_|IDA_|KVM_|RWLOCK_|snd_|SPIN_)(?:[a-zA-Z_]*_)?BUG(?:_ON)?(?:_[A-Z_]+)?\s*\(/) {
my $msg_level = \&WARN;
$msg_level = \&CHK if ($file);
&{$msg_level}("AVOID_BUG",
@@ -5510,9 +5510,9 @@ sub process {
}
}
-# check for unnecessary parentheses around comparisons in if uses
-# when !drivers/staging or command-line uses --strict
- if (($realfile !~ m@^(?:drivers/staging/)@ || $check_orig) &&
+# check for unnecessary parentheses around comparisons
+# except in drivers/staging
+ if (($realfile !~ m@^(?:drivers/staging/)@) &&
$perl_version_ok && defined($stat) &&
$stat =~ /(^.\s*if\s*($balanced_parens))/) {
my $if_stat = $1;
@@ -5840,6 +5840,8 @@ sub process {
#CamelCase
if ($var !~ /^$Constant$/ &&
$var =~ /[A-Z][a-z]|[a-z][A-Z]/ &&
+#Ignore C keywords
+ $var !~ /^_Generic$/ &&
#Ignore some autogenerated defines and enum values
$var !~ /^(?:[A-Z]+_){1,5}[A-Z]{1,3}[a-z]/ &&
#Ignore Page<foo> variants
@@ -5901,9 +5903,9 @@ sub process {
}
}
-# multi-statement macros should be enclosed in a do while loop, grab the
-# first statement and ensure its the whole macro if its not enclosed
-# in a known good container
+# Usually multi-statement macros should be enclosed in a do {} while
+# (0) loop. Grab the first statement and ensure its the whole macro
+# if its not enclosed in a known good container
if ($realfile !~ m@/vmlinux.lds.h$@ &&
$line =~ /^.\s*\#\s*define\s*$Ident(\()?/) {
my $ln = $linenr;
@@ -5956,10 +5958,13 @@ sub process {
my $exceptions = qr{
$Declare|
+ # named exceptions
module_param_named|
MODULE_PARM_DESC|
DECLARE_PER_CPU|
DEFINE_PER_CPU|
+ static_assert|
+ # declaration primitives
__typeof__\(|
union|
struct|
@@ -5994,11 +5999,11 @@ sub process {
ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE",
"Macros starting with if should be enclosed by a do - while loop to avoid possible if/else logic defects\n" . "$herectx");
} elsif ($dstat =~ /;/) {
- ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE",
- "Macros with multiple statements should be enclosed in a do - while loop\n" . "$herectx");
+ WARN("MULTISTATEMENT_MACRO_USE_DO_WHILE",
+ "Non-declarative macros with multiple statements should be enclosed in a do - while loop\n" . "$herectx\nBUT SEE:\n$DO_WHILE_0_ADVICE");
} else {
ERROR("COMPLEX_MACRO",
- "Macros with complex values should be enclosed in parentheses\n" . "$herectx");
+ "Macros with complex values should be enclosed in parentheses\n" . "$herectx\nBUT SEE:\n$DO_WHILE_0_ADVICE");
}
}
@@ -6040,6 +6045,12 @@ sub process {
CHK("MACRO_ARG_PRECEDENCE",
"Macro argument '$arg' may be better as '($arg)' to avoid precedence issues\n" . "$herectx");
}
+
+# check if this is an unused argument
+ if ($define_stmt !~ /\b$arg\b/ && $define_stmt) {
+ WARN("MACRO_ARG_UNUSED",
+ "Argument '$arg' is not used in function-like macro\n" . "$herectx");
+ }
}
# check for macros with flow control, but without ## concatenation
@@ -6583,11 +6594,11 @@ sub process {
# ignore udelay's < 10, however
if (! ($delay < 10) ) {
CHK("USLEEP_RANGE",
- "usleep_range is preferred over udelay; see Documentation/timers/timers-howto.rst\n" . $herecurr);
+ "usleep_range is preferred over udelay; see function description of usleep_range() and udelay().\n" . $herecurr);
}
if ($delay > 2000) {
WARN("LONG_UDELAY",
- "long udelay - prefer mdelay; see arch/arm/include/asm/delay.h\n" . $herecurr);
+ "long udelay - prefer mdelay; see function description of mdelay().\n" . $herecurr);
}
}
@@ -6595,7 +6606,7 @@ sub process {
if ($line =~ /\bmsleep\s*\((\d+)\);/) {
if ($1 < 20) {
WARN("MSLEEP",
- "msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.rst\n" . $herecurr);
+ "msleep < 20ms can sleep for up to 20ms; see function description of msleep().\n" . $herecurr);
}
}
@@ -6903,7 +6914,7 @@ sub process {
($extension eq "f" &&
defined $qualifier && $qualifier !~ /^w/) ||
($extension eq "4" &&
- defined $qualifier && $qualifier !~ /^cc/)) {
+ defined $qualifier && $qualifier !~ /^c(?:[hlbc]|hR)$/)) {
$bad_specifier = $specifier;
last;
}
@@ -7063,11 +7074,11 @@ sub process {
my $max = $7;
if ($min eq $max) {
WARN("USLEEP_RANGE",
- "usleep_range should not use min == max args; see Documentation/timers/timers-howto.rst\n" . "$here\n$stat\n");
+ "usleep_range should not use min == max args; see function description of usleep_range().\n" . "$here\n$stat\n");
} elsif ($min =~ /^\d+$/ && $max =~ /^\d+$/ &&
$min > $max) {
WARN("USLEEP_RANGE",
- "usleep_range args reversed, use min then max; see Documentation/timers/timers-howto.rst\n" . "$here\n$stat\n");
+ "usleep_range args reversed, use min then max; see function description of usleep_range().\n" . "$here\n$stat\n");
}
}
@@ -7691,6 +7702,12 @@ sub process {
ERROR("NOT_UNIFIED_DIFF",
"Does not appear to be a unified-diff format patch\n");
}
+ if ($is_patch && $has_commit_log && $chk_fixes_tag) {
+ if ($needs_fixes_tag ne "" && !$is_revert && !$fixes_tag) {
+ WARN("MISSING_FIXES_TAG",
+ "The commit message has '$needs_fixes_tag', perhaps it also needs a 'Fixes:' tag?\n");
+ }
+ }
if ($is_patch && $has_commit_log && $chk_signoff) {
if ($signoff == 0) {
ERROR("MISSING_SIGN_OFF",