sub CheckHeadersStyle()

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;
}