Skip to content
Snippets Groups Projects
checkpatch.pl 191 KiB
Newer Older
  • Learn to ignore specific revisions
  • 				"extern prototypes should be avoided in .h files\n" . $herecurr) &&
    			    $fix) {
    
    				$fixed[$fixlinenr] =~ s/(.*)\bextern\b\s*(.*)/$1$2/;
    
    # check for new externs in .c files.
    		if ($realfile =~ /\.c$/ && defined $stat &&
    		    $stat =~ /^.\s*(?:extern\s+)?$Type\s+($Ident)(\s*)\(/s)
    		{
    			my $function_name = $1;
    			my $paren_space = $2;
    
    			my $s = $stat;
    			if (defined $cond) {
    				substr($s, 0, length($cond), '');
    			}
    			if ($s =~ /^\s*;/ &&
    			    $function_name ne 'uninitialized_var')
    			{
    				WARN("AVOID_EXTERNS",
    				     "externs should be avoided in .c files\n" .  $herecurr);
    			}
    
    			if ($paren_space =~ /\n/) {
    				WARN("FUNCTION_ARGUMENTS",
    				     "arguments for function declarations should follow identifier\n" . $herecurr);
    			}
    
    		} elsif ($realfile =~ /\.c$/ && defined $stat &&
    		    $stat =~ /^.\s*extern\s+/)
    		{
    			WARN("AVOID_EXTERNS",
    			     "externs should be avoided in .c files\n" .  $herecurr);
    		}
    
    
    # check for function declarations that have arguments without identifier names
    		if (defined $stat &&
    
    		    $stat =~ /^.\s*(?:extern\s+)?$Type\s*(?:$Ident|\(\s*\*\s*$Ident\s*\))\s*\(\s*([^{]+)\s*\)\s*;/s &&
    
    		    $1 ne "void") {
    			my $args = trim($1);
    			while ($args =~ m/\s*($Type\s*(?:$Ident|\(\s*\*\s*$Ident?\s*\)\s*$balanced_parens)?)/g) {
    				my $arg = trim($1);
    				if ($arg =~ /^$Type$/ && $arg !~ /enum\s+$Ident$/) {
    					WARN("FUNCTION_ARGUMENTS",
    					     "function definition argument '$arg' should also have an identifier name\n" . $herecurr);
    				}
    			}
    		}
    
    # check for function definitions
    		if ($^V && $^V ge 5.10.0 &&
    		    defined $stat &&
    		    $stat =~ /^.\s*(?:$Storage\s+)?$Type\s*($Ident)\s*$balanced_parens\s*{/s) {
    			$context_function = $1;
    
    # check for multiline function definition with misplaced open brace
    			my $ok = 0;
    			my $cnt = statement_rawlines($stat);
    			my $herectx = $here . "\n";
    			for (my $n = 0; $n < $cnt; $n++) {
    				my $rl = raw_line($linenr, $n);
    				$herectx .=  $rl . "\n";
    				$ok = 1 if ($rl =~ /^[ \+]\{/);
    				$ok = 1 if ($rl =~ /\{/ && $n == 0);
    				last if $rl =~ /^[ \+].*\{/;
    			}
    			if (!$ok) {
    				ERROR("OPEN_BRACE",
    				      "open brace '{' following function definitions go on the next line\n" . $herectx);
    			}
    		}
    
    
    # checks for new __setup's
    		if ($rawline =~ /\b__setup\("([^"]*)"/) {
    			my $name = $1;
    
    			if (!grep(/$name/, @setup_docs)) {
    				CHK("UNDOCUMENTED_SETUP",
    
    				    "__setup appears un-documented -- check Documentation/admin-guide/kernel-parameters.rst\n" . $herecurr);
    
    			}
    		}
    
    # check for pointless casting of kmalloc return
    		if ($line =~ /\*\s*\)\s*[kv][czm]alloc(_node){0,1}\b/) {
    			WARN("UNNECESSARY_CASTS",
    			     "unnecessary cast may hide bugs, see http://c-faq.com/malloc/mallocnocast.html\n" . $herecurr);
    		}
    
    
    # alloc style
    # p = alloc(sizeof(struct foo), ...) should be p = alloc(sizeof(*p), ...)
    		if ($^V && $^V ge 5.10.0 &&
    		    $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*([kv][mz]alloc(?:_node)?)\s*\(\s*(sizeof\s*\(\s*struct\s+$Lval\s*\))/) {
    			CHK("ALLOC_SIZEOF_STRUCT",
    			    "Prefer $3(sizeof(*$1)...) over $3($4...)\n" . $herecurr);
    		}
    
    
    # check for k[mz]alloc with multiplies that could be kmalloc_array/kcalloc
    		if ($^V && $^V ge 5.10.0 &&
    		    defined $stat &&
    		    $stat =~ /^\+\s*($Lval)\s*\=\s*(?:$balanced_parens)?\s*(k[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)\s*,/) {
    			my $oldfunc = $3;
    			my $a1 = $4;
    			my $a2 = $10;
    			my $newfunc = "kmalloc_array";
    			$newfunc = "kcalloc" if ($oldfunc eq "kzalloc");
    			my $r1 = $a1;
    			my $r2 = $a2;
    			if ($a1 =~ /^sizeof\s*\S/) {
    				$r1 = $a2;
    				$r2 = $a1;
    			}
    			if ($r1 !~ /^sizeof\b/ && $r2 =~ /^sizeof\s*\S/ &&
    			    !($r1 =~ /^$Constant$/ || $r1 =~ /^[A-Z_][A-Z0-9_]*$/)) {
    				my $cnt = statement_rawlines($stat);
    
    				my $herectx = get_stat_here($linenr, $cnt, $here);
    
    
    				if (WARN("ALLOC_WITH_MULTIPLY",
    					 "Prefer $newfunc over $oldfunc with multiply\n" . $herectx) &&
    				    $cnt == 1 &&
    				    $fix) {
    					$fixed[$fixlinenr] =~ s/\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*(k[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)/$1 . ' = ' . "$newfunc(" . trim($r1) . ', ' . trim($r2)/e;
    				}
    			}
    		}
    
    
    # check for krealloc arg reuse
    		if ($^V && $^V ge 5.10.0 &&
    		    $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*krealloc\s*\(\s*\1\s*,/) {
    			WARN("KREALLOC_ARG_REUSE",
    			     "Reusing the krealloc arg is almost always a bug\n" . $herecurr);
    		}
    
    
    # check for alloc argument mismatch
    		if ($line =~ /\b(kcalloc|kmalloc_array)\s*\(\s*sizeof\b/) {
    			WARN("ALLOC_ARRAY_ARGS",
    			     "$1 uses number as first arg, sizeof is generally wrong\n" . $herecurr);
    		}
    
    
    # check for multiple semicolons
    		if ($line =~ /;\s*;\s*$/) {
    
    			if (WARN("ONE_SEMICOLON",
    				 "Statements terminations use 1 semicolon\n" . $herecurr) &&
    			    $fix) {
    
    				$fixed[$fixlinenr] =~ s/(\s*;\s*){2,}$/;/g;
    			}
    		}
    
    # check for #defines like: 1 << <digit> that could be BIT(digit), it is not exported to uapi
    		if ($realfile !~ m@^include/uapi/@ &&
    		    $line =~ /#\s*define\s+\w+\s+\(?\s*1\s*([ulUL]*)\s*\<\<\s*(?:\d+|$Ident)\s*\)?/) {
    			my $ull = "";
    			$ull = "_ULL" if (defined($1) && $1 =~ /ll/i);
    			if (CHK("BIT_MACRO",
    				"Prefer using the BIT$ull macro\n" . $herecurr) &&
    			    $fix) {
    				$fixed[$fixlinenr] =~ s/\(?\s*1\s*[ulUL]*\s*<<\s*(\d+|$Ident)\s*\)?/BIT${ull}($1)/;
    			}
    		}
    
    # check for #if defined CONFIG_<FOO> || defined CONFIG_<FOO>_MODULE
    		if ($line =~ /^\+\s*#\s*if\s+defined(?:\s*\(?\s*|\s+)(CONFIG_[A-Z_]+)\s*\)?\s*\|\|\s*defined(?:\s*\(?\s*|\s+)\1_MODULE\s*\)?\s*$/) {
    			my $config = $1;
    			if (WARN("PREFER_IS_ENABLED",
    				 "Prefer IS_ENABLED(<FOO>) to CONFIG_<FOO> || CONFIG_<FOO>_MODULE\n" . $herecurr) &&
    			    $fix) {
    				$fixed[$fixlinenr] = "\+#if IS_ENABLED($config)";
    
    # check for case / default statements not preceded by break/fallthrough/switch
    
    		if ($line =~ /^.\s*(?:case\s+(?:$Ident|$Constant)\s*|default):/) {
    			my $has_break = 0;
    			my $has_statement = 0;
    			my $count = 0;
    			my $prevline = $linenr;
    
    			while ($prevline > 1 && ($file || $count < 3) && !$has_break) {
    
    				$prevline--;
    				my $rline = $rawlines[$prevline - 1];
    				my $fline = $lines[$prevline - 1];
    				last if ($fline =~ /^\@\@/);
    				next if ($fline =~ /^\-/);
    				next if ($fline =~ /^.(?:\s*(?:case\s+(?:$Ident|$Constant)[\s$;]*|default):[\s$;]*)*$/);
    				$has_break = 1 if ($rline =~ /fall[\s_-]*(through|thru)/i);
    				next if ($fline =~ /^.[\s$;]*$/);
    				$has_statement = 1;
    				$count++;
    
    				$has_break = 1 if ($fline =~ /\bswitch\b|\b(?:break\s*;[\s$;]*$|exit\s*\(\b|return\b|goto\b|continue\b)/);
    
    			}
    			if (!$has_break && $has_statement) {
    				WARN("MISSING_BREAK",
    
    				     "Possible switch case/default not preceded by break or fallthrough comment\n" . $herecurr);
    
    # check for switch/default statements without a break;
    		if ($^V && $^V ge 5.10.0 &&
    		    defined $stat &&
    		    $stat =~ /^\+[$;\s]*(?:case[$;\s]+\w+[$;\s]*:[$;\s]*|)*[$;\s]*\bdefault[$;\s]*:[$;\s]*;/g) {
    			my $cnt = statement_rawlines($stat);
    
    			my $herectx = get_stat_here($linenr, $cnt, $here);
    
    
    			WARN("DEFAULT_NO_BREAK",
    			     "switch default: should use break\n" . $herectx);
    
    # check for gcc specific __FUNCTION__
    
    		if ($line =~ /\b__FUNCTION__\b/) {
    			if (WARN("USE_FUNC",
    				 "__func__ should be used instead of gcc specific __FUNCTION__\n"  . $herecurr) &&
    			    $fix) {
    
    				$fixed[$fixlinenr] =~ s/\b__FUNCTION__\b/__func__/g;
    
    # check for uses of __DATE__, __TIME__, __TIMESTAMP__
    		while ($line =~ /\b(__(?:DATE|TIME|TIMESTAMP)__)\b/g) {
    			ERROR("DATE_TIME",
    			      "Use of the '$1' macro makes the build non-deterministic\n" . $herecurr);
    		}
    
    
    # check for use of yield()
    		if ($line =~ /\byield\s*\(\s*\)/) {
    			WARN("YIELD",
    			     "Using yield() is generally wrong. See yield() kernel-doc (sched/core.c)\n"  . $herecurr);
    		}
    
    
    # check for comparisons against true and false
    		if ($line =~ /\+\s*(.*?)\b(true|false|$Lval)\s*(==|\!=)\s*(true|false|$Lval)\b(.*)$/i) {
    			my $lead = $1;
    			my $arg = $2;
    			my $test = $3;
    			my $otype = $4;
    			my $trail = $5;
    			my $op = "!";
    
    			($arg, $otype) = ($otype, $arg) if ($arg =~ /^(?:true|false)$/i);
    
    			my $type = lc($otype);
    			if ($type =~ /^(?:true|false)$/) {
    				if (("$test" eq "==" && "$type" eq "true") ||
    				    ("$test" eq "!=" && "$type" eq "false")) {
    					$op = "";
    				}
    
    				CHK("BOOL_COMPARISON",
    				    "Using comparison to $otype is error prone\n" . $herecurr);
    
    ## maybe suggesting a correct construct would better
    ##				    "Using comparison to $otype is error prone.  Perhaps use '${lead}${op}${arg}${trail}'\n" . $herecurr);
    
    			}
    		}
    
    
    # check for semaphores initialized locked
    		if ($line =~ /^.\s*sema_init.+,\W?0\W?\)/) {
    			WARN("CONSIDER_COMPLETION",
    			     "consider using a completion\n" . $herecurr);
    		}
    
    
    # recommend kstrto* over simple_strto* and strict_strto*
    		if ($line =~ /\b((simple|strict)_(strto(l|ll|ul|ull)))\s*\(/) {
    
    			WARN("CONSIDER_KSTRTO",
    
    			     "$1 is obsolete, use k$3 instead\n" . $herecurr);
    
    # check for __initcall(), use device_initcall() explicitly or more appropriate function please
    
    		if ($line =~ /^.\s*__initcall\s*\(/) {
    			WARN("USE_DEVICE_INITCALL",
    
    			     "please use device_initcall() or more appropriate function instead of __initcall() (see include/linux/init.h)\n" . $herecurr);
    		}
    
    # check for various structs that are normally const (ops, kgdb, device_tree)
    # and avoid what seem like struct definitions 'struct foo {'
    
    		if ($line !~ /\bconst\b/ &&
    
    		    $line =~ /\bstruct\s+($const_structs)\b(?!\s*\{)/) {
    
    			WARN("CONST_STRUCT",
    
    			     "struct $1 should normally be const\n" . $herecurr);
    
    		}
    
    # use of NR_CPUS is usually wrong
    # ignore definitions of NR_CPUS and usage to define arrays as likely right
    		if ($line =~ /\bNR_CPUS\b/ &&
    		    $line !~ /^.\s*\s*#\s*if\b.*\bNR_CPUS\b/ &&
    		    $line !~ /^.\s*\s*#\s*define\b.*\bNR_CPUS\b/ &&
    		    $line !~ /^.\s*$Declare\s.*\[[^\]]*NR_CPUS[^\]]*\]/ &&
    		    $line !~ /\[[^\]]*\.\.\.[^\]]*NR_CPUS[^\]]*\]/ &&
    		    $line !~ /\[[^\]]*NR_CPUS[^\]]*\.\.\.[^\]]*\]/)
    		{
    			WARN("NR_CPUS",
    			     "usage of NR_CPUS is often wrong - consider using cpu_possible(), num_possible_cpus(), for_each_possible_cpu(), etc\n" . $herecurr);
    		}
    
    
    # Use of __ARCH_HAS_<FOO> or ARCH_HAVE_<BAR> is wrong.
    		if ($line =~ /\+\s*#\s*define\s+((?:__)?ARCH_(?:HAS|HAVE)\w*)\b/) {
    			ERROR("DEFINE_ARCH_HAS",
    			      "#define of '$1' is wrong - use Kconfig variables or standard guards instead\n" . $herecurr);
    		}
    
    
    # likely/unlikely comparisons similar to "(likely(foo) > 0)"
    		if ($^V && $^V ge 5.10.0 &&
    		    $line =~ /\b((?:un)?likely)\s*\(\s*$FuncArg\s*\)\s*$Compare/) {
    			WARN("LIKELY_MISUSE",
    			     "Using $1 should generally have parentheses around the comparison\n" . $herecurr);
    
    		}
    
    # whine mightly about in_atomic
    		if ($line =~ /\bin_atomic\s*\(/) {
    			if ($realfile =~ m@^drivers/@) {
    				ERROR("IN_ATOMIC",
    				      "do not use in_atomic in drivers\n" . $herecurr);
    			} elsif ($realfile !~ m@^kernel/@) {
    				WARN("IN_ATOMIC",
    				     "use of in_atomic() is incorrect outside core kernel code\n" . $herecurr);
    			}
    		}
    
    
    # check for mutex_trylock_recursive usage
    		if ($line =~ /mutex_trylock_recursive/) {
    			ERROR("LOCKING",
    			      "recursive locking is bad, do not use this ever.\n" . $herecurr);
    		}
    
    
    # check for lockdep_set_novalidate_class
    		if ($line =~ /^.\s*lockdep_set_novalidate_class\s*\(/ ||
    		    $line =~ /__lockdep_no_validate__\s*\)/ ) {
    			if ($realfile !~ m@^kernel/lockdep@ &&
    			    $realfile !~ m@^include/linux/lockdep@ &&
    			    $realfile !~ m@^drivers/base/core@) {
    				ERROR("LOCKDEP",
    				      "lockdep_no_validate class is reserved for device->mutex.\n" . $herecurr);
    			}
    		}
    
    
    		if ($line =~ /debugfs_create_\w+.*\b$mode_perms_world_writable\b/ ||
    		    $line =~ /DEVICE_ATTR.*\b$mode_perms_world_writable\b/) {
    
    			WARN("EXPORTED_WORLD_WRITABLE",
    			     "Exporting world writable files is usually an error. Consider more restrictive permissions.\n" . $herecurr);
    		}
    
    # check for DEVICE_ATTR uses that could be DEVICE_ATTR_<FOO>
    # and whether or not function naming is typical and if
    # DEVICE_ATTR permissions uses are unusual too
    		if ($^V && $^V ge 5.10.0 &&
    		    defined $stat &&
    		    $stat =~ /\bDEVICE_ATTR\s*\(\s*(\w+)\s*,\s*\(?\s*(\s*(?:${multi_mode_perms_string_search}|0[0-7]{3,3})\s*)\s*\)?\s*,\s*(\w+)\s*,\s*(\w+)\s*\)/) {
    			my $var = $1;
    			my $perms = $2;
    			my $show = $3;
    			my $store = $4;
    			my $octal_perms = perms_to_octal($perms);
    			if ($show =~ /^${var}_show$/ &&
    			    $store =~ /^${var}_store$/ &&
    			    $octal_perms eq "0644") {
    				if (WARN("DEVICE_ATTR_RW",
    					 "Use DEVICE_ATTR_RW\n" . $herecurr) &&
    				    $fix) {
    					$fixed[$fixlinenr] =~ s/\bDEVICE_ATTR\s*\(\s*$var\s*,\s*\Q$perms\E\s*,\s*$show\s*,\s*$store\s*\)/DEVICE_ATTR_RW(${var})/;
    				}
    			} elsif ($show =~ /^${var}_show$/ &&
    				 $store =~ /^NULL$/ &&
    				 $octal_perms eq "0444") {
    				if (WARN("DEVICE_ATTR_RO",
    					 "Use DEVICE_ATTR_RO\n" . $herecurr) &&
    				    $fix) {
    					$fixed[$fixlinenr] =~ s/\bDEVICE_ATTR\s*\(\s*$var\s*,\s*\Q$perms\E\s*,\s*$show\s*,\s*NULL\s*\)/DEVICE_ATTR_RO(${var})/;
    				}
    			} elsif ($show =~ /^NULL$/ &&
    				 $store =~ /^${var}_store$/ &&
    				 $octal_perms eq "0200") {
    				if (WARN("DEVICE_ATTR_WO",
    					 "Use DEVICE_ATTR_WO\n" . $herecurr) &&
    				    $fix) {
    					$fixed[$fixlinenr] =~ s/\bDEVICE_ATTR\s*\(\s*$var\s*,\s*\Q$perms\E\s*,\s*NULL\s*,\s*$store\s*\)/DEVICE_ATTR_WO(${var})/;
    				}
    			} elsif ($octal_perms eq "0644" ||
    				 $octal_perms eq "0444" ||
    				 $octal_perms eq "0200") {
    				my $newshow = "$show";
    				$newshow = "${var}_show" if ($show ne "NULL" && $show ne "${var}_show");
    				my $newstore = $store;
    				$newstore = "${var}_store" if ($store ne "NULL" && $store ne "${var}_store");
    				my $rename = "";
    				if ($show ne $newshow) {
    					$rename .= " '$show' to '$newshow'";
    				}
    				if ($store ne $newstore) {
    					$rename .= " '$store' to '$newstore'";
    				}
    				WARN("DEVICE_ATTR_FUNCTIONS",
    				     "Consider renaming function(s)$rename\n" . $herecurr);
    			} else {
    				WARN("DEVICE_ATTR_PERMS",
    				     "DEVICE_ATTR unusual permissions '$perms' used\n" . $herecurr);
    			}
    		}
    
    
    # Mode permission misuses where it seems decimal should be octal
    # This uses a shortcut match to avoid unnecessary uses of a slow foreach loop
    
    # o Ignore module_param*(...) uses with a decimal 0 permission as that has a
    #   specific definition of not visible in sysfs.
    # o Ignore proc_create*(...) uses with a decimal 0 permission as that means
    #   use the default permissions
    
    		if ($^V && $^V ge 5.10.0 &&
    		    defined $stat &&
    		    $line =~ /$mode_perms_search/) {
    			foreach my $entry (@mode_permission_funcs) {
    				my $func = $entry->[0];
    				my $arg_pos = $entry->[1];
    
    				my $lc = $stat =~ tr@\n@@;
    				$lc = $lc + $linenr;
    
    				my $stat_real = get_stat_real($linenr, $lc);
    
    
    				my $skip_args = "";
    				if ($arg_pos > 1) {
    					$arg_pos--;
    					$skip_args = "(?:\\s*$FuncArg\\s*,\\s*){$arg_pos,$arg_pos}";
    				}
    				my $test = "\\b$func\\s*\\(${skip_args}($FuncArg(?:\\|\\s*$FuncArg)*)\\s*[,\\)]";
    				if ($stat =~ /$test/) {
    					my $val = $1;
    					$val = $6 if ($skip_args ne "");
    
    					if (!($func =~ /^(?:module_param|proc_create)/ && $val eq "0") &&
    					    (($val =~ /^$Int$/ && $val !~ /^$Octal$/) ||
    					     ($val =~ /^$Octal$/ && length($val) ne 4))) {
    
    						ERROR("NON_OCTAL_PERMISSIONS",
    						      "Use 4 digit octal (0777) not decimal permissions\n" . "$here\n" . $stat_real);
    					}
    					if ($val =~ /^$Octal$/ && (oct($val) & 02)) {
    						ERROR("EXPORTED_WORLD_WRITABLE",
    						      "Exporting writable files is usually an error. Consider more restrictive permissions.\n" . "$here\n" . $stat_real);
    					}
    				}
    			}
    		}
    
    # check for uses of S_<PERMS> that could be octal for readability
    
    		while ($line =~ m{\b($multi_mode_perms_string_search)\b}g) {
    			my $oval = $1;
    			my $octal = perms_to_octal($oval);
    
    			if (WARN("SYMBOLIC_PERMS",
    				 "Symbolic permissions '$oval' are not preferred. Consider using octal permissions '$octal'.\n" . $herecurr) &&
    			    $fix) {
    
    				$fixed[$fixlinenr] =~ s/\Q$oval\E/$octal/;
    
    			}
    		}
    
    # validate content of MODULE_LICENSE against list from include/linux/module.h
    		if ($line =~ /\bMODULE_LICENSE\s*\(\s*($String)\s*\)/) {
    			my $extracted_string = get_quoted_string($line, $rawline);
    			my $valid_licenses = qr{
    						GPL|
    						GPL\ v2|
    						GPL\ and\ additional\ rights|
    						Dual\ BSD/GPL|
    						Dual\ MIT/GPL|
    						Dual\ MPL/GPL|
    						Proprietary
    					}x;
    			if ($extracted_string !~ /^"(?:$valid_licenses)"$/x) {
    				WARN("MODULE_LICENSE",
    				     "unknown module license " . $extracted_string . "\n" . $herecurr);
    			}
    		}
    
    	}
    
    	# If we have no input at all, then there is nothing to report on
    	# so just keep quiet.
    	if ($#rawlines == -1) {
    		exit(0);
    	}
    
    	# In mailback mode only produce a report in the negative, for
    	# things that appear to be patches.
    	if ($mailback && ($clean == 1 || !$is_patch)) {
    		exit(0);
    	}
    
    	# This is not a patch, and we are are in 'no-patch' mode so
    	# just keep quiet.
    	if (!$chk_patch && !$is_patch) {
    		exit(0);
    	}
    
    
    	if (!$is_patch && $filename !~ /cover-letter\.patch$/) {
    
    		ERROR("NOT_UNIFIED_DIFF",
    		      "Does not appear to be a unified-diff format patch\n");
    	}
    
    	if ($is_patch && $has_commit_log && $chk_signoff && $signoff == 0) {
    
    		ERROR("MISSING_SIGN_OFF",
    		      "Missing Signed-off-by: line(s)\n");
    	}
    
    	print report_dump();
    	if ($summary && !($clean == 1 && $quiet == 1)) {
    		print "$filename " if ($summary_file);
    		print "total: $cnt_error errors, $cnt_warn warnings, " .
    			(($check)? "$cnt_chk checks, " : "") .
    			"$cnt_lines lines checked\n";
    	}
    
    	if ($quiet == 0) {
    
    		# If there were any defects found and not already fixing them
    		if (!$clean and !$fix) {
    			print << "EOM"
    
    NOTE: For some of the reported defects, checkpatch may be able to
          mechanically convert to the typical style using --fix or --fix-inplace.
    EOM
    
    		# If there were whitespace errors which cleanpatch can fix
    		# then suggest that.
    		if ($rpt_cleaners) {
    			$rpt_cleaners = 0;
    
    			print << "EOM"
    
    NOTE: Whitespace errors detected.
          You may wish to use scripts/cleanpatch or scripts/cleanfile
    EOM
    
    	if ($clean == 0 && $fix &&
    	    ("@rawlines" ne "@fixed" ||
    	     $#fixed_inserted >= 0 || $#fixed_deleted >= 0)) {
    
    		my $newfile = $filename;
    		$newfile .= ".EXPERIMENTAL-checkpatch-fixes" if (!$fix_inplace);
    		my $linecount = 0;
    		my $f;
    
    
    		@fixed = fix_inserted_deleted_lines(\@fixed, \@fixed_inserted, \@fixed_deleted);
    
    
    		open($f, '>', $newfile)
    		    or die "$P: Can't open $newfile for write\n";
    		foreach my $fixed_line (@fixed) {
    			$linecount++;
    			if ($file) {
    				if ($linecount > 3) {
    					$fixed_line =~ s/^\+//;
    
    					print $f $fixed_line . "\n";
    
    				}
    			} else {
    				print $f $fixed_line . "\n";
    			}
    		}
    		close($f);
    
    		if (!$quiet) {
    			print << "EOM";
    
    Wrote EXPERIMENTAL --fix correction(s) to '$newfile'
    
    Do _NOT_ trust the results written to this file.
    Do _NOT_ submit these changes without inspecting them for correctness.
    
    This EXPERIMENTAL file is simply a convenience to help rewrite patches.
    No warranties, expressed or implied...
    EOM
    		}
    
    	if ($quiet == 0) {
    		print "\n";
    		if ($clean == 1) {
    			print "$vname has no obvious style problems and is ready for submission.\n";
    		} else {
    			print "$vname has style problems, please review.\n";
    		}