#!/usr/bin/env perl
-# SPDX-License-Identifier: GPL-2.0
+# SPDX-License-Identifier: GPL-2.0-or-later
#
# (c) 2001, Dave Jones. (the file handling bit)
# (c) 2005, Joel Schopp <jschopp@austin.ibm.com> (the ugly bit)
use Getopt::Long qw(:config no_auto_abbrev);
+my $frr = 1;
+
my $quiet = 0;
my $verbose = 0;
my %verbose_messages = ();
my $codespellfile = "/usr/share/codespell/dictionary.txt";
my $user_codespellfile = "";
my $conststructsfile = "$D/const_structs.checkpatch";
-my $docsfile = "$D/../Documentation/dev-tools/checkpatch.rst";
+my $docsfile = "$D/../doc/developer/checkpatch.rst";
my $typedefsfile;
my $color = "auto";
-my $allow_c99_comments = 1; # Can be overridden by --ignore C99_COMMENT_TOLERANCE
+my $allow_c99_comments = 0; # Not in FRR.
+#my $allow_c99_comments = 1; # Can be overridden by --ignore C99_COMMENT_TOLERANCE
# git output parsing needs US English output, so first set backtick child process LANGUAGE
my $git_command ='export LANGUAGE=en_US.UTF-8; git';
my $tabsize = 8;
# Notes to $Attribute:
# We need \b after 'init' otherwise 'initconst' will cause a false positive in a check
our $Attribute = qr{
+ _Atomic|
const|
volatile|
__percpu|
}x;
our $c90_Keywords = qr{do|for|while|if|else|return|goto|continue|switch|default|case|break}x;
+our $Iterators = qr{
+ frr_each|frr_each_safe|frr_each_from|
+ frr_with_mutex|frr_with_privs|
+ LIST_FOREACH|LIST_FOREACH_SAFE|
+ SLIST_FOREACH|SLIST_FOREACH_SAFE|SLIST_FOREACH_PREVPTR|
+ STAILQ_FOREACH|STAILQ_FOREACH_SAFE|
+ TAILQ_FOREACH|TAILQ_FOREACH_SAFE|TAILQ_FOREACH_REVERSE|TAILQ_FOREACH_REVERSE_SAFE|
+ RB_FOREACH|RB_FOREACH_SAFE|RB_FOREACH_REVERSE|RB_FOREACH_REVERSE_SAFE|
+ SPLAY_FOREACH|
+ FOR_ALL_INTERFACES|FOR_ALL_INTERFACES_ADDRESSES|JSON_FOREACH|
+ LY_FOR_KEYS|LY_LIST_FOR|LY_TREE_FOR|LY_TREE_DFS_BEGIN|LYD_TREE_DFS_BEGIN|
+ RE_DEST_FOREACH_ROUTE|RE_DEST_FOREACH_ROUTE_SAFE|
+ RNODE_FOREACH_RE|RNODE_FOREACH_RE_SAFE|
+ UPDGRP_FOREACH_SUBGRP|UPDGRP_FOREACH_SUBGRP_SAFE|
+ SUBGRP_FOREACH_PEER|SUBGRP_FOREACH_PEER_SAFE|
+ SUBGRP_FOREACH_ADJ|SUBGRP_FOREACH_ADJ_SAFE|
+ AF_FOREACH|FOREACH_AFI_SAFI|FOREACH_SAFI|
+ LSDB_LOOP
+ }x;
our $BasicType;
our $NonptrType;
$spelling_fix{$suspect} = $fix;
}
close($spelling);
-} else {
+} elsif (!$frr) {
warn "No typos will be found - file '$spelling_file': $!\n";
}
}
my $const_structs;
-if (show_type("CONST_STRUCT")) {
+if (!$frr &&
+ show_type("CONST_STRUCT")) {
read_words(\$const_structs, $conststructsfile)
or warn "No structs that should be const will be found - file '$conststructsfile': $!\n";
}
my ($root) = @_;
my @tree_check = (
- "COPYING", "CREDITS", "Kbuild", "MAINTAINERS", "Makefile",
- "README", "Documentation", "arch", "include", "drivers",
- "fs", "init", "ipc", "kernel", "lib", "scripts",
+ "COPYING", "configure.ac", "Makefile.am",
+ "README.md", "doc", "lib", "vtysh", "watchfrr", "tests",
+ "zebra", "bgpd", "ospfd", "ospf6d", "isisd", "staticd",
+
);
foreach my $check (@tree_check) {
$realfile =~ m@/bpf/.*\.bpf\.c$@;
}
+sub remove_defuns {
+ my @breakfast = ();
+ my $milktoast;
+ for my $tasty (@rawlines) {
+ $milktoast = $tasty;
+ if (($tasty =~ /^\+DEFPY/ ||
+ $tasty =~ /^\+DEFUN/ ||
+ $tasty =~ /^\+ALIAS/) .. ($tasty =~ /^\+\{/)) {
+ $milktoast = "\n";
+ }
+ push(@breakfast, $milktoast);
+ }
+ @rawlines = @breakfast;
+}
+
sub process {
my $filename = shift;
my $checklicenseline = 1;
sanitise_line_reset();
+ remove_defuns();
+
my $line;
foreach my $rawline (@rawlines) {
$linenr++;
$commit_log_lines++; #could be a $signature
}
} elsif ($has_commit_log && $commit_log_lines < 2) {
- WARN("COMMIT_MESSAGE",
- "Missing commit description - Add an appropriate one\n");
+ # FRR: not used by FRR
+ # WARN("COMMIT_MESSAGE",
+ # "Missing commit description - Add an appropriate one\n");
$commit_log_lines = 2; #warn only once
}
(defined($1) || defined($2))))) {
$is_patch = 1;
$reported_maintainer_file = 1;
- WARN("FILE_PATH_CHANGES",
- "added, moved or deleted file(s), does MAINTAINERS need updating?\n" . $herecurr);
+ # WARN("FILE_PATH_CHANGES",
+ # "added, moved or deleted file(s), does MAINTAINERS need updating?\n" . $herecurr);
}
# Check for adding new DT bindings not in schema format
$checklicenseline = 2;
} elsif ($rawline =~ /^\+/) {
my $comment = "";
- if ($realfile =~ /\.(h|s|S)$/) {
+ if (!$frr &&
+ $realfile =~ /\.(h|s|S)$/) {
$comment = '/*';
+ } elsif ($frr &&
+ $realfile =~ /\.(h|s|S)$/) {
+ $comment = '//';
} elsif ($realfile =~ /\.(c|dts|dtsi)$/) {
$comment = '//';
} elsif (($checklicenseline == 2) || $realfile =~ /\.(sh|pl|py|awk|tc|yaml)$/) {
# if/while/etc brace do not go on next line, unless defining a do while loop,
# or if that brace on the next line is for something else
- if ($line =~ /(.*)\b((?:if|while|for|switch|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|do\b|else\b)/ && $line !~ /^.\s*\#/) {
+ if ($line =~ /(.*)\b((?:if|while|for|switch|(?:[a-z_]+|)frr_(each|with)[a-z_]+)\s*\(|do\b|else\b)/ && $line !~ /^.\s*\#/) {
my $pre_ctx = "$1$2";
my ($level, @ctx) = ctx_statement_level($linenr, $realcnt, 0);
}
# Check relative indent for conditionals and blocks.
- if ($line =~ /\b(?:(?:if|while|for|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|(?:do|else)\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) {
+ if ($line =~ /\b(?:(?:if|while|for|(?:[a-z_]+|)frr_(each|with)[a-z_]+)\s*\(|(?:do|else)\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) {
($stat, $cond, $line_nr_next, $remain_next, $off_next) =
ctx_statement_block($linenr, $realcnt, 0)
if (!defined $stat);
# no C99 // comments
if ($line =~ m{//}) {
- if (ERROR("C99_COMMENTS",
+ if (!$allow_c99_comments && ERROR("C99_COMMENTS",
"do not use C99 // comments\n" . $herecurr) &&
$fix) {
my $line = $fixed[$fixlinenr];
my $comment = trim($1);
$fixed[$fixlinenr] =~ s@\/\/(.*)$@/\* $comment \*/@;
}
+ } else {
+ WARN("C99_COMMENTS",
+ "C99 // comments do not match recommendation\n" . $herecurr);
}
}
# Remove C99 comments.
if|for|while|switch|return|case|
volatile|__volatile__|
__attribute__|format|__extension__|
- asm|__asm__)$/x)
+ asm|__asm__|
+ $Iterators)$/x)
{
# cpp #define statements have non-optional spaces, ie
# if there is a space between the name and the open
# likely a typedef for a function.
} elsif ($ctx =~ /$Type$/) {
+ # All-uppercase function names are usually macros,
+ # ignore those
+ } elsif ($name eq uc $name) {
+
} else {
if (WARN("SPACING",
"space prohibited between function name and open parenthesis '('\n" . $herecurr) &&
} elsif ($op eq ',') {
my $rtrim_before = 0;
my $space_after = 0;
- if ($ctx =~ /Wx./) {
+ if ($line=~/\#\s*define/) {
+ # FRR: ignore , spacing in macros
+ } elsif ($ctx =~ /Wx./) {
if (ERROR("SPACING",
"space prohibited before that '$op' $at\n" . $hereptr)) {
$line_fixed = 1;
# and ignore bitfield definitions like foo:1
# Strictly, labels can have whitespace after the identifier and before the :
# but this is not allowed here as many ?: uses would appear to be labels
- if ($sline =~ /^.\s+[A-Za-z_][A-Za-z\d_]*:(?!\s*\d+)/ &&
+ # FRR: has indented labels
+ if (!$frr &&
+ $sline =~ /^.\s+[A-Za-z_][A-Za-z\d_]*:(?!\s*\d+)/ &&
$sline !~ /^. [A-Za-z\d_][A-Za-z\d_]*:/ &&
$sline !~ /^.\s+default:/) {
if (WARN("INDENTED_LABEL",
my $ctx = '';
my $has_flow_statement = 0;
my $has_arg_concat = 0;
+ my $complex = 0;
($dstat, $dcond, $ln, $cnt, $off) =
ctx_statement_block($linenr, $realcnt, 0);
$ctx = $dstat;
$define_args =~ s/\s*//g;
$define_args =~ s/\\\+?//g;
@def_args = split(",", $define_args);
+ $complex = 1;
}
$dstat =~ s/$;//g;
} elsif ($dstat =~ /;/) {
ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE",
"Macros with multiple statements should be enclosed in a do - while loop\n" . "$herectx");
- } else {
+ } elsif ($complex) {
ERROR("COMPLEX_MACRO",
"Macros with complex values should be enclosed in parentheses\n" . "$herectx");
}
}
}
+ if (!defined $suppress_ifbraces{$linenr - 1} &&
+ $line =~ /\b(frr_with_)/) {
+ my ($level, $endln, @chunks) =
+ ctx_statement_full($linenr, $realcnt, $-[0]);
+
+ # Check the condition.
+ my ($cond, $block) = @{$chunks[0]};
+ #print "CHECKING<$linenr> cond<$cond> block<$block>\n";
+ if (defined $cond) {
+ substr($block, 0, length($cond), '');
+ }
+
+ if ($level == 0 && $block !~ /^\s*\{/) {
+ my $herectx = $here . "\n";
+ my $cnt = statement_rawlines($block);
+
+ for (my $n = 0; $n < $cnt; $n++) {
+ $herectx .= raw_line($linenr, $n) . "\n";
+ }
+
+ WARN("BRACES",
+ "braces {} are mandatory for frr_with_* blocks\n" . $herectx);
+ }
+ }
+
# check for single line unbalanced braces
if ($sline =~ /^.\s*\}\s*else\s*$/ ||
$sline =~ /^.\s*else\s*\{\s*$/) {
# uncoalesced string fragments
if ($line =~ /$String\s*[Lu]?"/) {
- if (WARN("STRING_FRAGMENTS",
+ # FRR: WARN -> CHK
+ if (CHK("STRING_FRAGMENTS",
"Consecutive strings are generally better as a single string\n" . $herecurr) &&
$fix) {
while ($line =~ /($String)(?=\s*")/g) {
}
# Check for compiler attributes
- if ($realfile !~ m@\binclude/uapi/@ &&
+ # FRR: doesn't use kernel macro replacements for compiler attributes.
+ if (!$frr &&
+ $realfile !~ m@\binclude/uapi/@ &&
$rawline =~ /\b__attribute__\s*\(\s*($balanced_parens)\s*\)/) {
my $attr = $1;
$attr =~ s/\s*\(\s*(.*)\)\s*/$1/;
}
# Check for __attribute__ weak, or __weak declarations (may have link issues)
- if ($perl_version_ok &&
+ if (!$frr &&
+ $perl_version_ok &&
$line =~ /(?:$Declare|$DeclareMisordered)\s*$Ident\s*$balanced_parens\s*(?:$Attribute)?\s*;/ &&
($line =~ /\b__attribute__\s*\(\s*\(.*\bweak\b/ ||
$line =~ /\b__weak\b/)) {
}
# check for c99 types like uint8_t used outside of uapi/ and tools/
- if ($realfile !~ m@\binclude/uapi/@ &&
+ if (!$frr &&
+ $realfile !~ m@\binclude/uapi/@ &&
$realfile !~ m@\btools/@ &&
$line =~ /\b($Declare)\s*$Ident\s*[=;,\[]/) {
my $type = $1;
}
# check for vsprintf extension %p<foo> misuses
- if ($perl_version_ok &&
+ if (!$frr &&
+ $perl_version_ok &&
defined $stat &&
$stat =~ /^\+(?![^\{]*\{\s*).*\b(\w+)\s*\(.*$String\s*,/s &&
$1 !~ /^_*volatile_*$/) {
# }
# strlcpy uses that should likely be strscpy
- if ($line =~ /\bstrlcpy\s*\(/) {
+ if (!$frr &&
+ $line =~ /\bstrlcpy\s*\(/) {
WARN("STRLCPY",
"Prefer strscpy over strlcpy - see: https://lore.kernel.org/r/CAHk-=wgfRnXz0W3D37d01q3JFkr_i_uTL=V6A6G1oUZcprmknw\@mail.gmail.com/\n" . $herecurr);
}
}
# check for simple sscanf that should be kstrto<foo>
- if ($perl_version_ok &&
+ if (!$frr &&
+ $perl_version_ok &&
defined $stat &&
$line =~ /\bsscanf\b/) {
my $lc = $stat =~ tr@\n@@;
}
# recommend kstrto* over simple_strto* and strict_strto*
- if ($line =~ /\b((simple|strict)_(strto(l|ll|ul|ull)))\s*\(/) {
+ if (!$frr &&
+ $line =~ /\b((simple|strict)_(strto(l|ll|ul|ull)))\s*\(/) {
WARN("CONSIDER_KSTRTO",
"$1 is obsolete, use k$3 instead\n" . $herecurr);
}
# check for various structs that are normally const (ops, kgdb, device_tree)
# and avoid what seem like struct definitions 'struct foo {'
- if (defined($const_structs) &&
+ if (!$frr &&
+ defined($const_structs) &&
$line !~ /\bconst\b/ &&
$line =~ /\bstruct\s+($const_structs)\b(?!\s*\{)/) {
WARN("CONST_STRUCT",
WARN("DUPLICATED_SYSCTL_CONST",
"duplicated sysctl range checking value '$1', consider using the shared one in include/linux/sysctl.h\n" . $herecurr);
}
+
+# check for usage of nonstandard fixed-width integral types
+ if ($line =~ /u_int8_t/ ||
+ $line =~ /u_int32_t/ ||
+ $line =~ /u_int16_t/ ||
+ $line =~ /u_int64_t/ ||
+ $line =~ /[^a-z_]u_char[^a-z_]/ ||
+ $line =~ /[^a-z_]u_short[^a-z_]/ ||
+ $line =~ /[^a-z_]u_int[^a-z_]/ ||
+ $line =~ /[^a-z_]u_long[^a-z_]/) {
+ ERROR("NONSTANDARD_INTEGRAL_TYPES",
+ "Please, no nonstandard integer types in new code.\n" . $herecurr)
+ }
+
+# check for usage of non-32 bit atomics
+ if ($line =~ /_Atomic [u]?int(?!32)[0-9]+_t/) {
+ WARN("NON_32BIT_ATOMIC",
+ "Please, only use 32 bit atomics.\n" . $herecurr);
+ }
+
+# check for use of strcpy()
+ if ($line =~ /\bstrcpy\s*\(.*\)/) {
+ ERROR("STRCPY",
+ "strcpy() is error-prone; please use strlcpy()" . $herecurr);
+ }
+
+# check for use of strncpy()
+ if ($line =~ /\bstrncpy\s*\(.*\)/) {
+ WARN("STRNCPY",
+ "strncpy() is error-prone; please use strlcpy() if possible, or memcpy()" . $herecurr);
+ }
+
+# check for use of strcat()
+ if ($line =~ /\bstrcat\s*\(.*\)/) {
+ ERROR("STRCAT",
+ "strcat() is error-prone; please use strlcat() if possible" . $herecurr);
+ }
+
+# check for use of strncat()
+ if ($line =~ /\bstrncat\s*\(.*\)/) {
+ WARN("STRNCAT",
+ "strncat() is error-prone; please use strlcat() if possible" . $herecurr);
+ }
+
+# check for use of bzero()
+ if ($line =~ /\bbzero\s*\(.*\)/) {
+ ERROR("BZERO",
+ "bzero() is deprecated; use memset()" . $herecurr);
+ }
}
# If we have no input at all, then there is nothing to report on