#!/usr/bin/perl -n # SPDX-License-Identifier: GPL-2.0-or-later # # Copyright (C) 2018,2021 Red Hat, Inc. # # $ perldoc checkpatch.pl for eye-pleasing view of the manual: =head1 NAME checkpatch.pl - check for common mistakes =head1 SYNOPSIS checkpatch.pl [ ...] =head1 DESCRIPTION B checks source files or patches for common mistakes. =head1 OPTIONS =over 4 =item B<< >> A C source file or an unified diff. =back =cut use strict; use warnings; chomp; our $is_patch; our $is_file; our $is_commit_message; our $seen_error; our $line; # Current line our $check_line; # Complain if errors are found on this line our @functions_seen; our $type; our $filename; our $line_no; our $indent; our $check_is_todo; our $expect_spdx; our $subdir; sub new_hunk { $type = undef; $indent = undef; } sub new_file { $expect_spdx = 0; $check_is_todo = 1; $filename = $subdir // ''; $filename .= shift; @functions_seen = (); } my $header = $ENV{'NM_CHECKPATCH_HEADER'}; sub complain { my $message = shift; my $plain_message = shift; return unless $check_line; if (defined($header)) { warn "$header\n"; undef $header; } if ($plain_message) { warn "$message\n"; } else { warn "$filename:$line_no: $message:\n"; warn "> $line\n\n"; } $seen_error = 1; } sub check_commit { my $commit = shift; my $required = shift; my $commit_id; my $commit_message; if ($commit =~ /^([0-9a-f]{5,})\b/) { $commit_id = $1; } else { return unless $required; } if ($commit_id and not system 'git rev-parse --git-dir >/dev/null 2>/dev/null') { $commit_message = `git log --abbrev=12 --pretty=format:"%h ('%s')" -1 "$commit_id" 2>/dev/null`; complain "Commit '$commit_id' does not seem to exist" unless $commit_message; } $commit_message //= "<12 hex digits> ('')"; complain "Refer to the commit id properly: $commit_message" unless $commit =~ /^[0-9a-f]{12} \('/; } if ($is_patch) { # This is a line of an unified diff if (/^@@.*\+(\d+)/) { $line_no = $1 - 1; new_hunk; next; } if (/^\+\+\+ (b\/)?(.*)/) { new_file ($2); next; } s/^([ \+])(.*)/$2/ or next; $line_no++; $check_line = $1 eq '+'; $line = $2; } elsif ($is_file) { $line_no = $.; $. = 0 if eof; # This is a line from full C file $check_line = 1; $line = $_; } elsif ($is_commit_message) { $line_no++; $filename = '(commit message)'; $check_line = 1; $line = $_; /^---$/ and $is_commit_message = 0; /^(Reverts|Fixes): *(.*)/ and check_commit ($2, 1); /This reverts commit/ and next; /cherry picked from/ and next; /^git-subtree-dir: (.*)/ and $subdir = "$1/"; /\bcommit (.*)/ and check_commit ($1, 0); next; } else { # We don't handle these yet /^diff --cc/ and exit 0; $filename = ''; $line_no = 1; # We don't know if we're dealing with a patch or a C file yet $is_commit_message = 1 if /^From \S/; $is_file = 1 if /^#/; $is_patch = 1 if /^---/; next; } if ($is_file and $filename ne $ARGV) { new_file ($ARGV); new_hunk; } if ($filename !~ /\.[ch]$/) { if ($check_is_todo) { complain("Resolve todo list \"$filename\" first\n", 1) if $filename =~ /^TODO.txt$/; $check_is_todo = 0; } next; } next if $filename =~ /\/nm-[^\/]+-enum-types\.[ch]$/; next if $filename =~ /\b(shared|src)\/systemd\// and not $filename =~ /\/sd-adapt\// and not $filename =~ /\/nm-/; next if $filename =~ /\/(n-acd|c-list|c-siphash|n-dhcp4)\//; $expect_spdx = 1 if $line_no == 1; $expect_spdx = 0 if $line =~ /SPDX-License-Identifier/; complain ('Missing a SPDX-License-Identifier') if $line_no == 2 and $expect_spdx; complain ('Tabs are only allowed at the beginning of a line') if $line =~ /[^\t]\t/; complain ('Trailing whitespace') if $line =~ /[ \t]$/; complain ('Don\'t use glib typedefs for char/short/int/long/float/double') if $line =~ /\bg(char|short|int|long|float|double)\b/; complain ("Don't use \"$1 $2\" instead of \"$2 $1\"") if $line =~ /\b(char|short|int|long) +(unsigned|signed)\b/; complain ("Don't use \"unsigned int\" but just use \"unsigned\"") if $line =~ /\b(unsigned) +(int)\b/; complain ("Please use LGPL-2.1-or-later SPDX tag for new files") if $is_patch and $line =~ /SPDX-License-Identifier/ and not /LGPL-2.1-or-later/; complain ("Use a SPDX-License-Identifier instead of Licensing boilerplate") if $is_patch and $line =~ /under the terms of/; complain ("Don't use space inside elvis operator ?:") if $line =~ /\?[\t ]+:/; complain ("Don't add Emacs editor formatting hints to source files") if $line_no == 1 and $line =~ /-\*-.+-\*-/; complain ("XXX marker are reserved for development while work-in-progress. Use TODO or FIXME comment instead?") if $line =~ /\bXXX\b/; complain ("This gtk-doc annotation looks wrong") if $line =~ /\*.*\( *(transfer-(none|container|full)|allow none) *\) *(:|\()/; complain ("Prefer nm_assert() or g_return*() to g_assert*()") if $line =~ /g_assert/ and (not $filename =~ /\/tests\//) and (not $filename =~ /\/nm-test-/); complain ("Use gs_free_error with GError variables") if $line =~ /\bgs_free\b +GError *\*/; complain ("Initialize GError variables to NULL, if you pass them on") if $line =~ /\bGError +\*([a-z0-9_]+);/; complain ("Don't use strcmp/g_strcmp0 unless you need to sort. Consider nm_streq()/nm_streq0(),NM_IN_STRSET() for testing equality") if $line =~ /\b(strcmp|g_strcmp0)\b/; complain ("Don't use API that uses the numeric source id. Instead, use GSource and API like nm_g_idle_add(), nm_g_idle_add_source(), nm_clear_g_source_inst(), etc.") if $line =~ /\b(g_idle_add|g_idle_add_full|g_timeout_add|g_timeout_add_seconds|g_source_remove|nm_clear_g_source)\b/; complain ("Prefer g_snprintf() over snprintf() (for consistency)") if $line =~ /\b(snprintf)\b/; complain ("Avoid g_clear_pointer() and use nm_clear_pointer() (or nm_clear_g_free(), g_clear_object(), etc.)") if $line =~ /\b(g_clear_pointer)\b/; complain ("Define setting properties with _nm_setting_property_define_direct_*() API") if $line =~ /g_param_spec_/ and $filename =~ /\/libnm-core-impl\/nm-setting/; complain ("Use spaces instead of tabs") if $line =~ /\t/; # Further on we process stuff without comments. $_ = $line; s/\s*\/\*.*\*\///; s/\s*\/\*.*//; s/\s*\/\/.*//; /^\s* \* / and next; if (/^typedef*/) { # We expect the { on the same line as the typedef. Otherwise it # looks too much like a function declaration complain ('Unexpected line break following a typedef') unless /[;{,]$/; next; } elsif (/^[A-Za-z_][A-Za-z0-9_ ]*\*?$/ and /[a-z]/) { # A function type $type = $_; next; } elsif ($type and /^([A-Za-z_][A-Za-z0-9_]*)(\s*)\(/) { my @order = qw/^get_property$ ^set_property$ (? Check a single file. =item B Check the currently staged changes. =item B A F<.git/hooks/post-commit> oneliner that, wisely, tolerates failures while still providing advice. The large line context allows helps checkpatch.pl get a better idea about the changes in context of code that does not change. =back =head1 BUGS Proabably too many. =head1 SEE ALSO F =head1 COPYRIGHT Copyright (C) 2018,2021 Red Hat This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. =head1 AUTHOR Lubomir Rintel C =cut