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