kconfig_lint: Separate errors from warnings

- Create subroutines for printing warnings and errors
- Change all the existing warning and error routines to use subroutines
- Add new command line options to suppress errors and to print notes

Change-Id: I04893faffca21c5bb7b51be920cca4620dc283c3
Signed-off-by: Martin Roth <martinroth@google.com>
Reviewed-on: https://review.coreboot.org/12555
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
This commit is contained in:
Martin Roth 2015-11-26 19:12:44 -07:00
parent cacbcf4815
commit d808017760
1 changed files with 93 additions and 140 deletions

View File

@ -25,7 +25,9 @@ use File::Find;
use Getopt::Long; use Getopt::Long;
use Getopt::Std; use Getopt::Std;
my $suppress_error_output = 0; # flag to prevent warning and error text my $suppress_error_output = 0; # flag to prevent error text
my $suppress_warning_output = 0; # flag to prevent warning text
my $show_note_output = 0; # flag to show minor notes text
my $print_full_output = 0; # flag to print wholeconfig output my $print_full_output = 0; # flag to print wholeconfig output
my $output_file = "-"; # filename of output - set stdout by default my $output_file = "-"; # filename of output - set stdout by default
my $dont_use_git_grep = 0; my $dont_use_git_grep = 0;
@ -33,7 +35,8 @@ my $dont_use_git_grep = 0;
#globals #globals
my $top_dir = "."; # Directory where Kconfig is run my $top_dir = "."; # Directory where Kconfig is run
my $root_dir = "src"; # Directory of the top level Kconfig file my $root_dir = "src"; # Directory of the top level Kconfig file
my $errors_found = 0; # count of warnings and errors my $errors_found = 0; # count of errors
my $warnings_found = 0;
my $exclude_dirs = '--exclude-dir="build" --exclude-dir="coreboot-builds" --exclude-dir="payloads" --exclude-dir="configs" --exclude-dir="util"'; # directories to exclude when searching for used symbols - NOT USED FOR GIT GREP (TODO) my $exclude_dirs = '--exclude-dir="build" --exclude-dir="coreboot-builds" --exclude-dir="payloads" --exclude-dir="configs" --exclude-dir="util"'; # directories to exclude when searching for used symbols - NOT USED FOR GIT GREP (TODO)
my @exclude_files = ('\.txt$', '\.tex$', 'config', '\.tags'); #files to exclude when looking for symbols my @exclude_files = ('\.txt$', '\.tex$', 'config', '\.tags'); #files to exclude when looking for symbols
my $config_file = ""; # name of config file to load symbol values from. my $config_file = ""; # name of config file to load symbol values from.
@ -83,7 +86,37 @@ sub Main {
print_wholeconfig(); print_wholeconfig();
exit($errors_found); if ($errors_found) {
print "# $errors_found errors";
if ($warnings_found) {
print ", $warnings_found warnings";
}
print "\n";
}
exit($errors_found + $warnings_found);
}
#-------------------------------------------------------------------------------
# Print and count errors
#-------------------------------------------------------------------------------
sub show_error {
my ($error_msg) = @_;
unless ($suppress_error_output) {
print "#!!!!! Error: $error_msg\n";
$errors_found++;
}
}
#-------------------------------------------------------------------------------
# Print and count warnings
#-------------------------------------------------------------------------------
sub show_warning {
my ($warning_msg) = @_;
unless ($suppress_warning_output) {
print "#!!!!! Warning: $warning_msg\n";
$warnings_found++;
}
} }
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
@ -104,10 +137,7 @@ sub check_for_ifdef {
my $symbol = $3; my $symbol = $3;
if ((exists $symbols{$symbol}) && ($symbols{$symbol}{type} ne "string")) { if ((exists $symbols{$symbol}) && ($symbols{$symbol}{type} ne "string")) {
unless ($suppress_error_output) { show_error("#ifdef 'CONFIG_$symbol' used in $file at line $lineno. Symbols of type '$symbols{$symbol}{type}' are always defined.");
print "#!!!!! Warning: #ifdef 'CONFIG_$symbol' used in $file at line $lineno. Symbols of type '$symbols{$symbol}{type}' are always defined.\n";
}
$errors_found++;
} }
} }
} }
@ -124,10 +154,7 @@ sub check_for_ifdef {
next if ( $line =~ /^([^:]+):(\d+):.+defined\s*\(\s*CONFIG_$symbol.*(&&|\|\|)\s*!?\s*\(?\s*CONFIG_$symbol/ ); next if ( $line =~ /^([^:]+):(\d+):.+defined\s*\(\s*CONFIG_$symbol.*(&&|\|\|)\s*!?\s*\(?\s*CONFIG_$symbol/ );
if ((exists $symbols{$symbol}) && ($symbols{$symbol}{type} ne "string")) { if ((exists $symbols{$symbol}) && ($symbols{$symbol}{type} ne "string")) {
unless ($suppress_error_output) { show_error("defined 'CONFIG_$symbol' used in $file at line $lineno. Symbols of type '$symbols{$symbol}{type}' are always defined.");
print "#!!!!! Warning: defined 'CONFIG_$symbol' used in $file at line $lineno. Symbols of type '$symbols{$symbol}{type}' are always defined.\n";
}
$errors_found++;
} }
} }
} }
@ -150,15 +177,9 @@ sub check_for_def {
my $symbol = $3; my $symbol = $3;
if ((exists $symbols{$symbol})) { if ((exists $symbols{$symbol})) {
unless ($suppress_error_output) { show_warning("#define of symbol 'CONFIG_$symbol' used in $file at line $lineno.");
print "#!!!!! Warning: #define of symbol 'CONFIG_$symbol' used in $file at line $lineno.\n";
}
$errors_found++;
} else { } else {
unless ($suppress_error_output) { show_warning("#define 'CONFIG_$symbol' used in $file at line $lineno. Other #defines should not look like Kconfig symbols.");
print "#!!!!! Warning: #define 'CONFIG_$symbol' used in $file at line $lineno. Other #defines should not look like Kconfig symbols.\n";
}
$errors_found++;
} }
} }
} }
@ -182,16 +203,13 @@ sub check_is_enabled {
#make sure that #make sure that
if (exists $symbols{$symbol}) { if (exists $symbols{$symbol}) {
if ($symbols{$symbol}{type} ne "bool") { if ($symbols{$symbol}{type} ne "bool") {
unless ($suppress_error_output) { show_error("IS_ENABLED(CONFIG_$symbol) used in $file at line $lineno. IS_ENABLED is only valid for type 'bool', not '$symbols{$symbol}{type}'.");
print "#!!!!! Warning: IS_ENABLED(CONFIG_$symbol) used in $file at line $lineno. IS_ENABLED is only valid for type 'bool', not '$symbols{$symbol}{type}'.\n";
}
$errors_found++;
} }
} else { } else {
print "#!!!!! Warning: IS_ENABLED() used on unknown value CONFIG_$symbol in $file at line $lineno.\n"; show_error("IS_ENABLED() used on unknown value CONFIG_$symbol in $file at line $lineno.");
} }
} else { } else {
print "# uninterpreted IS_ENABLED line: $line\n"; show_error("# uninterpreted IS_ENABLED line: $line");
} }
} }
} }
@ -221,10 +239,7 @@ sub check_defaults {
if ($default_set) { if ($default_set) {
my $filename = $symbols{$sym}{$sym_num}{file}; my $filename = $symbols{$sym}{$sym_num}{file};
my $line_no = $symbols{$sym}{$sym_num}{default}{$def_num}{default_line_no}; my $line_no = $symbols{$sym}{$sym_num}{default}{$def_num}{default_line_no};
unless ($suppress_error_output) { show_warning("Default for '$sym' referenced in $filename at line $line_no will never be set - overridden by default set in $default_filename at line $default_line_no");
print "#!!!!! Error: Default for '$sym' referenced in $filename at line $line_no will never be set - overridden by default set in $default_filename at line $default_line_no \n";
}
$errors_found++;
} }
else { else {
#if no default is set, see if this is a default with no dependencies #if no default is set, see if this is a default with no dependencies
@ -257,10 +272,7 @@ sub check_referenced_symbols {
for ( my $i = 0 ; $i <= $referenced_symbols{$key}{count} ; $i++ ) { for ( my $i = 0 ; $i <= $referenced_symbols{$key}{count} ; $i++ ) {
my $filename = $referenced_symbols{$key}{$i}{filename}; my $filename = $referenced_symbols{$key}{$i}{filename};
my $line_no = $referenced_symbols{$key}{$i}{line_no}; my $line_no = $referenced_symbols{$key}{$i}{line_no};
unless ($suppress_error_output) { show_error("Undefined Symbol '$key' used in $filename at line $line_no.");
print "#!!!!! Error: Undefined Symbol '$key' used in $filename at line $line_no.\n";
}
$errors_found++;
} }
} }
} }
@ -322,10 +334,7 @@ sub check_used_symbols {
for ( my $i = 0 ; $i <= $symbols{$key}{count} ; $i++ ) { for ( my $i = 0 ; $i <= $symbols{$key}{count} ; $i++ ) {
my $filename = $symbols{$key}{$i}{file}; my $filename = $symbols{$key}{$i}{file};
my $line_no = $symbols{$key}{$i}{line_no}; my $line_no = $symbols{$key}{$i}{line_no};
unless ($suppress_error_output) { show_warning("Unused symbol '$key' referenced in $filename at line $line_no.");
print "#!!!!! Warning: Unused symbol '$key' referenced in $filename at line $line_no.\n";
}
$errors_found++;
} }
} }
} }
@ -439,18 +448,12 @@ sub build_and_parse_kconfig_tree {
elsif ( $line =~ /^\s*endchoice/ ) { elsif ( $line =~ /^\s*endchoice/ ) {
$inside_config = ""; $inside_config = "";
if ( !$inside_choice ) { if ( !$inside_choice ) {
unless ($suppress_error_output) { show_error("'endchoice' keyword not within a choice block in $filename at line $line_no.");
print "#!!!!! Warning: 'endchoice' keyword not within a choice block in $filename at line $line_no.\n";
}
$errors_found++;
} }
$inside_choice = ""; $inside_choice = "";
if ( $configs_inside_choice == 0 ) { if ( $configs_inside_choice == 0 ) {
unless ($suppress_error_output) { show_error("choice block has no symbols in $filename at line $line_no.");
print "#!!!!! Warning: choice block has no symbols in $filename at line $line_no.\n";
}
$errors_found++;
} }
$configs_inside_choice = 0; $configs_inside_choice = 0;
} }
@ -458,16 +461,10 @@ sub build_and_parse_kconfig_tree {
# [optional] # [optional]
elsif ( $line =~ /^\s*optional/ ) { elsif ( $line =~ /^\s*optional/ ) {
if ($inside_config) { if ($inside_config) {
unless ($suppress_error_output) { show_error("Keyword 'optional' appears inside config for '$inside_config' in $filename at line $line_no. This is not valid.");
print "#!!!!! Error: Keyword 'optional' appears inside config for '$inside_config' in $filename at line $line_no. This is not valid.\n";
}
$errors_found++;
} }
if ( !$inside_choice ) { if ( !$inside_choice ) {
unless ($suppress_error_output) { show_error("Keyword 'optional' appears outside of a choice block in $filename at line $line_no. This is not valid.");
print "#!!!!! Error: Keyword 'optional' appears outside of a choice block in $filename at line $line_no. This is not valid.\n";
}
$errors_found++;
} }
} }
@ -521,10 +518,7 @@ sub build_and_parse_kconfig_tree {
# select <symbol> [if <expr>] # select <symbol> [if <expr>]
elsif ( $line =~ /^\s*select/ ) { elsif ( $line =~ /^\s*select/ ) {
unless ($inside_config) { unless ($inside_config) {
unless ($suppress_error_output) { show_error("Keyword 'select' appears outside of config in $filename at line $line_no. This is not valid.");
print "#!!!!! Error: Keyword 'select' appears outside of config in $filename at line $line_no. This is not valid.\n";
}
$errors_found++;
} }
if ( $line =~ /^\s*select\s+(.*)$/ ) { if ( $line =~ /^\s*select\s+(.*)$/ ) {
@ -551,10 +545,7 @@ sub build_and_parse_kconfig_tree {
# do nothing # do nothing
} }
else { else {
unless ($suppress_error_output) { show_error("$line ($filename line $line_no unrecognized)");
print "### $line ($filename line $line_no unrecognized)\n";
}
$errors_found++;
} }
push @wholeconfig, @parseline; push @wholeconfig, @parseline;
@ -630,17 +621,13 @@ sub handle_range {
my $checkrange2 = $1; my $checkrange2 = $1;
if ( $checkrange1 && $checkrange2 && ( hex($checkrange1) > hex($checkrange2) ) ) { if ( $checkrange1 && $checkrange2 && ( hex($checkrange1) > hex($checkrange2) ) ) {
unless ($suppress_error_output) { show_error("Range entry in $filename line $line_no value 1 ($range1) is greater than value 2 ($range2).");
print "#!!!!! Error: Range entry in $filename line $line_no value 1 ($range1) is greater than value 2 ($range2).\n";
}
$errors_found++;
} }
if ($inside_config) { if ($inside_config) {
if ( exists( $symbols{$inside_config}{range1} ) ) { if ( exists( $symbols{$inside_config}{range1} ) ) {
if ( ( $symbols{$inside_config}{range1} != $range1 ) || ( $symbols{$inside_config}{range2} != $range2 ) ) { if ( ( $symbols{$inside_config}{range1} != $range1 ) || ( $symbols{$inside_config}{range2} != $range2 ) ) {
unless ($suppress_error_output) { if ($show_note_output) {
print "#!!!!! Note: Config '$inside_config' range entry $range1 $range2 at $filename line $line_no does"; print "#!!!!! Note: Config '$inside_config' range entry $range1 $range2 at $filename line $line_no does";
print " not match the previously defined range $symbols{$inside_config}{range1} $symbols{$inside_config}{range2}"; print " not match the previously defined range $symbols{$inside_config}{range1} $symbols{$inside_config}{range2}";
print " defined in $symbols{$inside_config}{range_file} on line"; print " defined in $symbols{$inside_config}{range_file} on line";
@ -656,10 +643,7 @@ sub handle_range {
} }
} }
else { else {
unless ($suppress_error_output) { show_error("Range entry in $filename line $line_no is not inside a config block.");
print "#!!!!! Error: Range entry in $filename line $line_no is not inside a config block.\n";
}
$errors_found++;
} }
} }
@ -689,10 +673,7 @@ sub handle_default {
handle_expressions( $default, $inside_config, $filename, $line_no ); handle_expressions( $default, $inside_config, $filename, $line_no );
} }
else { else {
unless ($suppress_error_output) { show_error("$name entry in $filename line $line_no is not inside a config or choice block.");
print "#!!!!! Error: $name entry in $filename line $line_no is not inside a config or choice block.\n";
}
$errors_found++;
} }
} }
@ -783,10 +764,7 @@ sub handle_expressions {
return; return;
} }
else { else {
unless ($suppress_error_output) { show_error("Unrecognized expression '$exprline' in $filename line $line_no.");
print "#### Unrecognized expression '$exprline' in $filename line $line_no.\n";
}
$errors_found++;
} }
return; return;
@ -826,11 +804,8 @@ sub add_referenced_symbol {
$line =~ /^(\s+)/; #find the indentation level. $line =~ /^(\s+)/; #find the indentation level.
$help_whitespace = $1; $help_whitespace = $1;
if ( !$help_whitespace ) { if ( !$help_whitespace ) {
unless ($suppress_error_output) { show_warning("$filename line $line_no help text starts with no whitespace.");
print "# Warning: $filename line $line_no help text starts with no whitespace.\n";
}
return $inside_help; return $inside_help;
$errors_found++;
} }
} }
@ -850,10 +825,9 @@ sub add_referenced_symbol {
elsif ( ( $line =~ /^(\s*)help/ ) || ( $line =~ /^(\s*)---help---/ ) ) { elsif ( ( $line =~ /^(\s*)help/ ) || ( $line =~ /^(\s*)---help---/ ) ) {
$inside_help = $line_no; $inside_help = $line_no;
if ( ( !$inside_config ) && ( !$inside_choice ) ) { if ( ( !$inside_config ) && ( !$inside_choice ) ) {
unless ($suppress_error_output) { if ($show_note_output) {
print "# Note: $filename line $line_no help is not inside a config or choice block.\n"; print "# Note: $filename line $line_no help is not inside a config or choice block.\n";
} }
$errors_found++;
} }
elsif ($inside_config) { elsif ($inside_config) {
$help_whitespace = ""; $help_whitespace = "";
@ -878,11 +852,7 @@ sub handle_type {
if ($inside_config) { if ($inside_config) {
if ( exists( $symbols{$inside_config}{type} ) ) { if ( exists( $symbols{$inside_config}{type} ) ) {
if ( $symbols{$inside_config}{type} !~ /$type/ ) { if ( $symbols{$inside_config}{type} !~ /$type/ ) {
print "#!!!!! Error: Config '$inside_config' type entry $type at $filename line $line_no does not match"; show_error("Config '$inside_config' type entry $type at $filename line $line_no does not match $symbols{$inside_config}{type} defined in $symbols{$inside_config}{type_file} on line $symbols{$inside_config}{type_line_no}.");
print " the previously defined type $symbols{$inside_config}{type}";
print " defined in $symbols{$inside_config}{type_file} on line";
print " $symbols{$inside_config}{type_line_no}.\n";
$errors_found++;
} }
} }
else { else {
@ -892,10 +862,7 @@ sub handle_type {
} }
} }
else { else {
unless ($suppress_error_output) { show_error("Type entry in $filename line $line_no is not inside a config block.");
print "#!!!!! Error: Type entry in $filename line $line_no is not inside a config block.\n";
}
$errors_found++;
} }
} }
@ -915,10 +882,7 @@ sub handle_prompt {
} }
if ( !defined @$menu_array_ref[0] ) { if ( !defined @$menu_array_ref[0] ) {
unless ($suppress_error_output) { show_error("Symbol '$inside_config' with prompt '$prompt' appears outside of a menu in $filename at line $line_no.");
print "#!!!!! Warning: Symbol '$inside_config' with prompt '$prompt' appears outside of a menu in $filename at line $line_no. This is discouraged.\n";
}
$errors_found++;
} }
my $sym_num = $symbols{$inside_config}{count}; my $sym_num = $symbols{$inside_config}{count};
@ -938,10 +902,7 @@ sub handle_prompt {
#do nothing #do nothing
} }
else { else {
unless ($suppress_error_output) { show_error("$name entry in $filename line $line_no is not inside a config or choice block.");
print "#!!!!! Error: $name entry in $filename line $line_no is not inside a config or choice block.\n";
}
$errors_found++;
} }
} }
@ -954,19 +915,12 @@ sub simple_line_checks {
#check for spaces instead of tabs #check for spaces instead of tabs
if ( $line =~ /^ +/ ) { if ( $line =~ /^ +/ ) {
unless ($suppress_error_output) { show_error("$filename line $line_no starts with a space.");
print "# Note: $filename line $line_no starts with a space.\n";
}
$errors_found++;
} }
#verify a linefeed at the end of the line #verify a linefeed at the end of the line
if ( $line !~ /.*\n/ ) { if ( $line !~ /.*\n/ ) {
unless ($suppress_error_output) { show_error("$filename line $line_no does not end with linefeed. This can cause the line to not be recognized by the Kconfig parser.\n#($line)");
print "#!!!!! Warning: $filename line $line_no does not end with linefeed. This can cause the line to not be recognized by the Kconfig parser.\n";
print "#($line)\n";
}
$errors_found++;
$line =~ s/\s*$//; $line =~ s/\s*$//;
} }
else { else {
@ -1005,10 +959,7 @@ sub load_kconfig_file {
#the directory should exist when using a glob #the directory should exist when using a glob
else { else {
unless ($suppress_error_output) { show_warning("Could not find dir '$dir_prefix'");
print "#!!!!! Warning: Could not find dir '$dir_prefix'\n";
}
$errors_found++;
} }
} }
@ -1017,10 +968,7 @@ sub load_kconfig_file {
#throw a warning if the file has already been loaded. #throw a warning if the file has already been loaded.
if ( exists $loaded_files{$input_file} ) { if ( exists $loaded_files{$input_file} ) {
unless ($suppress_error_output) { show_warning("'$input_file' sourced in '$loadfile' at line $loadline was already loaded by $loaded_files{$input_file}");
print "#!!!!! Warning: '$input_file' sourced in '$loadfile' at line $loadline was already loaded by $loaded_files{$input_file}\n";
}
$errors_found++;
} }
#load the file's contents and mark the file as loaded for checking later #load the file's contents and mark the file as loaded for checking later
@ -1032,10 +980,7 @@ sub load_kconfig_file {
# if the file isn't being loaded from a glob, it should exist. # if the file isn't being loaded from a glob, it should exist.
elsif ( $expanded == 0 ) { elsif ( $expanded == 0 ) {
unless ($suppress_error_output) { show_warning("Could not find file '$input_file' sourced in $loadfile at line $loadline");
print "#!!!!! Warning: Could not find file '$input_file' sourced in $loadfile at line $loadline\n";
}
$errors_found++;
} }
my $line_in_file = 0; my $line_in_file = 0;
@ -1109,10 +1054,7 @@ sub print_wholeconfig {
sub check_if_file_referenced { sub check_if_file_referenced {
my $filename = $File::Find::name; my $filename = $File::Find::name;
if ( ( $filename =~ /Kconfig/ ) && ( !exists $loaded_files{$filename} ) ) { if ( ( $filename =~ /Kconfig/ ) && ( !exists $loaded_files{$filename} ) ) {
unless ($suppress_error_output) { show_warning("'$filename' is never referenced");
print "#!!!!! Warning: '$filename' is never referenced\n";
}
$errors_found++;
} }
} }
@ -1123,13 +1065,22 @@ sub check_arguments {
my $show_usage = 0; my $show_usage = 0;
GetOptions( GetOptions(
'help|?' => sub { usage() }, 'help|?' => sub { usage() },
'e|errors_off' => \$suppress_error_output,
'n|notes' => \$show_note_output,
'o|output=s' => \$output_file, 'o|output=s' => \$output_file,
'p|print' => \$print_full_output, 'p|print' => \$print_full_output,
'w|warnings_off' => \$suppress_error_output, 'w|warnings_off' => \$suppress_warning_output,
'path=s' => \$top_dir, 'path=s' => \$top_dir,
'c|config=s' => \$config_file, 'c|config=s' => \$config_file,
'G|no_git_grep' => \$dont_use_git_grep, 'G|no_git_grep' => \$dont_use_git_grep,
); );
if ($suppress_error_output) {
$suppress_warning_output = 1;
}
if ($suppress_warning_output) {
$show_note_output=0;
}
} }
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
@ -1139,7 +1090,9 @@ sub usage {
print "kconfig_lint <options>\n"; print "kconfig_lint <options>\n";
print " -o|--output=file Set output filename\n"; print " -o|--output=file Set output filename\n";
print " -p|--print Print full output\n"; print " -p|--print Print full output\n";
print " -e|--errors_off Don't print warnings or errors\n";
print " -w|--warnings_off Don't print warnings\n"; print " -w|--warnings_off Don't print warnings\n";
print " -n|--notes Show minor notes\n";
print " --path=dir Path to top level kconfig\n"; print " --path=dir Path to top level kconfig\n";
print " -c|--config=file Filename of config file to load\n"; print " -c|--config=file Filename of config file to load\n";
print " -G|--no_git_grep Use standard grep tools instead of git grep\n"; print " -G|--no_git_grep Use standard grep tools instead of git grep\n";