checkpatch: qualify do-while-0 advice

Add a paragraph of advice qualifying the general do-while-0 advice, noting
3 possible misguidings.  reduce one ERROR to WARN, for the case I actually
encountered.

And add 'static_assert' to named exceptions, along with some additional
comments about named exceptions vs (detection of) declarative construction
primitives (union, struct, [], etc).

Link: https://lkml.kernel.org/r/20250325235156.663269-3-jim.cromie@gmail.com
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
Cc: Andy Whitcroft <apw@canonical.com>
Cc: Joe Perches <joe@perches.com>
Cc: Dwaipayan Ray <dwaipayanray1@gmail.com>
Cc: Lukas Bulwahn <lukas.bulwahn@gmail.com>
Cc: Louis Chauvet <louis.chauvet@bootlin.com>
Cc: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
Jim Cromie 2025-03-25 17:51:55 -06:00 committed by Andrew Morton
parent df3d527495
commit 15d4734c7a

View file

@ -151,6 +151,24 @@ EOM
exit($exitcode); 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 { sub uniq {
my %seen; my %seen;
return grep { !$seen{$_}++ } @_; return grep { !$seen{$_}++ } @_;
@ -5883,9 +5901,9 @@ sub process {
} }
} }
# multi-statement macros should be enclosed in a do while loop, grab the # Usually multi-statement macros should be enclosed in a do {} while
# first statement and ensure its the whole macro if its not enclosed # (0) loop. Grab the first statement and ensure its the whole macro
# in a known good container # if its not enclosed in a known good container
if ($realfile !~ m@/vmlinux.lds.h$@ && if ($realfile !~ m@/vmlinux.lds.h$@ &&
$line =~ /^.\s*\#\s*define\s*$Ident(\()?/) { $line =~ /^.\s*\#\s*define\s*$Ident(\()?/) {
my $ln = $linenr; my $ln = $linenr;
@ -5938,10 +5956,13 @@ sub process {
my $exceptions = qr{ my $exceptions = qr{
$Declare| $Declare|
# named exceptions
module_param_named| module_param_named|
MODULE_PARM_DESC| MODULE_PARM_DESC|
DECLARE_PER_CPU| DECLARE_PER_CPU|
DEFINE_PER_CPU| DEFINE_PER_CPU|
static_assert|
# declaration primitives
__typeof__\(| __typeof__\(|
union| union|
struct| struct|
@ -5976,11 +5997,11 @@ sub process {
ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE", 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"); "Macros starting with if should be enclosed by a do - while loop to avoid possible if/else logic defects\n" . "$herectx");
} elsif ($dstat =~ /;/) { } elsif ($dstat =~ /;/) {
ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE", WARN("MULTISTATEMENT_MACRO_USE_DO_WHILE",
"Macros with multiple statements should be enclosed in a do - while loop\n" . "$herectx"); "Non-declarative macros with multiple statements should be enclosed in a do - while loop\n" . "$herectx\nBUT SEE:\n$DO_WHILE_0_ADVICE");
} else { } else {
ERROR("COMPLEX_MACRO", 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");
} }
} }