From f27c7a756d1148619f5bba518e1a0f8493699a7d Mon Sep 17 00:00:00 2001
From: Ryutaro Okada <1015ryu88@gmail.com>
Date: Sat, 9 Aug 2025 19:24:56 -0700
Subject: [PATCH 1/5] Implement unused variable checker on HIR.
gcc/rust/ChangeLog:
* Make-lang.in: New file.
* checks/lints/unused-var/rust-unused-var-checker.cc (UnusedVarChecker): Implement unused
variable checker.
* checks/lints/unused-var/rust-unused-var-checker.h (UnusedVarChecker): Implement unused
variable checker.
* checks/lints/unused-var/rust-unused-var-collector.cc (UnusedVarCollector): Implement
unused variable collector.
* checks/lints/unused-var/rust-unused-var-collector.h (UnusedVarCollector): Implement
unused variable collector.
* checks/lints/unused-var/rust-unused-var-context.cc (UnusedVarContext): Implement
unused variable context.
* checks/lints/unused-var/rust-unused-var-context.h (UnusedVarContext): Implement unused
variable context.
Signed-off-by: Ryutaro Okada <1015ryu88@gmail.com>
---
gcc/rust/Make-lang.in | 9 ++
.../unused-var/rust-unused-var-checker.cc | 128 +++++++++++++++++
.../unused-var/rust-unused-var-checker.h | 48 +++++++
.../unused-var/rust-unused-var-collector.cc | 130 ++++++++++++++++++
.../unused-var/rust-unused-var-collector.h | 71 ++++++++++
.../unused-var/rust-unused-var-context.cc | 69 ++++++++++
.../unused-var/rust-unused-var-context.h | 41 ++++++
7 files changed, 496 insertions(+)
create mode 100644 gcc/rust/checks/lints/unused-var/rust-unused-var-checker.cc
create mode 100644 gcc/rust/checks/lints/unused-var/rust-unused-var-checker.h
create mode 100644 gcc/rust/checks/lints/unused-var/rust-unused-var-collector.cc
create mode 100644 gcc/rust/checks/lints/unused-var/rust-unused-var-collector.h
create mode 100644 gcc/rust/checks/lints/unused-var/rust-unused-var-context.cc
create mode 100644 gcc/rust/checks/lints/unused-var/rust-unused-var-context.h
diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in
index 8cdf6c98331..cfaa355aa4c 100644
--- a/gcc/rust/Make-lang.in
+++ b/gcc/rust/Make-lang.in
@@ -208,6 +208,9 @@ GRS_OBJS = \
rust/rust-const-checker.o \
rust/rust-lint-marklive.o \
rust/rust-lint-unused-var.o \
+ rust/rust-unused-var-checker.o \
+ rust/rust-unused-var-collector.o \
+ rust/rust-unused-var-context.o \
rust/rust-readonly-check.o \
rust/rust-readonly-check2.o \
rust/rust-hir-type-check-path.o \
@@ -441,6 +444,7 @@ RUST_INCLUDES = -I $(srcdir)/rust \
-I $(srcdir)/rust/typecheck \
-I $(srcdir)/rust/checks/lints \
-I $(srcdir)/rust/checks/errors \
+ -I $(srcdir)/rust/checks/lints/unused-var \
-I $(srcdir)/rust/checks/errors/privacy \
-I $(srcdir)/rust/checks/errors/borrowck \
-I $(srcdir)/rust/util \
@@ -510,6 +514,11 @@ rust/%.o: rust/checks/lints/%.cc
$(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
$(POSTCOMPILE)
+# build unused variable checking pass files in rust folder
+rust/%.o: rust/checks/lints/unused-var/%.cc
+ $(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
+ $(POSTCOMPILE)
+
# build rust/checks/errors files in rust folder
rust/%.o: rust/checks/errors/%.cc
$(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
diff --git a/gcc/rust/checks/lints/unused-var/rust-unused-var-checker.cc b/gcc/rust/checks/lints/unused-var/rust-unused-var-checker.cc
new file mode 100644
index 00000000000..d828bbc5ca1
--- /dev/null
+++ b/gcc/rust/checks/lints/unused-var/rust-unused-var-checker.cc
@@ -0,0 +1,128 @@
+// Copyright (C) 2025 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC 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 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3. If not see
+// .
+
+#include "rust-unused-var-checker.h"
+#include "rust-hir-item.h"
+
+#include "options.h"
+
+namespace Rust {
+namespace Analysis {
+UnusedVarChecker::UnusedVarChecker ()
+ : nr_context (
+ Resolver2_0::ImmutableNameResolutionContext::get ().resolver ()),
+ mappings (Analysis::Mappings::get ()),
+ unused_var_context (*UnusedVarContext::get ())
+{}
+void
+UnusedVarChecker::go (HIR::Crate &crate)
+{
+ UnusedVarCollector ().go (crate);
+ for (auto &item : crate.get_items ())
+ item->accept_vis (*this);
+}
+void
+UnusedVarChecker::visit (HIR::LetStmt &stmt)
+{
+ HIR::Pattern &pattern = stmt.get_pattern ();
+ check_variable (pattern);
+ walk (stmt);
+}
+void
+UnusedVarChecker::visit_function_param (HIR::FunctionParam ¶m)
+{
+ check_variable (param.get_param_name ());
+}
+
+void
+UnusedVarChecker::visit (HIR::ConstantItem &item)
+{
+ std::string var_name = item.get_identifier ().as_string ();
+ bool starts_with_under_score = var_name.compare (0, 1, "_") == 0;
+ auto id = item.get_mappings ().get_hirid ();
+ if (!unused_var_context.is_variable_used (id) && !starts_with_under_score)
+ rust_warning_at (item.get_locus (), OPT_Wunused_variable,
+ "unused name '%s'",
+ item.get_identifier ().as_string ().c_str ());
+}
+
+void
+UnusedVarChecker::visit (HIR::StaticItem &item)
+{
+ std::string var_name = item.get_identifier ().as_string ();
+ bool starts_with_under_score = var_name.compare (0, 1, "_") == 0;
+ auto id = item.get_mappings ().get_hirid ();
+ if (!unused_var_context.is_variable_used (id) && !starts_with_under_score)
+ rust_warning_at (item.get_locus (), OPT_Wunused_variable,
+ "unused name '%s'",
+ item.get_identifier ().as_string ().c_str ());
+}
+
+void
+UnusedVarChecker::visit (HIR::TraitItemFunc &item)
+{
+ // TODO: check trait item functions if they are not derived.
+}
+
+void
+UnusedVarChecker::check_variable (HIR::Pattern &pattern)
+{
+ switch (pattern.get_pattern_type ())
+ {
+ case HIR::Pattern::PatternType::IDENTIFIER:
+ check_variable_identifier (
+ static_cast (pattern));
+ break;
+ case HIR::Pattern::PatternType::TUPLE:
+ check_variable_tuple (static_cast (pattern));
+ break;
+ default:
+ break;
+ }
+}
+void
+UnusedVarChecker::check_variable_identifier (HIR::IdentifierPattern &pattern)
+{
+ std::string var_name = pattern.get_identifier ().as_string ();
+ bool starts_with_under_score = var_name.compare (0, 1, "_") == 0;
+ auto id = pattern.get_mappings ().get_hirid ();
+ if (!unused_var_context.is_variable_used (id) && var_name != "self"
+ && !starts_with_under_score)
+ rust_warning_at (pattern.get_locus (), OPT_Wunused_variable,
+ "unused name '%s'",
+ pattern.get_identifier ().as_string ().c_str ());
+}
+void
+UnusedVarChecker::check_variable_tuple (HIR::TuplePattern &pattern)
+{
+ switch (pattern.get_items ().get_item_type ())
+ {
+ case HIR::TuplePatternItems::ItemType::NO_REST:
+ {
+ auto items
+ = static_cast (pattern.get_items ());
+ for (auto &item : items.get_patterns ())
+ check_variable (*item);
+ }
+ break;
+ default:
+ break;
+ }
+}
+} // namespace Analysis
+} // namespace Rust
\ No newline at end of file
diff --git a/gcc/rust/checks/lints/unused-var/rust-unused-var-checker.h b/gcc/rust/checks/lints/unused-var/rust-unused-var-checker.h
new file mode 100644
index 00000000000..130313c75cc
--- /dev/null
+++ b/gcc/rust/checks/lints/unused-var/rust-unused-var-checker.h
@@ -0,0 +1,48 @@
+// Copyright (C) 2025 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC 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 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3. If not see
+// .
+
+#include "rust-hir-item.h"
+#include "rust-hir-visitor.h"
+#include "rust-immutable-name-resolution-context.h"
+#include "rust-unused-var-collector.h"
+
+namespace Rust {
+namespace Analysis {
+class UnusedVarChecker : public HIR::DefaultHIRVisitor
+{
+public:
+ UnusedVarChecker ();
+ void go (HIR::Crate &crate);
+
+private:
+ const Resolver2_0::NameResolutionContext &nr_context;
+ Analysis::Mappings &mappings;
+ UnusedVarContext &unused_var_context;
+
+ using HIR::DefaultHIRVisitor::visit;
+ virtual void visit (HIR::LetStmt &stmt) override;
+ virtual void visit_function_param (HIR::FunctionParam ¶m) override;
+ virtual void visit (HIR::TraitItemFunc &decl) override;
+ virtual void visit (HIR::ConstantItem &item) override;
+ virtual void visit (HIR::StaticItem &item) override;
+ void check_variable_identifier (HIR::IdentifierPattern &identifier);
+ void check_variable_tuple (HIR::TuplePattern &pattern);
+ void check_variable (HIR::Pattern &pattern);
+};
+} // namespace Analysis
+} // namespace Rust
\ No newline at end of file
diff --git a/gcc/rust/checks/lints/unused-var/rust-unused-var-collector.cc b/gcc/rust/checks/lints/unused-var/rust-unused-var-collector.cc
new file mode 100644
index 00000000000..16fa02f4d61
--- /dev/null
+++ b/gcc/rust/checks/lints/unused-var/rust-unused-var-collector.cc
@@ -0,0 +1,130 @@
+// Copyright (C) 2025 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC 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 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3. If not see
+// .
+
+#include "rust-unused-var-collector.h"
+#include "rust-hir-full-decls.h"
+#include "rust-hir-item.h"
+#include "rust-hir-path.h"
+#include "rust-immutable-name-resolution-context.h"
+
+namespace Rust {
+namespace Analysis {
+UnusedVarCollector::UnusedVarCollector ()
+ : nr_context (
+ Resolver2_0::ImmutableNameResolutionContext::get ().resolver ()),
+ mappings (Analysis::Mappings::get ()),
+ unused_var_context (*UnusedVarContext::get ())
+{}
+void
+UnusedVarCollector::go (HIR::Crate &crate)
+{
+ for (auto &item : crate.get_items ())
+ item->accept_vis (*this);
+}
+void
+UnusedVarCollector::visit (HIR::LetStmt &stmt)
+{
+ HIR::Pattern &pattern = stmt.get_pattern ();
+ collect_variable (pattern);
+ walk (stmt);
+}
+
+void
+UnusedVarCollector::visit (HIR::ConstantItem &item)
+{
+ unused_var_context.add_variable (item.get_mappings ().get_hirid ());
+ walk (item);
+}
+
+void
+UnusedVarCollector::visit (HIR::StaticItem &item)
+{
+ unused_var_context.add_variable (item.get_mappings ().get_hirid ());
+ walk (item);
+}
+
+void
+UnusedVarCollector::visit_function_param (HIR::FunctionParam ¶m)
+{
+ collect_variable (param.get_param_name ());
+}
+
+void
+UnusedVarCollector::visit_closure_param (HIR::ClosureParam ¶m)
+{
+ collect_variable (param.get_pattern ());
+}
+
+void
+UnusedVarCollector::collect_variable (HIR::Pattern &pattern)
+{
+ switch (pattern.get_pattern_type ())
+ {
+ case HIR::Pattern::PatternType::IDENTIFIER:
+ {
+ auto &identifier = static_cast (pattern);
+ auto id = identifier.get_mappings ().get_hirid ();
+ unused_var_context.add_variable (id);
+ }
+ break;
+ case HIR::Pattern::PatternType::TUPLE:
+ {
+ collect_variable_tuple (static_cast (pattern));
+ }
+ break;
+ default:
+ break;
+ }
+}
+void
+UnusedVarCollector::collect_variable_tuple (HIR::TuplePattern &pattern)
+{
+ switch (pattern.get_items ().get_item_type ())
+ {
+ case HIR::TuplePatternItems::ItemType::NO_REST:
+ {
+ auto &items
+ = static_cast (pattern.get_items ());
+ for (auto &sub : items.get_patterns ())
+ collect_variable (*sub);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void
+UnusedVarCollector::visit (HIR::PathInExpression &expr)
+{
+ mark_path_used (expr);
+}
+
+void
+UnusedVarCollector::visit (HIR::QualifiedPathInExpression &expr)
+{
+ mark_path_used (expr);
+}
+
+void
+UnusedVarCollector::visit (HIR::StructExprFieldIdentifier &ident)
+{
+ mark_path_used (ident);
+}
+} // namespace Analysis
+} // namespace Rust
diff --git a/gcc/rust/checks/lints/unused-var/rust-unused-var-collector.h b/gcc/rust/checks/lints/unused-var/rust-unused-var-collector.h
new file mode 100644
index 00000000000..ce1f5d1e7aa
--- /dev/null
+++ b/gcc/rust/checks/lints/unused-var/rust-unused-var-collector.h
@@ -0,0 +1,71 @@
+// Copyright (C) 2025 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC 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 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3. If not see
+// .
+
+#include "rust-hir-expr.h"
+#include "rust-hir-item.h"
+#include "rust-hir-path.h"
+#include "rust-hir-visitor.h"
+#include "rust-name-resolution-context.h"
+#include "rust-name-resolver.h"
+#include "rust-unused-var-context.h"
+
+namespace Rust {
+namespace Analysis {
+class UnusedVarCollector : public HIR::DefaultHIRVisitor
+{
+public:
+ UnusedVarCollector ();
+ void go (HIR::Crate &crate);
+
+private:
+ const Resolver2_0::NameResolutionContext &nr_context;
+ Analysis::Mappings &mappings;
+ UnusedVarContext &unused_var_context;
+
+ using HIR::DefaultHIRVisitor::visit;
+ virtual void visit (HIR::LetStmt &stmt) override;
+ virtual void visit (HIR::PathInExpression &expr) override;
+ virtual void visit (HIR::StructExprFieldIdentifier &ident) override;
+ virtual void visit (HIR::ConstantItem &item) override;
+ virtual void visit (HIR::StaticItem &item) override;
+ virtual void visit (HIR::QualifiedPathInExpression &expr) override;
+ virtual void visit_function_param (HIR::FunctionParam ¶m) override;
+ virtual void visit_closure_param (HIR::ClosureParam ¶m) override;
+
+ void collect_variable (HIR::Pattern &pattern);
+ void collect_variable_tuple (HIR::TuplePattern &pattern);
+
+ template void mark_path_used (T &path_expr)
+ {
+ NodeId ast_node_id = path_expr.get_mappings ().get_nodeid ();
+ NodeId def_id;
+
+ if (auto id = nr_context.lookup (ast_node_id))
+ def_id = *id;
+ else
+ return;
+
+ auto hir_id = mappings.lookup_node_to_hir (def_id);
+ if (!hir_id)
+ return;
+
+ unused_var_context.mark_used (hir_id.value ());
+ }
+};
+} // namespace Analysis
+} // namespace Rust
\ No newline at end of file
diff --git a/gcc/rust/checks/lints/unused-var/rust-unused-var-context.cc b/gcc/rust/checks/lints/unused-var/rust-unused-var-context.cc
new file mode 100644
index 00000000000..2271aeeee06
--- /dev/null
+++ b/gcc/rust/checks/lints/unused-var/rust-unused-var-context.cc
@@ -0,0 +1,69 @@
+// Copyright (C) 2025 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC 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 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3. If not see
+// .
+
+#include "rust-unused-var-context.h"
+
+namespace Rust {
+namespace Analysis {
+
+UnusedVarContext *
+UnusedVarContext::get ()
+{
+ static UnusedVarContext *instance;
+ if (instance == nullptr)
+ instance = new UnusedVarContext ();
+ return instance;
+}
+
+UnusedVarContext::~UnusedVarContext () {}
+
+void
+UnusedVarContext::add_variable (HirId id)
+{
+ if (is_used.find (id) == is_used.end ())
+ is_used.insert ({id, false});
+}
+
+void
+UnusedVarContext::mark_used (HirId id)
+{
+ is_used[id] = true;
+}
+
+bool
+UnusedVarContext::is_variable_used (HirId id) const
+{
+ auto it = is_used.find (id);
+ return it != is_used.end () && it->second;
+}
+
+std::string
+UnusedVarContext::as_string () const
+{
+ std::stringstream ss;
+ ss << "UnusedVarContext: ";
+ for (const auto &pair : is_used)
+ {
+ ss << "HirId: " << pair.first << " Used: " << (pair.second ? "Yes" : "No")
+ << "\n";
+ }
+ return ss.str ();
+}
+
+} // namespace Analysis
+} // namespace Rust
diff --git a/gcc/rust/checks/lints/unused-var/rust-unused-var-context.h b/gcc/rust/checks/lints/unused-var/rust-unused-var-context.h
new file mode 100644
index 00000000000..2672923b07a
--- /dev/null
+++ b/gcc/rust/checks/lints/unused-var/rust-unused-var-context.h
@@ -0,0 +1,41 @@
+// Copyright (C) 2025 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC 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 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3. If not see
+// .
+
+#include "rust-mapping-common.h"
+
+namespace Rust {
+namespace Analysis {
+class UnusedVarContext
+{
+public:
+ static UnusedVarContext *get ();
+ ~UnusedVarContext ();
+
+ void add_variable (HirId id);
+ void mark_used (HirId id);
+
+ bool is_variable_used (HirId id) const;
+
+ std::string as_string () const;
+
+private:
+ UnusedVarContext () = default;
+ std::map is_used;
+};
+} // namespace Analysis
+} // namespace Rust
From d80890678c0df866e537475d2dc3476f7016c3f1 Mon Sep 17 00:00:00 2001
From: Ryutaro Okada <1015ryu88@gmail.com>
Date: Mon, 25 Aug 2025 05:29:50 -0700
Subject: [PATCH 2/5] Manage UnusedVariable without using a global variable
---
.../lints/unused-var/rust-unused-var-checker.cc | 11 ++++++-----
.../checks/lints/unused-var/rust-unused-var-checker.h | 2 +-
.../lints/unused-var/rust-unused-var-collector.cc | 5 ++---
.../lints/unused-var/rust-unused-var-collector.h | 4 ++--
.../lints/unused-var/rust-unused-var-context.cc | 11 -----------
.../checks/lints/unused-var/rust-unused-var-context.h | 4 ----
6 files changed, 11 insertions(+), 26 deletions(-)
diff --git a/gcc/rust/checks/lints/unused-var/rust-unused-var-checker.cc b/gcc/rust/checks/lints/unused-var/rust-unused-var-checker.cc
index d828bbc5ca1..041048c676d 100644
--- a/gcc/rust/checks/lints/unused-var/rust-unused-var-checker.cc
+++ b/gcc/rust/checks/lints/unused-var/rust-unused-var-checker.cc
@@ -27,12 +27,13 @@ UnusedVarChecker::UnusedVarChecker ()
: nr_context (
Resolver2_0::ImmutableNameResolutionContext::get ().resolver ()),
mappings (Analysis::Mappings::get ()),
- unused_var_context (*UnusedVarContext::get ())
+ unused_var_context (std::make_unique ())
{}
void
UnusedVarChecker::go (HIR::Crate &crate)
{
- UnusedVarCollector ().go (crate);
+ UnusedVarCollector collector (*unused_var_context);
+ collector.go (crate);
for (auto &item : crate.get_items ())
item->accept_vis (*this);
}
@@ -55,7 +56,7 @@ UnusedVarChecker::visit (HIR::ConstantItem &item)
std::string var_name = item.get_identifier ().as_string ();
bool starts_with_under_score = var_name.compare (0, 1, "_") == 0;
auto id = item.get_mappings ().get_hirid ();
- if (!unused_var_context.is_variable_used (id) && !starts_with_under_score)
+ if (!unused_var_context->is_variable_used (id) && !starts_with_under_score)
rust_warning_at (item.get_locus (), OPT_Wunused_variable,
"unused name '%s'",
item.get_identifier ().as_string ().c_str ());
@@ -67,7 +68,7 @@ UnusedVarChecker::visit (HIR::StaticItem &item)
std::string var_name = item.get_identifier ().as_string ();
bool starts_with_under_score = var_name.compare (0, 1, "_") == 0;
auto id = item.get_mappings ().get_hirid ();
- if (!unused_var_context.is_variable_used (id) && !starts_with_under_score)
+ if (!unused_var_context->is_variable_used (id) && !starts_with_under_score)
rust_warning_at (item.get_locus (), OPT_Wunused_variable,
"unused name '%s'",
item.get_identifier ().as_string ().c_str ());
@@ -101,7 +102,7 @@ UnusedVarChecker::check_variable_identifier (HIR::IdentifierPattern &pattern)
std::string var_name = pattern.get_identifier ().as_string ();
bool starts_with_under_score = var_name.compare (0, 1, "_") == 0;
auto id = pattern.get_mappings ().get_hirid ();
- if (!unused_var_context.is_variable_used (id) && var_name != "self"
+ if (!unused_var_context->is_variable_used (id) && var_name != "self"
&& !starts_with_under_score)
rust_warning_at (pattern.get_locus (), OPT_Wunused_variable,
"unused name '%s'",
diff --git a/gcc/rust/checks/lints/unused-var/rust-unused-var-checker.h b/gcc/rust/checks/lints/unused-var/rust-unused-var-checker.h
index 130313c75cc..273bc6a0179 100644
--- a/gcc/rust/checks/lints/unused-var/rust-unused-var-checker.h
+++ b/gcc/rust/checks/lints/unused-var/rust-unused-var-checker.h
@@ -32,7 +32,7 @@ class UnusedVarChecker : public HIR::DefaultHIRVisitor
private:
const Resolver2_0::NameResolutionContext &nr_context;
Analysis::Mappings &mappings;
- UnusedVarContext &unused_var_context;
+ std::unique_ptr unused_var_context;
using HIR::DefaultHIRVisitor::visit;
virtual void visit (HIR::LetStmt &stmt) override;
diff --git a/gcc/rust/checks/lints/unused-var/rust-unused-var-collector.cc b/gcc/rust/checks/lints/unused-var/rust-unused-var-collector.cc
index 16fa02f4d61..8834ecd5965 100644
--- a/gcc/rust/checks/lints/unused-var/rust-unused-var-collector.cc
+++ b/gcc/rust/checks/lints/unused-var/rust-unused-var-collector.cc
@@ -24,11 +24,10 @@
namespace Rust {
namespace Analysis {
-UnusedVarCollector::UnusedVarCollector ()
+UnusedVarCollector::UnusedVarCollector (UnusedVarContext &context)
: nr_context (
Resolver2_0::ImmutableNameResolutionContext::get ().resolver ()),
- mappings (Analysis::Mappings::get ()),
- unused_var_context (*UnusedVarContext::get ())
+ mappings (Analysis::Mappings::get ()), unused_var_context (context)
{}
void
UnusedVarCollector::go (HIR::Crate &crate)
diff --git a/gcc/rust/checks/lints/unused-var/rust-unused-var-collector.h b/gcc/rust/checks/lints/unused-var/rust-unused-var-collector.h
index ce1f5d1e7aa..c317240197e 100644
--- a/gcc/rust/checks/lints/unused-var/rust-unused-var-collector.h
+++ b/gcc/rust/checks/lints/unused-var/rust-unused-var-collector.h
@@ -21,15 +21,15 @@
#include "rust-hir-path.h"
#include "rust-hir-visitor.h"
#include "rust-name-resolution-context.h"
-#include "rust-name-resolver.h"
#include "rust-unused-var-context.h"
+#include "rust-name-resolver.h"
namespace Rust {
namespace Analysis {
class UnusedVarCollector : public HIR::DefaultHIRVisitor
{
public:
- UnusedVarCollector ();
+ UnusedVarCollector (UnusedVarContext &context);
void go (HIR::Crate &crate);
private:
diff --git a/gcc/rust/checks/lints/unused-var/rust-unused-var-context.cc b/gcc/rust/checks/lints/unused-var/rust-unused-var-context.cc
index 2271aeeee06..728d61d217d 100644
--- a/gcc/rust/checks/lints/unused-var/rust-unused-var-context.cc
+++ b/gcc/rust/checks/lints/unused-var/rust-unused-var-context.cc
@@ -21,17 +21,6 @@
namespace Rust {
namespace Analysis {
-UnusedVarContext *
-UnusedVarContext::get ()
-{
- static UnusedVarContext *instance;
- if (instance == nullptr)
- instance = new UnusedVarContext ();
- return instance;
-}
-
-UnusedVarContext::~UnusedVarContext () {}
-
void
UnusedVarContext::add_variable (HirId id)
{
diff --git a/gcc/rust/checks/lints/unused-var/rust-unused-var-context.h b/gcc/rust/checks/lints/unused-var/rust-unused-var-context.h
index 2672923b07a..62392c63e6f 100644
--- a/gcc/rust/checks/lints/unused-var/rust-unused-var-context.h
+++ b/gcc/rust/checks/lints/unused-var/rust-unused-var-context.h
@@ -23,9 +23,6 @@ namespace Analysis {
class UnusedVarContext
{
public:
- static UnusedVarContext *get ();
- ~UnusedVarContext ();
-
void add_variable (HirId id);
void mark_used (HirId id);
@@ -34,7 +31,6 @@ class UnusedVarContext
std::string as_string () const;
private:
- UnusedVarContext () = default;
std::map is_used;
};
} // namespace Analysis
From 6c33ae083075e6859bf99b7bd5aca785ec9de10c Mon Sep 17 00:00:00 2001
From: Ryutaro Okada <1015ryu88@gmail.com>
Date: Mon, 25 Aug 2025 05:50:20 -0700
Subject: [PATCH 3/5] Use visit function instead of manually checking variables
---
.../unused-var/rust-unused-var-checker.cc | 49 +---------------
.../unused-var/rust-unused-var-checker.h | 7 +--
.../unused-var/rust-unused-var-collector.cc | 58 ++-----------------
.../unused-var/rust-unused-var-collector.h | 8 +--
4 files changed, 9 insertions(+), 113 deletions(-)
diff --git a/gcc/rust/checks/lints/unused-var/rust-unused-var-checker.cc b/gcc/rust/checks/lints/unused-var/rust-unused-var-checker.cc
index 041048c676d..c6cfd5bb2da 100644
--- a/gcc/rust/checks/lints/unused-var/rust-unused-var-checker.cc
+++ b/gcc/rust/checks/lints/unused-var/rust-unused-var-checker.cc
@@ -37,19 +37,6 @@ UnusedVarChecker::go (HIR::Crate &crate)
for (auto &item : crate.get_items ())
item->accept_vis (*this);
}
-void
-UnusedVarChecker::visit (HIR::LetStmt &stmt)
-{
- HIR::Pattern &pattern = stmt.get_pattern ();
- check_variable (pattern);
- walk (stmt);
-}
-void
-UnusedVarChecker::visit_function_param (HIR::FunctionParam ¶m)
-{
- check_variable (param.get_param_name ());
-}
-
void
UnusedVarChecker::visit (HIR::ConstantItem &item)
{
@@ -79,25 +66,8 @@ UnusedVarChecker::visit (HIR::TraitItemFunc &item)
{
// TODO: check trait item functions if they are not derived.
}
-
-void
-UnusedVarChecker::check_variable (HIR::Pattern &pattern)
-{
- switch (pattern.get_pattern_type ())
- {
- case HIR::Pattern::PatternType::IDENTIFIER:
- check_variable_identifier (
- static_cast (pattern));
- break;
- case HIR::Pattern::PatternType::TUPLE:
- check_variable_tuple (static_cast (pattern));
- break;
- default:
- break;
- }
-}
void
-UnusedVarChecker::check_variable_identifier (HIR::IdentifierPattern &pattern)
+UnusedVarChecker::visit (HIR::IdentifierPattern &pattern)
{
std::string var_name = pattern.get_identifier ().as_string ();
bool starts_with_under_score = var_name.compare (0, 1, "_") == 0;
@@ -108,22 +78,5 @@ UnusedVarChecker::check_variable_identifier (HIR::IdentifierPattern &pattern)
"unused name '%s'",
pattern.get_identifier ().as_string ().c_str ());
}
-void
-UnusedVarChecker::check_variable_tuple (HIR::TuplePattern &pattern)
-{
- switch (pattern.get_items ().get_item_type ())
- {
- case HIR::TuplePatternItems::ItemType::NO_REST:
- {
- auto items
- = static_cast (pattern.get_items ());
- for (auto &item : items.get_patterns ())
- check_variable (*item);
- }
- break;
- default:
- break;
- }
-}
} // namespace Analysis
} // namespace Rust
\ No newline at end of file
diff --git a/gcc/rust/checks/lints/unused-var/rust-unused-var-checker.h b/gcc/rust/checks/lints/unused-var/rust-unused-var-checker.h
index 273bc6a0179..d916caa2d0a 100644
--- a/gcc/rust/checks/lints/unused-var/rust-unused-var-checker.h
+++ b/gcc/rust/checks/lints/unused-var/rust-unused-var-checker.h
@@ -17,6 +17,7 @@
// .
#include "rust-hir-item.h"
+#include "rust-hir-pattern.h"
#include "rust-hir-visitor.h"
#include "rust-immutable-name-resolution-context.h"
#include "rust-unused-var-collector.h"
@@ -35,14 +36,10 @@ class UnusedVarChecker : public HIR::DefaultHIRVisitor
std::unique_ptr unused_var_context;
using HIR::DefaultHIRVisitor::visit;
- virtual void visit (HIR::LetStmt &stmt) override;
- virtual void visit_function_param (HIR::FunctionParam ¶m) override;
virtual void visit (HIR::TraitItemFunc &decl) override;
virtual void visit (HIR::ConstantItem &item) override;
virtual void visit (HIR::StaticItem &item) override;
- void check_variable_identifier (HIR::IdentifierPattern &identifier);
- void check_variable_tuple (HIR::TuplePattern &pattern);
- void check_variable (HIR::Pattern &pattern);
+ virtual void visit (HIR::IdentifierPattern &identifier) override;
};
} // namespace Analysis
} // namespace Rust
\ No newline at end of file
diff --git a/gcc/rust/checks/lints/unused-var/rust-unused-var-collector.cc b/gcc/rust/checks/lints/unused-var/rust-unused-var-collector.cc
index 8834ecd5965..deeabdef550 100644
--- a/gcc/rust/checks/lints/unused-var/rust-unused-var-collector.cc
+++ b/gcc/rust/checks/lints/unused-var/rust-unused-var-collector.cc
@@ -20,6 +20,7 @@
#include "rust-hir-full-decls.h"
#include "rust-hir-item.h"
#include "rust-hir-path.h"
+#include "rust-hir-pattern.h"
#include "rust-immutable-name-resolution-context.h"
namespace Rust {
@@ -35,13 +36,6 @@ UnusedVarCollector::go (HIR::Crate &crate)
for (auto &item : crate.get_items ())
item->accept_vis (*this);
}
-void
-UnusedVarCollector::visit (HIR::LetStmt &stmt)
-{
- HIR::Pattern &pattern = stmt.get_pattern ();
- collect_variable (pattern);
- walk (stmt);
-}
void
UnusedVarCollector::visit (HIR::ConstantItem &item)
@@ -58,54 +52,10 @@ UnusedVarCollector::visit (HIR::StaticItem &item)
}
void
-UnusedVarCollector::visit_function_param (HIR::FunctionParam ¶m)
-{
- collect_variable (param.get_param_name ());
-}
-
-void
-UnusedVarCollector::visit_closure_param (HIR::ClosureParam ¶m)
-{
- collect_variable (param.get_pattern ());
-}
-
-void
-UnusedVarCollector::collect_variable (HIR::Pattern &pattern)
-{
- switch (pattern.get_pattern_type ())
- {
- case HIR::Pattern::PatternType::IDENTIFIER:
- {
- auto &identifier = static_cast (pattern);
- auto id = identifier.get_mappings ().get_hirid ();
- unused_var_context.add_variable (id);
- }
- break;
- case HIR::Pattern::PatternType::TUPLE:
- {
- collect_variable_tuple (static_cast (pattern));
- }
- break;
- default:
- break;
- }
-}
-void
-UnusedVarCollector::collect_variable_tuple (HIR::TuplePattern &pattern)
+UnusedVarCollector::visit (HIR::IdentifierPattern &pattern)
{
- switch (pattern.get_items ().get_item_type ())
- {
- case HIR::TuplePatternItems::ItemType::NO_REST:
- {
- auto &items
- = static_cast (pattern.get_items ());
- for (auto &sub : items.get_patterns ())
- collect_variable (*sub);
- }
- break;
- default:
- break;
- }
+ auto id = pattern.get_mappings ().get_hirid ();
+ unused_var_context.add_variable (id);
}
void
diff --git a/gcc/rust/checks/lints/unused-var/rust-unused-var-collector.h b/gcc/rust/checks/lints/unused-var/rust-unused-var-collector.h
index c317240197e..8b358a91ded 100644
--- a/gcc/rust/checks/lints/unused-var/rust-unused-var-collector.h
+++ b/gcc/rust/checks/lints/unused-var/rust-unused-var-collector.h
@@ -19,6 +19,7 @@
#include "rust-hir-expr.h"
#include "rust-hir-item.h"
#include "rust-hir-path.h"
+#include "rust-hir-pattern.h"
#include "rust-hir-visitor.h"
#include "rust-name-resolution-context.h"
#include "rust-unused-var-context.h"
@@ -38,17 +39,12 @@ class UnusedVarCollector : public HIR::DefaultHIRVisitor
UnusedVarContext &unused_var_context;
using HIR::DefaultHIRVisitor::visit;
- virtual void visit (HIR::LetStmt &stmt) override;
virtual void visit (HIR::PathInExpression &expr) override;
virtual void visit (HIR::StructExprFieldIdentifier &ident) override;
virtual void visit (HIR::ConstantItem &item) override;
virtual void visit (HIR::StaticItem &item) override;
+ virtual void visit (HIR::IdentifierPattern &pattern) override;
virtual void visit (HIR::QualifiedPathInExpression &expr) override;
- virtual void visit_function_param (HIR::FunctionParam ¶m) override;
- virtual void visit_closure_param (HIR::ClosureParam ¶m) override;
-
- void collect_variable (HIR::Pattern &pattern);
- void collect_variable_tuple (HIR::TuplePattern &pattern);
template void mark_path_used (T &path_expr)
{
From 5a36d458c661c857c50a458c2ee0a29428b3074f Mon Sep 17 00:00:00 2001
From: Ryutaro Okada <1015ryu88@gmail.com>
Date: Mon, 25 Aug 2025 05:51:47 -0700
Subject: [PATCH 4/5] Add flag to enable new unused variable checker
---
gcc/rust/lang.opt | 4 ++++
gcc/rust/rust-session-manager.cc | 8 +++++++-
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/gcc/rust/lang.opt b/gcc/rust/lang.opt
index d9824f1a5ac..67a2ae075c9 100644
--- a/gcc/rust/lang.opt
+++ b/gcc/rust/lang.opt
@@ -233,4 +233,8 @@ frust-assume-builtin-offset-of
Rust Var(flag_assume_builtin_offset_of)
Define a built-in offset_of macro in the compiler and assume it is present
+frust-unused-check-2.0
+Rust Var(flag_unused_check_2_0)
+Use the new unused variable check instead of old one
+
; This comment is to ensure we retain the blank line above.
diff --git a/gcc/rust/rust-session-manager.cc b/gcc/rust/rust-session-manager.cc
index c88f4675753..b6bd1462732 100644
--- a/gcc/rust/rust-session-manager.cc
+++ b/gcc/rust/rust-session-manager.cc
@@ -39,6 +39,7 @@
#include "rust-cfg-parser.h"
#include "rust-lint-scan-deadcode.h"
#include "rust-lint-unused-var.h"
+#include "rust-unused-var-checker.h"
#include "rust-readonly-check.h"
#include "rust-hir-dump.h"
#include "rust-ast-dump.h"
@@ -736,7 +737,12 @@ Session::compile_crate (const char *filename)
{
// lints
Analysis::ScanDeadcode::Scan (hir);
- Analysis::UnusedVariables::Lint (*ctx);
+
+ if (flag_unused_check_2_0)
+ Analysis::UnusedVarChecker ().go (hir);
+ else
+ Analysis::UnusedVariables::Lint (*ctx);
+
Analysis::ReadonlyCheck::Lint (*ctx);
// metadata
From 32b918d46088b8b2633a66fceebe28baa6728078 Mon Sep 17 00:00:00 2001
From: Ryutaro Okada <1015ryu88@gmail.com>
Date: Mon, 25 Aug 2025 05:52:05 -0700
Subject: [PATCH 5/5] Add tests for new unused variable check
---
.../rust/unused-var/static_item_0.rs | 2 ++
.../rust/unused-var/template_function_0.rs | 6 ++++
gcc/testsuite/rust/unused-var/unused_var.exp | 33 +++++++++++++++++++
3 files changed, 41 insertions(+)
create mode 100644 gcc/testsuite/rust/unused-var/static_item_0.rs
create mode 100644 gcc/testsuite/rust/unused-var/template_function_0.rs
create mode 100644 gcc/testsuite/rust/unused-var/unused_var.exp
diff --git a/gcc/testsuite/rust/unused-var/static_item_0.rs b/gcc/testsuite/rust/unused-var/static_item_0.rs
new file mode 100644
index 00000000000..741d887c609
--- /dev/null
+++ b/gcc/testsuite/rust/unused-var/static_item_0.rs
@@ -0,0 +1,2 @@
+static TEST: usize = 1;
+// { dg-warning "unused name" "" { target *-*-* } .-1 }
\ No newline at end of file
diff --git a/gcc/testsuite/rust/unused-var/template_function_0.rs b/gcc/testsuite/rust/unused-var/template_function_0.rs
new file mode 100644
index 00000000000..8a091262d0d
--- /dev/null
+++ b/gcc/testsuite/rust/unused-var/template_function_0.rs
@@ -0,0 +1,6 @@
+#[lang = "sized"]
+pub trait Sized {}
+
+pub fn test (a: usize) -> () {
+ // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
\ No newline at end of file
diff --git a/gcc/testsuite/rust/unused-var/unused_var.exp b/gcc/testsuite/rust/unused-var/unused_var.exp
new file mode 100644
index 00000000000..e586d0f7425
--- /dev/null
+++ b/gcc/testsuite/rust/unused-var/unused_var.exp
@@ -0,0 +1,33 @@
+# Copyright (C) 2025 Free Software Foundation, Inc.
+
+# 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 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3. If not see
+# .
+
+# Run compile tests with unused var check 2.0 enabled
+
+# Load support procs.
+load_lib rust-dg.exp
+
+# Initialize `dg'.
+dg-init
+
+# Main loop.
+set saved-dg-do-what-default ${dg-do-what-default}
+
+set dg-do-what-default "compile"
+dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.rs]] "-frust-unused-check-2.0" ""
+set dg-do-what-default ${saved-dg-do-what-default}
+
+# All done.
+dg-finish
\ No newline at end of file