Commit 15d4734c authored by Jim Cromie's avatar Jim Cromie Committed by Andrew Morton
Browse files

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: default avatarJim 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: default avatarAndrew Morton <akpm@linux-foundation.org>
parent df3d5274
Loading
Loading
Loading
Loading
+27 −6
Original line number Diff line number Diff line
@@ -151,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{$_}++ } @_;
@@ -5883,9 +5901,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;
@@ -5938,10 +5956,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|
@@ -5976,11 +5997,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");
				}

			}