From: Daniel P. Berrangé <berrange@redhat.com>
Some checks want to be performed either at the start of a new file
within a patch, or at the end. This is complicated by the fact that
the information relevant to the check may be spread across multiple
lines. It is further complicated by a need to support both git and
non-git diffs, and special handling for renames where there might
not be any patch hunks.
To handle this more sanely, introduce explicit tracking of file
start/end, taking account of git metadata, and calling a hook
function at each transition.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
scripts/checkpatch.pl | 109 ++++++++++++++++++++++++++++++++++++++++--
1 file changed, 106 insertions(+), 3 deletions(-)
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index 7675418b0b..b74391e63a 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -1417,6 +1417,38 @@ sub checkspdx {
}
}
+# All three of the methods below take a 'file info' record
+# which is a hash ref containing
+#
+# 'isgit': is this from an enhanced git diff or plain diff
+# 'linestart': line number of start of file diff
+# 'lineend': line number of end of file diff
+# 'filenew': the new filename
+# 'fileold': the old filename (same as 'new filename' except
+# for renames in git diffs)
+# 'action': one of 'modified' (always) or 'new' or 'deleted' or
+# 'renamed' (git diffs only)
+# 'mode': file mode for new/deleted files (git diffs only)
+# 'similarity': file similarity when renamed (git diffs only)
+# 'facts': hash ref for storing any metadata related to checks
+#
+
+# Called at the end of each patch, with the list of
+# real filenames that were seen in the patch
+sub process_file_list {
+ my @fileinfos = @_;
+}
+
+# Called at the start of processing a diff hunk for a file
+sub process_start_of_file {
+ my $fileinfo = shift;
+}
+
+# Called at the end of processing a diff hunk for a file
+sub process_end_of_file {
+ my $fileinfo = shift;
+}
+
sub process {
my $filename = shift;
@@ -1453,7 +1485,10 @@ sub process {
my $realfile = '';
my $realline = 0;
my $realcnt = 0;
+ my $fileinfo;
+ my @fileinfolist;
my $here = '';
+ my $oldhere = '';
my $in_comment = 0;
my $comment_edge = 0;
my $first_line = 0;
@@ -1591,17 +1626,56 @@ sub process {
$prefix = "$filename:$realline: " if ($emacs && $file);
$prefix = "$filename:$linenr: " if ($emacs && !$file);
+ $oldhere = $here;
$here = "#$linenr: " if (!$file);
$here = "#$realline: " if ($file);
# extract the filename as it passes
- if ($line =~ /^diff --git.*?(\S+)$/) {
- $realfile = $1;
- $realfile =~ s@^([^/]*)/@@ if (!$file);
+ if ($line =~ /^diff --git\s+(\S+)\s+(\S+)$/) {
+ my $fileold = $1;
+ my $filenew = $2;
+
+ if (defined $fileinfo) {
+ $fileinfo->{lineend} = $oldhere;
+ process_end_of_file($fileinfo)
+ }
+ $fileold =~ s@^([^/]*)/@@ if (!$file);
+ $filenew =~ s@^([^/]*)/@@ if (!$file);
+ $realfile = $filenew;
checkfilename($realfile, \$acpi_testexpected, \$acpi_nontestexpected);
+
+ $fileinfo = {
+ "isgit" => 1,
+ "githeader" => 1,
+ "linestart" => $here,
+ "lineend" => 0,
+ "fileold" => $fileold,
+ "filenew" => $filenew,
+ "action" => "modified",
+ "mode" => 0,
+ "similarity" => 0,
+ "facts" => {},
+ };
+ push @fileinfolist, $fileinfo;
+ } elsif (defined $fileinfo && $fileinfo->{githeader} &&
+ $line =~ /^(new|deleted) (?:file )?mode\s+([0-7]+)$/) {
+ $fileinfo->{action} = $1;
+ $fileinfo->{mode} = oct($2);
+ } elsif (defined $fileinfo && $fileinfo->{githeader} &&
+ $line =~ /^similarity index (\d+)%/) {
+ $fileinfo->{similarity} = int($1);
+ } elsif (defined $fileinfo && $fileinfo->{githeader} &&
+ $line =~ /^rename (from|to) [\w\/\.\-]+\s*$/) {
+ $fileinfo->{action} = "renamed";
+ # For a no-change rename, we'll never have any "+++..."
+ # lines, so trigger actions now
+ if ($1 eq "to" && $fileinfo->{similarity} == 100) {
+ process_start_of_file($fileinfo);
+ }
} elsif ($line =~ /^\+\+\+\s+(\S+)/) {
$realfile = $1;
$realfile =~ s@^([^/]*)/@@ if (!$file);
+
checkfilename($realfile, \$acpi_testexpected, \$acpi_nontestexpected);
$p1_prefix = $1;
@@ -1610,6 +1684,30 @@ sub process {
WARN("patch prefix '$p1_prefix' exists, appears to be a -p0 patch\n");
}
+ if (defined $fileinfo && !$fileinfo->{isgit}) {
+ $fileinfo->{lineend} = $oldhere;
+ process_end_of_file($fileinfo);
+ }
+
+ if (!defined $fileinfo || !$fileinfo->{isgit}) {
+ $fileinfo = {
+ "isgit" => 0,
+ "githeader" => 0,
+ "linestart" => $here,
+ "lineend" => 0,
+ "fileold" => $realfile,
+ "filenew" => $realfile,
+ "action" => "modified",
+ "mode" => 0,
+ "similarity" => 0,
+ "facts" => {},
+ };
+ push @fileinfolist, $fileinfo;
+ } else {
+ $fileinfo->{githeader} = 0;
+ }
+ process_start_of_file($fileinfo);
+
next;
}
@@ -3213,6 +3311,11 @@ sub process {
}
}
+ if (defined $fileinfo) {
+ process_end_of_file($fileinfo);
+ }
+ process_file_list(@fileinfolist);
+
if ($is_patch && $chk_signoff && $signoff == 0) {
ERROR("Missing Signed-off-by: line(s)\n");
}
--
2.49.0
On 5/12/25 20:24, Daniel P. Berrangé wrote:
> From: Daniel P. Berrangé <berrange@redhat.com>
>
> Some checks want to be performed either at the start of a new file
> within a patch, or at the end. This is complicated by the fact that
> the information relevant to the check may be spread across multiple
> lines. It is further complicated by a need to support both git and
> non-git diffs, and special handling for renames where there might
> not be any patch hunks.
>
> To handle this more sanely, introduce explicit tracking of file
> start/end, taking account of git metadata, and calling a hook
> function at each transition.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> scripts/checkpatch.pl | 109 ++++++++++++++++++++++++++++++++++++++++--
> 1 file changed, 106 insertions(+), 3 deletions(-)
>
> diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
> index 7675418b0b..b74391e63a 100755
> --- a/scripts/checkpatch.pl
> +++ b/scripts/checkpatch.pl
> @@ -1417,6 +1417,38 @@ sub checkspdx {
> }
> }
>
> +# All three of the methods below take a 'file info' record
> +# which is a hash ref containing
> +#
> +# 'isgit': is this from an enhanced git diff or plain diff
> +# 'linestart': line number of start of file diff
> +# 'lineend': line number of end of file diff
> +# 'filenew': the new filename
> +# 'fileold': the old filename (same as 'new filename' except
> +# for renames in git diffs)
> +# 'action': one of 'modified' (always) or 'new' or 'deleted' or
> +# 'renamed' (git diffs only)
It would be nice to have some support for 'quilt' patches too. This would
mean being able to match '(---|+++) /dev/null' for new and deleted files.
Anyhow, that's for another series.
> +# 'mode': file mode for new/deleted files (git diffs only)
> +# 'similarity': file similarity when renamed (git diffs only)
> +# 'facts': hash ref for storing any metadata related to checks
> +#
> +
> +# Called at the end of each patch, with the list of
> +# real filenames that were seen in the patch
> +sub process_file_list {
> + my @fileinfos = @_;
> +}
> +
> +# Called at the start of processing a diff hunk for a file
> +sub process_start_of_file {
> + my $fileinfo = shift;
> +}
> +
> +# Called at the end of processing a diff hunk for a file
> +sub process_end_of_file {
> + my $fileinfo = shift;
> +}
> +
> sub process {
> my $filename = shift;
>
> @@ -1453,7 +1485,10 @@ sub process {
> my $realfile = '';
> my $realline = 0;
> my $realcnt = 0;
> + my $fileinfo;
> + my @fileinfolist;
> my $here = '';
> + my $oldhere = '';
> my $in_comment = 0;
> my $comment_edge = 0;
> my $first_line = 0;
> @@ -1591,17 +1626,56 @@ sub process {
> $prefix = "$filename:$realline: " if ($emacs && $file);
> $prefix = "$filename:$linenr: " if ($emacs && !$file);
>
> + $oldhere = $here;
> $here = "#$linenr: " if (!$file);
> $here = "#$realline: " if ($file);
>
> # extract the filename as it passes
> - if ($line =~ /^diff --git.*?(\S+)$/) {
> - $realfile = $1;
> - $realfile =~ s@^([^/]*)/@@ if (!$file);
> + if ($line =~ /^diff --git\s+(\S+)\s+(\S+)$/) {
> + my $fileold = $1;
> + my $filenew = $2;
> +
> + if (defined $fileinfo) {
> + $fileinfo->{lineend} = $oldhere;
> + process_end_of_file($fileinfo)
> + }
> + $fileold =~ s@^([^/]*)/@@ if (!$file);
> + $filenew =~ s@^([^/]*)/@@ if (!$file);
> + $realfile = $filenew;
> checkfilename($realfile, \$acpi_testexpected, \$acpi_nontestexpected);
> +
> + $fileinfo = {
> + "isgit" => 1,
> + "githeader" => 1,
> + "linestart" => $here,
> + "lineend" => 0,
> + "fileold" => $fileold,
> + "filenew" => $filenew,
> + "action" => "modified",
> + "mode" => 0,
> + "similarity" => 0,
> + "facts" => {},
> + };
> + push @fileinfolist, $fileinfo;
> + } elsif (defined $fileinfo && $fileinfo->{githeader} &&
> + $line =~ /^(new|deleted) (?:file )?mode\s+([0-7]+)$/) {
> + $fileinfo->{action} = $1;
> + $fileinfo->{mode} = oct($2);
> + } elsif (defined $fileinfo && $fileinfo->{githeader} &&
> + $line =~ /^similarity index (\d+)%/) {
> + $fileinfo->{similarity} = int($1);
> + } elsif (defined $fileinfo && $fileinfo->{githeader} &&
> + $line =~ /^rename (from|to) [\w\/\.\-]+\s*$/) {
> + $fileinfo->{action} = "renamed";
> + # For a no-change rename, we'll never have any "+++..."
> + # lines, so trigger actions now
> + if ($1 eq "to" && $fileinfo->{similarity} == 100) {
> + process_start_of_file($fileinfo);
> + }
> } elsif ($line =~ /^\+\+\+\s+(\S+)/) {
> $realfile = $1;
> $realfile =~ s@^([^/]*)/@@ if (!$file);
> +
> checkfilename($realfile, \$acpi_testexpected, \$acpi_nontestexpected);
>
> $p1_prefix = $1;
> @@ -1610,6 +1684,30 @@ sub process {
> WARN("patch prefix '$p1_prefix' exists, appears to be a -p0 patch\n");
> }
>
> + if (defined $fileinfo && !$fileinfo->{isgit}) {
> + $fileinfo->{lineend} = $oldhere;
> + process_end_of_file($fileinfo);
> + }
> +
> + if (!defined $fileinfo || !$fileinfo->{isgit}) {
> + $fileinfo = {
> + "isgit" => 0,
> + "githeader" => 0,
> + "linestart" => $here,
> + "lineend" => 0,
> + "fileold" => $realfile,
> + "filenew" => $realfile,
> + "action" => "modified",
> + "mode" => 0,
> + "similarity" => 0,
> + "facts" => {},
> + };
> + push @fileinfolist, $fileinfo;
> + } else {
> + $fileinfo->{githeader} = 0;
> + }
> + process_start_of_file($fileinfo);
> +
> next;
> }
>
> @@ -3213,6 +3311,11 @@ sub process {
> }
> }
>
> + if (defined $fileinfo) {
> + process_end_of_file($fileinfo);
> + }
> + process_file_list(@fileinfolist);
> +
> if ($is_patch && $chk_signoff && $signoff == 0) {
> ERROR("Missing Signed-off-by: line(s)\n");
> }
© 2016 - 2025 Red Hat, Inc.