xref: /linux/scripts/kconfig/kconfig-sym-check.pl (revision 0eaed89c18aeedf0898baf2dbf5ff027c6795152)
1#!/usr/bin/env perl
2# SPDX-License-Identifier: GPL-2.0
3
4use warnings;
5use strict;
6
7my $srctree = shift @ARGV;
8unless (defined $srctree) {
9	$srctree = `git rev-parse --show-toplevel 2>/dev/null`;
10	chomp $srctree;
11	my $msg = "Usage: $0 <srctree> [excludes file]\n";
12	$msg .= "Please provide <srctree>.";
13	$msg .= " Is it '$srctree'?" if $srctree;
14	$msg .= "\n";
15	die $msg;
16}
17my $kconfig_sym_check_excludes = defined $ARGV[0] ? $ARGV[0] : undef;
18
19sub indent_depth {
20	my ($ws) = @_;
21	my $col = 0;
22	for my $c (split //, $ws) {
23		$col = $c eq "\t" ? int($col / 8) * 8 + 8 : $col + 1;
24	}
25	return $col;
26}
27
28my @files = `git -C \Q$srctree\E ls-files '*Kconfig*' 2>/dev/null`;
29if (@files) {
30	chomp @files;
31	@files = map { "$srctree/$_" } @files;
32} else {
33	@files = `find \Q$srctree\E -name '*Kconfig*'`;
34	chomp @files;
35}
36
37@files = grep { !m{/scripts/kconfig/tests/} } @files;
38
39my %configs = ();
40my %refs = ();
41
42foreach my $file (@files) {
43	open F, $file or die "Cannot open $file: $!";
44
45	my $help = 0;
46	my $help_level;
47	my $level;
48
49	while (<F>) {
50		chomp;
51
52		while (/\\\s*$/) {
53			s/\\\s*$/ /;
54			my $cont = <F> // last;
55			chomp $cont;
56			$_ .= $cont;
57		}
58
59		next if /^\s*$/;
60		next if /^\s*#/;
61
62		/^(\s*)/;
63		$level = indent_depth($1);
64
65		if ($help && $level < $help_level) {
66			$help = 0;
67		}
68
69		next if ($help);
70
71		if (/^\s*(help|\-\-\-help\-\-\-)$/) {
72			$help = 1;
73			my $next;
74			while (defined($next = <F>)) {
75				last unless $next =~ /^\s*(?:#.*)?$/;
76			}
77			last unless defined $next;
78			$next =~ /^(\s*)/;
79			if (indent_depth($1) >= $level) {
80				$help_level = indent_depth($1);
81			} else {
82				$help = 0;
83			}
84			$_ = $next;
85			redo;
86		}
87
88		if (/^\s*(config|menuconfig)\s+([a-zA-Z0-9_]+)\s*(#.*)?$/) {
89			$configs{$2}++;
90			next;
91		}
92
93		if (/^\s*(default|def_bool|def_tristate|select|depends\s+on|imply|visible\s+if|range|if|bool|tristate|int|hex|string|prompt)\s+(.+)\s*$/) {
94			my $s = $2;
95			$s =~ s/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'//g;
96			$s =~ s/#.*//;
97			$s =~ s/\$\((?:[^()]*|\((?:[^()]*|\([^()]*\))*\))*\)//g;
98			$s =~ s/%%[^%]*%%//g;
99			my @syms = split /[^a-zA-Z0-9_]+/, $s;
100			map {
101				$refs{$_}++ if (/[a-zA-Z]/ && $_ ne "if" && $_ ne "y" && $_ ne "n" && $_ ne "m" && !/^0[xX][0-9a-fA-F]+$/);
102			} @syms
103		}
104	}
105
106	close F;
107}
108
109my %known_syms = ();
110if (defined $kconfig_sym_check_excludes) {
111	my $file = $kconfig_sym_check_excludes;
112	open(F, "<", $file) or die "Cannot open $file: $!";
113	while (<F>) {
114		chomp;
115		next if /^\s*$/;
116		next if /^\s*#/;
117		$known_syms{$1}++ if (/^\s*([a-zA-Z0-9_]+)\s*(#.*)?$/);
118	}
119}
120
121my $ret = 0;
122foreach my $k (sort keys %refs) {
123	next if (exists $configs{$k} || exists $known_syms{$k});
124
125	print "$k";
126	print " - warning: '$k' is probably not what you want; Kconfig tristate literals are always lowercase ('n', 'y', 'm')" if ($k eq "N" || $k eq "Y" || $k eq "M");
127	print "\n";
128
129	$ret = 1;
130}
131
132exit $ret;
133