Line data Source code
1 : // Copyright (C) 2025-2026 Free Software Foundation, Inc.
2 :
3 : // This file is part of GCC.
4 :
5 : // GCC is free software; you can redistribute it and/or modify it under
6 : // the terms of the GNU General Public License as published by the Free
7 : // Software Foundation; either version 3, or (at your option) any later
8 : // version.
9 :
10 : // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
11 : // WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 : // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 : // for more details.
14 :
15 : // You should have received a copy of the GNU General Public License
16 : // along with GCC; see the file COPYING3. If not see
17 : // <http://www.gnu.org/licenses/>.
18 :
19 : #include "rust-unused-checker.h"
20 : #include "rust-hir-expr.h"
21 : #include "rust-hir-generic-param.h"
22 : #include "rust-hir-item.h"
23 :
24 : #include "options.h"
25 : #include "rust-keyword-values.h"
26 :
27 : namespace Rust {
28 : namespace Analysis {
29 9 : UnusedChecker::UnusedChecker ()
30 9 : : nr_context (
31 9 : Resolver2_0::ImmutableNameResolutionContext::get ().resolver ()),
32 18 : mappings (Analysis::Mappings::get ()), unused_context (UnusedContext ())
33 9 : {}
34 : void
35 9 : UnusedChecker::go (HIR::Crate &crate)
36 : {
37 9 : UnusedCollector collector (unused_context);
38 9 : collector.go (crate);
39 25 : for (auto &item : crate.get_items ())
40 16 : item->accept_vis (*this);
41 9 : }
42 :
43 : bool
44 21 : is_snake_case (Identifier identifier)
45 : {
46 21 : auto s = identifier.as_string ();
47 21 : return std::all_of (s.begin (), s.end (), [] (unsigned char c) {
48 48 : return ISLOWER (c) || ISDIGIT (c) || c == '_';
49 21 : });
50 21 : }
51 :
52 : void
53 0 : UnusedChecker::visit (HIR::ConstantItem &item)
54 : {
55 0 : std::string var_name = item.get_identifier ().as_string ();
56 0 : auto id = item.get_mappings ().get_hirid ();
57 0 : if (!unused_context.is_variable_used (id) && var_name[0] != '_')
58 0 : rust_warning_at (item.get_locus (), OPT_Wunused_variable,
59 : "unused variable %qs",
60 0 : item.get_identifier ().as_string ().c_str ());
61 0 : }
62 :
63 : void
64 2 : UnusedChecker::visit (HIR::StaticItem &item)
65 : {
66 4 : std::string var_name = item.get_identifier ().as_string ();
67 2 : auto id = item.get_mappings ().get_hirid ();
68 2 : if (!unused_context.is_variable_used (id) && var_name[0] != '_')
69 1 : rust_warning_at (item.get_locus (), OPT_Wunused_variable,
70 : "unused variable %qs",
71 2 : item.get_identifier ().as_string ().c_str ());
72 :
73 2 : if (!std::all_of (var_name.begin (), var_name.end (), [] (unsigned char c) {
74 6 : return ISUPPER (c) || ISDIGIT (c) || c == '_';
75 : }))
76 1 : rust_warning_at (item.get_locus (), OPT_Wunused_variable,
77 : "static variable %qs should have an upper case name",
78 : var_name.c_str ());
79 2 : }
80 :
81 : void
82 0 : UnusedChecker::visit (HIR::TraitItemFunc &item)
83 : {
84 : // TODO: check trait item functions if they are not derived.
85 0 : }
86 : void
87 8 : UnusedChecker::visit (HIR::IdentifierPattern &pattern)
88 : {
89 16 : std::string var_name = pattern.get_identifier ().as_string ();
90 8 : auto id = pattern.get_mappings ().get_hirid ();
91 8 : if (!unused_context.is_variable_used (id)
92 8 : && var_name != Values::Keywords::SELF && var_name[0] != '_')
93 1 : rust_warning_at (pattern.get_locus (), OPT_Wunused_variable,
94 : "unused variable %qs",
95 2 : pattern.get_identifier ().as_string ().c_str ());
96 :
97 8 : if (pattern.is_mut () && !unused_context.is_mut_used (id)
98 9 : && var_name != Values::Keywords::SELF && var_name[0] != '_')
99 1 : rust_warning_at (pattern.get_locus (), OPT_Wunused_variable,
100 : "unused mut %qs",
101 2 : pattern.get_identifier ().as_string ().c_str ());
102 :
103 8 : if (!is_snake_case (pattern.get_identifier ()))
104 1 : rust_warning_at (pattern.get_locus (), OPT_Wunused_variable,
105 : "variable %qs should have a snake case name",
106 : var_name.c_str ());
107 8 : }
108 : void
109 :
110 9 : UnusedChecker::visit (HIR::AssignmentExpr &expr)
111 :
112 : {
113 9 : const auto &lhs = expr.get_lhs ();
114 9 : auto var_name = lhs.to_string ();
115 9 : NodeId ast_node_id = lhs.get_mappings ().get_nodeid ();
116 9 : NodeId def_id = nr_context.lookup (ast_node_id).value ();
117 9 : HirId id = mappings.lookup_node_to_hir (def_id).value ();
118 9 : if (unused_context.is_variable_assigned (id, lhs.get_mappings ().get_hirid ())
119 9 : && var_name[0] != '_')
120 7 : rust_warning_at (lhs.get_locus (), OPT_Wunused_variable,
121 : "unused assignment %qs", var_name.c_str ());
122 9 : }
123 :
124 : void
125 2 : UnusedChecker::visit (HIR::StructPatternFieldIdent &pattern)
126 : {
127 4 : std::string var_name = pattern.get_identifier ().as_string ();
128 2 : auto id = pattern.get_mappings ().get_hirid ();
129 2 : if (!unused_context.is_variable_used (id)
130 2 : && var_name != Values::Keywords::SELF && var_name[0] != '_')
131 0 : rust_warning_at (pattern.get_locus (), OPT_Wunused_variable,
132 : "unused variable %qs",
133 0 : pattern.get_identifier ().as_string ().c_str ());
134 :
135 2 : if (pattern.is_mut () && !unused_context.is_mut_used (id)
136 4 : && var_name != Values::Keywords::SELF && var_name[0] != '_')
137 2 : rust_warning_at (pattern.get_locus (), OPT_Wunused_variable,
138 : "unused mut %qs",
139 4 : pattern.get_identifier ().as_string ().c_str ());
140 2 : }
141 :
142 : void
143 1 : UnusedChecker::visit (HIR::EmptyStmt &stmt)
144 : {
145 1 : rust_warning_at (stmt.get_locus (), OPT_Wunused_variable,
146 : "unnecessary trailing semicolons");
147 1 : }
148 :
149 : void
150 11 : UnusedChecker::visit (HIR::Function &fct)
151 : {
152 11 : if (!is_snake_case (fct.get_function_name ()))
153 1 : rust_warning_at (fct.get_locus (), OPT_Wunused_variable,
154 : "function %qs should have a snake case name",
155 2 : fct.get_function_name ().as_string ().c_str ());
156 11 : walk (fct);
157 11 : }
158 :
159 : void
160 1 : UnusedChecker::visit (HIR::Module &mod)
161 : {
162 1 : if (!is_snake_case (mod.get_module_name ()))
163 1 : rust_warning_at (mod.get_locus (), OPT_Wunused_variable,
164 : "module %qs should have a snake case name",
165 2 : mod.get_module_name ().as_string ().c_str ());
166 1 : walk (mod);
167 1 : }
168 :
169 : void
170 1 : UnusedChecker::visit (HIR::LifetimeParam &lft)
171 : {
172 3 : if (!is_snake_case (lft.get_lifetime ().get_name ()))
173 1 : rust_warning_at (lft.get_locus (), OPT_Wunused_variable,
174 : "lifetime %qs should have a snake case name",
175 2 : lft.get_lifetime ().get_name ().c_str ());
176 1 : walk (lft);
177 1 : }
178 :
179 : void
180 4 : UnusedChecker::visit_loop_label (HIR::LoopLabel &label)
181 : {
182 4 : auto lifetime = label.get_lifetime ();
183 4 : std::string var_name = lifetime.to_string ();
184 4 : auto id = lifetime.get_mappings ().get_hirid ();
185 4 : if (!unused_context.is_label_used (id) && var_name[0] != '_')
186 2 : rust_warning_at (lifetime.get_locus (), OPT_Wunused_variable,
187 4 : "unused label %qs", lifetime.to_string ().c_str ());
188 4 : }
189 :
190 : } // namespace Analysis
191 : } // namespace Rust
|