in meta/style.pm [879:1284]
sub CheckHeadersStyle
{
#
# Purpose of this check is to find out
# whether header files are correctly formated
#
# Wrong formating includes:
# - multiple empty lines
# - double spaces
# - wrong spacing idient
#
# we could put that to local dictionary file
my @acronyms = GetAcronyms();
my @spellExceptions = qw/ CRC32 IPv4 IPv6 BGPv6 6th 0xFF /;
my %exceptions = map { $_ => $_ } @spellExceptions;
my %wordsToCheck = ();
my %wordsChecked = ();
CheckMetadataSourceFiles();
my @headers = GetHeaderFiles();
my @metaheaders = GetMetaHeaderFiles();
my @exheaders = GetExperimentalHeaderFiles();
@headers = (@headers, @metaheaders, @exheaders);
for my $header (@headers)
{
next if $header eq "saimetadata.h"; # skip auto generated header
next if $header eq "saimetadatasize.h"; # skip auto generated header
my $data = ReadHeaderFile($header);
my $oncedef = uc ("__${header}_");
$oncedef =~ s/\./_/g;
my $oncedefCount = 0;
CheckHeaderLicense($data, $header);
CheckFunctionsParams($data, $header);
CheckDoxygenCommentFormating($data, $header);
CheckQuadApi($data, $header);
CheckStructAlignment($data, $header);
CheckNonDoxygenComments($data, $header);
CheckSwitchKeys($data, $header) if $header eq "saiswitch.h";
CheckDoxygenSpacing($data, $header);
my @lines = split/\n/,$data;
my $n = 0;
my $empty = 0;
my $emptydoxy = 0;
for my $line (@lines)
{
$n++;
CheckFunctionNaming($header, $n, $line);
CheckInOutParams($header, $n, $line);
$oncedefCount++ if $line =~ /\b$oncedef\b/;
# detect multiple empty lines
if ($line =~ /^$/)
{
$empty++;
if ($empty > 1)
{
LogWarning "header contains two empty lines one after another $header $n";
}
}
else { $empty = 0 }
# detect multiple empty lines in doxygen comments
if ($line =~ /^\s+\*\s*$/)
{
$emptydoxy++;
if ($emptydoxy > 1)
{
LogWarning "header contains two empty lines in doxygen $header $n";
}
}
else { $emptydoxy = 0 }
if ($line =~ /^\s+\* / and not $line =~ /\*( {4}| {8}| )[^ ]/)
{
LogWarning "not expected number of spaces after * (1,4,8) $header $n:$line";
}
if ($line =~ /\*\s+[^ ].* / and not $line =~ /\* \@(brief|file|note)/)
{
if (not $line =~ /const.+const\s+\w+;/ and not $line =~ m!\\$!)
{
LogWarning "too many spaces after *\\s+ $header $n:$line";
}
}
if ($line =~ /(typedef|{|}|_In\w+|_Out\w+)( [^ ].* | )/ and not $line =~ /typedef\s+u?int/i and not $line =~ m!\\$!)
{
LogWarning "too many spaces $header $n:$line";
}
if ($line =~ m!/\*\*! and not $line =~ m!/\*\*\s*$! and not $line =~ m!/\*\*.+\*/!)
{
LogWarning "multiline doxygen comment should start '/**' $header $n:$line";
}
if ($line =~ m![^ ]\*/!)
{
LogWarning "coment is ending without space $header $n:$line";
}
if ($line =~ /^\s*sai_(\w+)_fn\s+(\w+);/)
{
# make struct function members to follow convention
LogWarning "$2 should be equal to $1" if (($1 ne $2) and not($1 =~ /^bulk/))
}
if ($line =~ /_(?:In|Out)\w+\s+(?:sai_)?uint32_t\s*\*?(\w+)/)
{
my $param = $1;
my $pattern = '^(attr_count|object_count|number_of_counters|count|u32|device_addr|start_reg_addr|number_of_registers|reg_val)$';
if (not $param =~ /$pattern/)
{
LogWarning "param $param should match $pattern $header:$n:$line";
}
}
if ($line =~ /typedef.+_fn\s*\)/ and not $line =~ /typedef( \S+)+ \(\*\w+_fn\)\(/)
{
LogWarning "wrong style typedef function definition $header:$n:$line";
}
if ($line =~ / ([.,:;)])/ and not $line =~ /\.(1D|1Q|\.\.)/)
{
LogWarning "space before '$1' : $header:$n:$line";
}
if ($line =~ / \* / and not $line =~ /^\s*\* / and not $line =~ /^#define/)
{
LogWarning "floating star $header:$n:$line";
}
if ($line =~ /}[^ ]/)
{
LogWarning "no space after '}' $header:$n:$line" if (not $line =~ /^\s*\* /);
}
if ($line =~ /_[IO].+\w+\*\*? /)
{
LogWarning "star should be next to param name $header:$n:$line";
}
if ($line =~ /_In_ .+\*/ and not $line =~ /_In_ const/)
{
LogWarning "pointer input parameters should be const $header:$n:$line";
}
if ($line =~ /[^ ]\s*_(In|Out|Inout)_/ and not $line =~ /^#define/)
{
LogWarning "each param should be in separate line $header:$n:$line";
}
if ($line =~ m!/\*\*\s+[a-z]!)
{
LogWarning "doxygen comment should start with capital letter: $header:$n:$line";
}
if ($line =~ /sai_\w+_statistics_fn/)
{
LogWarning "statistics should use 'stats' to follow convention $header:$n:$line";
}
if ($line =~ /^\s*(SAI_\w+)\s*=\s*(.*)$/)
{
my $init = $2;
if ($init =~ m!^(0x\w+|SAI_\w+|SAI_\w+ \+ \d+|SAI_\w+ \+ 0x[0-9a-f]{1,8}|SAI_\w+ \+ SAI_\w+|\d+|\(?\d+ << \d+\)?),?\s*(/\*\*.*\*/)?$!)
{
# supported initializers for enum:
# - 0x00000000 (hexadecimal number)
# - 0 (decimal number)
# - SAI_... (other SAI enum)
# - n << m (flags shifted)
# - SAI_.. + SAI_.. (sum of SAI enums)
# - SAI_.. + 0x00 (sum of SAI and hexadecimal number)
# - SAI_.. + 0 (sum of SAI and decimal number)
}
else
{
LogWarning "unsupported initializer on enum: $line";
}
}
if ($line =~ /^\s*SAI_\w+\s*=\s*+0x(\w+)(,|$)/ and length($1) != 8)
{
LogWarning "enum number '0x$1' should have 8 digits $header:$n:$line";
}
if ($line =~ /^\s*SAI_\w+(\s*)=(\s*)/ and ($1 eq "" or $2 eq ""))
{
LogWarning "space is required before and after '=' $header:$n:$line";
}
if ($line =~ /#define\s*(\w+)/ and $header ne "saitypes.h")
{
my $defname = $1;
if (not $defname =~ /^(SAI_|__SAI)/)
{
LogWarning "define should start with SAI_ or __SAI: $header:$n:$line";
}
}
if ($line =~ /\s+$/)
{
LogWarning "line ends in whitespace $header $n: $line";
}
if ($line =~ /[^\x20-\x7e]/)
{
LogWarning "line contains non ascii characters $header $n: $line";
}
if ($line =~ /typedef .+?\(\s*\*\s*(\w+)\s*\)/)
{
my $fname = $1;
if (not $fname =~ /^sai_\w+_fn$/)
{
LogWarning "all function declarations should be in format sai_\\w+_fn $header $n: $line";
}
}
my $prev = $lines[$n-2];
if ($line =~ /\*\s*\@\w+/ and $prev =~ /\@brief/)
{
LogWarning "missing empty line before $header $n: $line";
}
if ($line =~ /==|SAI_\w+/ and $prev =~ /\@(validonly|condition)/)
{
LogWarning "merge with previous line: $header $n: $line";
}
if ($line =~ /\@type/ and $prev =~ /^\s*\*./)
{
LogWarning "missing empty line before: $header $n: $line";
}
if ($line =~ /_(In|Out|Inout)_.+(\* | \* )/)
{
LogWarning "move * to the right of parameter: $header $n: $line";
}
if ($line =~ /\*.*SAI_.+(==|!=)/ and not $line =~ /\@(condition|validonly)/)
{
if (not $line =~ /(condition|validonly|valid when|only when)\s+SAI_/i)
{
LogWarning "condition should be preceded by 'valid when' or 'only when': $header $n: $line";
}
}
if ($line =~ /SAI_\w+ \s+=\s+(0x|S)/)
{
LogWarning "too many spaces before '=' $header:$n: $line"
}
if ($line =~ /__/ and not $line =~ /^#.+__SAI\w*_H_|VA_ARGS|BOOL_DEFINED/)
{
LogWarning "double underscore detected: $header $n: $line";
}
if ($line eq "" and $prev =~ /{/)
{
LogWarning "empty line after '$prev': $header:$n: $line";
}
my $pattern = join"|", @acronyms;
while ($line =~ /\b($pattern)\b/igp)
{
my $pre = $`;
my $post = $';
# force special word to be capital
my $word = $1;
next if $word =~ /^($pattern)$/;
next if $word =~ /^(an|An)$/; # exception, can be word and acronym
next if $line =~ /$word.h/;
next if not $line =~ /\*/; # must contain star, so will be comment
next if "$pre$word" =~ m!http://$word$!;
next if ($line =~ /\@param\[\w+\]\s+$word /); # skip word if word is param name
LogWarning "Word '$word' should use capital letters $header $n:$line";
}
# perform aspell checking (move to separate method)
if ($line =~ m!^\s*(\*|/\*\*)!)
{
while ($line =~ /\b([a-z0-9']+)\b/ig)
{
my $pre = $`;
my $post = $';
my $word = $1;
next if $word =~ /^($pattern)$/; # capital words
next if ($line =~ /\@validonly\s+\w+->\w+/); # skip valionly code
next if ($line =~ /\@passparam\s+\w+/); # skip passparam
next if ($line =~ /\@extraparam\s+\w+/); # skip extraparam
next if ($line =~ /\@param\[\w+\]\s+$word /); # skip word if word is param name
# look into good and bad words hash to speed things up
next if defined $exceptions{$word};
next if $word =~ /^sai\w+/i;
next if $word =~ /0x\S+/;
next if "$pre$word" =~ /802.\d+\w+/;
next if defined $wordsChecked{$word};
$wordsChecked{$word} = 1;
$wordsToCheck{$word} = "$header $n:$line";
}
}
if ($line =~ /#include\s*\"sai/ and not $header =~ /^sai(|extensions|metadatautils).h$/)
{
# TODO we should dedice later whether use <> or "" on all includes to make it consistent
LogWarning "include should use <> brackets on: $header:$n:$line";
}
if ($line =~ /typedef\s*(enum|struct|union).*{/)
{
LogWarning "move '{' to new line in typedef $header $n:$line";
}
if ($line =~ /^[^*]*union/ and not $line =~ /union _\w+\b/)
{
LogError "all unions must be named $header $n:$line";
}
CheckDoxygenStyle($header, $line, $n);
next if $line =~ /^ \*($|[ \/])/; # doxygen comment
next if $line =~ /^$/; # empty line
next if $line =~ /^typedef /; # type definition
next if $line =~ /^sai_status_t sai_\w+\(/; # return codes
next if $line =~ /^sai_object\w+_t sai_\w+\(/; # return codes
next if $line =~ /^int sai_\w+\($/; # methods returning int
next if $line =~ /^extern /; # extern in metadata
next if $line =~ /^[{}#\/]/; # start end of struct, define, start of comment
next if $line =~ /^ {8}(_In|_Out|\.\.\.)/; # function arguments
next if $line =~ /^ {4}(sai_)/i; # sai struct entry or SAI enum
next if $line =~ /^ {4}\/\*/; # doxygen start
next if $line =~ /^ {8}\/\*/; # doxygen start
next if $line =~ /^ {5}\*/; # doxygen comment continue
next if $line =~ /^ {8}sai_/; # union entry
next if $line =~ /^ {4}union/; # union
next if $line =~ /^ {4}[{}]/; # start or end of union
next if $line =~ /^ {4}(u?int)/; # union entries
next if $line =~ /^ {4}(char|bool)/; # union entries
next if $line =~ /^ {8}bool booldata/; # union bool
next if $line =~ /^ {4}(true|false)/; # bool definition
next if $line =~ /^ {4}(const|size_t|else)/; # const in meta headers
next if $line =~ /^(void|bool) /; # function return
next if $line =~ m![^\\]\\$!; # macro multiline
next if $line =~ /^ {4}(\w+);$/; # union entries
next if $line =~ /^union _sai_\w+ \{/; # union entries
LogWarning "C++ comment in ANSI C header: $header $n:$line" if $line =~ /\/\//;
LogWarning "Header doesn't meet style requirements (most likely ident is not 4 or 8 spaces) $header $n:$line";
}
if ($oncedefCount != 3)
{
LogWarning "$oncedef should be used 3 times in header, but used $oncedefCount";
}
}
GetWordsFromSources(\%wordsToCheck);
RunAspell(\%wordsToCheck) if not defined $main::optionDisableAspell;
}