checkpatch: extend attributes check to handle more patterns

It is generally preferred that the macros from
include/linux/compiler_attributes.h are used, unless there is a reason not
to.

checkpatch currently checks __attribute__ for each of packed, aligned,
section, printf, scanf, and weak.  Other declarations in
compiler_attributes.h are not handled.

Add a generic test to check the presence of such attributes.  Some
attributes require more specific handling and are kept separate.

Also add fixes to the generic attributes check to substitute the correct
conversions.

New attributes which are now handled are:

__always_inline__
__assume_aligned__(a, ## __VA_ARGS__)
__cold__
__const__
__copy__(symbol)
__designated_init__
__externally_visible__
__gnu_inline__
__malloc__
__mode__(x)
__no_caller_saved_registers__
__noclone__
__noinline__
__nonstring__
__noreturn__
__pure__
__unused__
__used__

Declarations which contain multiple attributes like
__attribute__((__packed__, __cold__)) are also handled except when proper
conversions for one or more attributes of the list cannot be determined.

Link: https://lore.kernel.org/linux-kernel-mentees/3ec15b41754b01666d94b76ce51b9832c2dd577a.camel@perches.com/
Link: https://lkml.kernel.org/r/20201025193103.23223-1-dwaipayanray1@gmail.com
Signed-off-by: Dwaipayan Ray <dwaipayanray1@gmail.com>
Suggested-by: Joe Perches <joe@perches.com>
Acked-by: Joe Perches <joe@perches.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Dwaipayan Ray 2020-12-15 20:44:36 -08:00 committed by Linus Torvalds
parent dc58bc553e
commit 7ebe1d173c
1 changed files with 70 additions and 37 deletions

View File

@ -6187,50 +6187,83 @@ sub process {
}
}
# Check for __attribute__ packed, prefer __packed
# Check for compiler attributes
if ($realfile !~ m@\binclude/uapi/@ &&
$line =~ /\b__attribute__\s*\(\s*\(.*\bpacked\b/) {
WARN("PREFER_PACKED",
"__packed is preferred over __attribute__((packed))\n" . $herecurr);
}
$rawline =~ /\b__attribute__\s*\(\s*($balanced_parens)\s*\)/) {
my $attr = $1;
$attr =~ s/\s*\(\s*(.*)\)\s*/$1/;
# Check for __attribute__ aligned, prefer __aligned
if ($realfile !~ m@\binclude/uapi/@ &&
$line =~ /\b__attribute__\s*\(\s*\(.*aligned/) {
WARN("PREFER_ALIGNED",
"__aligned(size) is preferred over __attribute__((aligned(size)))\n" . $herecurr);
}
my %attr_list = (
"aligned" => "__aligned",
"always_inline" => "__always_inline",
"assume_aligned" => "__assume_aligned",
"cold" => "__cold",
"const" => "__attribute_const__",
"copy" => "__copy",
"designated_init" => "__designated_init",
"externally_visible" => "__visible",
"format" => "printf|scanf",
"gnu_inline" => "__gnu_inline",
"malloc" => "__malloc",
"mode" => "__mode",
"no_caller_saved_registers" => "__no_caller_saved_registers",
"noclone" => "__noclone",
"noinline" => "noinline",
"nonstring" => "__nonstring",
"noreturn" => "__noreturn",
"packed" => "__packed",
"pure" => "__pure",
"used" => "__used"
);
# Check for __attribute__ section, prefer __section
if ($realfile !~ m@\binclude/uapi/@ &&
$line =~ /\b__attribute__\s*\(\s*\(.*_*section_*\s*\(\s*("[^"]*")/) {
my $old = substr($rawline, $-[1], $+[1] - $-[1]);
my $new = substr($old, 1, -1);
if (WARN("PREFER_SECTION",
"__section($new) is preferred over __attribute__((section($old)))\n" . $herecurr) &&
$fix) {
$fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*_*section_*\s*\(\s*\Q$old\E\s*\)\s*\)\s*\)/__section($new)/;
my @conv_array = ();
my $conv_possible = 1;
while ($attr =~ /\s*(\w+)\s*(${balanced_parens})?/g) {
my $curr_attr = $1;
my $params = '';
$params = $2 if defined($2);
$curr_attr =~ s/^[\s_]+|[\s_]+$//g;
if (exists($attr_list{$curr_attr})) {
if ($curr_attr eq "format" && $params) {
$params =~ /^\s*\(\s*(\w+)\s*,\s*(.*)/;
push(@conv_array, "__$1\($2");
} else {
my $new = $attr_list{$curr_attr};
push(@conv_array, "$new$params");
}
} else {
$conv_possible = 0;
last;
}
}
}
# Check for __attribute__ format(printf, prefer __printf
if ($realfile !~ m@\binclude/uapi/@ &&
$line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf/) {
if (WARN("PREFER_PRINTF",
"__printf(string-index, first-to-check) is preferred over __attribute__((format(printf, string-index, first-to-check)))\n" . $herecurr) &&
$fix) {
$fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf\s*,\s*(.*)\)\s*\)\s*\)/"__printf(" . trim($1) . ")"/ex;
if (scalar @conv_array > 0 && $conv_possible == 1) {
my $replace = join(' ', @conv_array);
if (WARN("PREFER_DEFINED_ATTRIBUTE_MACRO",
"$replace is preferred over __attribute__(($attr))\n" . $herecurr) &&
$fix) {
$fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*\Q$attr\E\s*\)\s*\)/$replace/;
$fixed[$fixlinenr] =~ s/\}\Q$replace\E/} $replace/;
}
}
}
# Check for __attribute__ format(scanf, prefer __scanf
if ($realfile !~ m@\binclude/uapi/@ &&
$line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\b/) {
if (WARN("PREFER_SCANF",
"__scanf(string-index, first-to-check) is preferred over __attribute__((format(scanf, string-index, first-to-check)))\n" . $herecurr) &&
$fix) {
$fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\s*,\s*(.*)\)\s*\)\s*\)/"__scanf(" . trim($1) . ")"/ex;
# Check for __attribute__ section, prefer __section
if ($attr =~ /^_*section_*\s*\(\s*("[^"]*")/) {
my $old = substr($attr, $-[1], $+[1] - $-[1]);
my $new = substr($old, 1, -1);
if (WARN("PREFER_DEFINED_ATTRIBUTE_MACRO",
"__section($new) is preferred over __attribute__((section($old)))\n" . $herecurr) &&
$fix) {
$fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*_*section_*\s*\(\s*\Q$old\E\s*\)\s*\)\s*\)/__section($new)/;
}
}
# Check for __attribute__ unused, prefer __always_unused or __maybe_unused
if ($attr =~ /^_*unused/) {
WARN("PREFER_DEFINED_ATTRIBUTE_MACRO",
"__always_unused or __maybe_unused is preferred over __attribute__((__unused__))\n" . $herecurr);
}
}