https://gcc.gnu.org/g:e9e13f3895aeaaf7c388b51d471eb5b908a5e556
commit r16-6359-ge9e13f3895aeaaf7c388b51d471eb5b908a5e556 Author: Lucas Ly Ba <[email protected]> Date: Fri Nov 14 20:36:32 2025 +0000 gccrs: add unused-assignments lint gcc/rust/ChangeLog: * checks/lints/unused-var/rust-unused-var-checker.cc (UnusedVarChecker): Implement unused assignments warning. (UnusedVarChecker::go): Remove unique pointer unused var context. (UnusedVarChecker::visit): Visit AssignExpr in HIR default visitor. * checks/lints/unused-var/rust-unused-var-checker.h: Add visit method. * checks/lints/unused-var/rust-unused-var-collector.cc (UnusedVarCollector): Collect warnings for assignments. (UnusedVarCollector::visit): Visit AssignExpr in HIR default visitor. * checks/lints/unused-var/rust-unused-var-collector.h: Add visit method. * checks/lints/unused-var/rust-unused-var-context.cc (UnusedVarContext::add_assign): Add assignment in map. (UnusedVarContext::remove_assign): Remove assignment in map. (UnusedVarContext::is_variable_assigned): Check if a variable is assigned. * checks/lints/unused-var/rust-unused-var-context.h: Add a map to stock assignments. gcc/testsuite/ChangeLog: * rust/compile/issue-4260_0.rs: New test. Signed-off-by: Lucas Ly Ba <[email protected]> Diff: --- .../lints/unused-var/rust-unused-var-checker.cc | 37 +++++++++++++++------- .../lints/unused-var/rust-unused-var-checker.h | 6 ++-- .../lints/unused-var/rust-unused-var-collector.cc | 15 +++++++-- .../lints/unused-var/rust-unused-var-collector.h | 18 ++++++++--- .../lints/unused-var/rust-unused-var-context.cc | 20 ++++++++++++ .../lints/unused-var/rust-unused-var-context.h | 6 +++- gcc/testsuite/rust/compile/issue-4260_0.rs | 22 +++++++++++++ 7 files changed, 103 insertions(+), 21 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 c6cfd5bb2da6..e4df446f783e 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 @@ -17,6 +17,7 @@ // <http://www.gnu.org/licenses/>. #include "rust-unused-var-checker.h" +#include "rust-hir-expr.h" #include "rust-hir-item.h" #include "options.h" @@ -27,12 +28,12 @@ UnusedVarChecker::UnusedVarChecker () : nr_context ( Resolver2_0::ImmutableNameResolutionContext::get ().resolver ()), mappings (Analysis::Mappings::get ()), - unused_var_context (std::make_unique<UnusedVarContext> ()) + unused_var_context (UnusedVarContext ()) {} void UnusedVarChecker::go (HIR::Crate &crate) { - UnusedVarCollector collector (*unused_var_context); + UnusedVarCollector collector (unused_var_context); collector.go (crate); for (auto &item : crate.get_items ()) item->accept_vis (*this); @@ -43,9 +44,8 @@ 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'", + if (!unused_var_context.is_variable_used (id) && !starts_with_under_score) + rust_warning_at (item.get_locus (), OPT_Wunused_variable, "unused name %qs", item.get_identifier ().as_string ().c_str ()); } @@ -55,9 +55,8 @@ 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'", + if (!unused_var_context.is_variable_used (id) && !starts_with_under_score) + rust_warning_at (item.get_locus (), OPT_Wunused_variable, "unused name %qs", item.get_identifier ().as_string ().c_str ()); } @@ -72,11 +71,27 @@ 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; 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'", + "unused name %qs", pattern.get_identifier ().as_string ().c_str ()); } +void + +UnusedVarChecker::visit (HIR::AssignmentExpr &expr) + +{ + const auto &lhs = expr.get_lhs (); + auto var_name = lhs.to_string (); + NodeId ast_node_id = lhs.get_mappings ().get_nodeid (); + NodeId def_id = nr_context.lookup (ast_node_id).value (); + HirId id = mappings.lookup_node_to_hir (def_id).value (); + if (unused_var_context.is_variable_assigned (id, + lhs.get_mappings ().get_hirid ()) + && !starts_with_under_score) + rust_warning_at (lhs.get_locus (), OPT_Wunused_variable, + "unused assignment %qs", var_name.c_str ()); +} } // namespace Analysis -} // namespace Rust \ No newline at end of file +} // namespace Rust 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 d916caa2d0ad..bb9c8ac85d64 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 @@ -16,6 +16,7 @@ // along with GCC; see the file COPYING3. If not see // <http://www.gnu.org/licenses/>. +#include "rust-hir-expr.h" #include "rust-hir-item.h" #include "rust-hir-pattern.h" #include "rust-hir-visitor.h" @@ -33,13 +34,14 @@ public: private: const Resolver2_0::NameResolutionContext &nr_context; Analysis::Mappings &mappings; - std::unique_ptr<UnusedVarContext> unused_var_context; + UnusedVarContext unused_var_context; using HIR::DefaultHIRVisitor::visit; virtual void visit (HIR::TraitItemFunc &decl) override; virtual void visit (HIR::ConstantItem &item) override; virtual void visit (HIR::StaticItem &item) override; virtual void visit (HIR::IdentifierPattern &identifier) override; + virtual void visit (HIR::AssignmentExpr &identifier) override; }; } // namespace Analysis -} // namespace Rust \ No newline at end of file +} // namespace Rust 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 deeabdef5503..e9e98c5f2b68 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 @@ -17,11 +17,13 @@ // <http://www.gnu.org/licenses/>. #include "rust-unused-var-collector.h" +#include "rust-hir-expr.h" #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" +#include "tree-check.h" namespace Rust { namespace Analysis { @@ -54,8 +56,7 @@ UnusedVarCollector::visit (HIR::StaticItem &item) void UnusedVarCollector::visit (HIR::IdentifierPattern &pattern) { - auto id = pattern.get_mappings ().get_hirid (); - unused_var_context.add_variable (id); + unused_var_context.add_variable (pattern.get_mappings ().get_hirid ()); } void @@ -75,5 +76,15 @@ UnusedVarCollector::visit (HIR::StructExprFieldIdentifier &ident) { mark_path_used (ident); } +void +UnusedVarCollector::visit (HIR::AssignmentExpr &expr) +{ + auto def_id = get_def_id (expr.get_lhs ()); + HirId id = expr.get_lhs ().get_mappings ().get_hirid (); + unused_var_context.add_assign (def_id, id); + visit_outer_attrs (expr); + expr.get_rhs ().accept_vis (*this); +} + } // 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 index ed338405692f..e3fed0c29466 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 @@ -46,14 +46,22 @@ private: virtual void visit (HIR::StaticItem &item) override; virtual void visit (HIR::IdentifierPattern &pattern) override; virtual void visit (HIR::QualifiedPathInExpression &expr) override; + virtual void visit (HIR::AssignmentExpr &expr) override; - template <typename T> void mark_path_used (T &path_expr) + template <typename T> HirId get_def_id (T &path_expr) { NodeId ast_node_id = path_expr.get_mappings ().get_nodeid (); - NodeId def_id = nr_context.lookup (ast_node_id).value (); - HirId hir_id = mappings.lookup_node_to_hir (def_id).value (); - unused_var_context.mark_used (hir_id); + NodeId id = nr_context.lookup (ast_node_id).value (); + HirId def_id = mappings.lookup_node_to_hir (id).value (); + return def_id; + } + + template <typename T> void mark_path_used (T &path_expr) + { + auto def_id = get_def_id (path_expr); + unused_var_context.mark_used (def_id); + unused_var_context.remove_assign (def_id); } }; } // namespace Analysis -} // namespace Rust \ No newline at end of file +} // namespace Rust 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 728d61d217d2..435fba46a8f7 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 @@ -41,6 +41,26 @@ UnusedVarContext::is_variable_used (HirId id) const return it != is_used.end () && it->second; } +void +UnusedVarContext::add_assign (HirId id_def, HirId id) +{ + assigned_vars[id_def].push_back (id); +} + +void +UnusedVarContext::remove_assign (HirId id_def) +{ + if (assigned_vars.find (id_def) != assigned_vars.end ()) + assigned_vars[id_def].pop_back (); +} +bool +UnusedVarContext::is_variable_assigned (HirId id_def, HirId id) +{ + auto assigned_vec = assigned_vars[id_def]; + return std::find (assigned_vec.begin (), assigned_vec.end (), id) + != assigned_vec.end (); +} + std::string UnusedVarContext::as_string () const { 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 14f89da7810c..bde793defb8a 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 @@ -26,13 +26,17 @@ class UnusedVarContext public: void add_variable (HirId id); void mark_used (HirId id); - bool is_variable_used (HirId id) const; + void add_assign (HirId id_def, HirId id); + void remove_assign (HirId id_def); + bool is_variable_assigned (HirId id_def, HirId id); + std::string as_string () const; private: std::map<HirId, bool> is_used; + std::map<HirId, std::vector<HirId>> assigned_vars; }; } // namespace Analysis diff --git a/gcc/testsuite/rust/compile/issue-4260_0.rs b/gcc/testsuite/rust/compile/issue-4260_0.rs new file mode 100644 index 000000000000..b6f1fba3b310 --- /dev/null +++ b/gcc/testsuite/rust/compile/issue-4260_0.rs @@ -0,0 +1,22 @@ +// { dg-additional-options "-frust-unused-check-2.0" } +pub fn a()->i32 { + let mut a = 1; + a = 2; +// { dg-warning "unused assignment .a." "" { target *-*-* } .-1 } + a = 3; +// { dg-warning "unused assignment .a." "" { target *-*-* } .-1 } + a = 4; +// { dg-warning "unused assignment .a." "" { target *-*-* } .-1 } + a = 5; + let mut b = a; + b = 1; +// { dg-warning "unused assignment .b." "" { target *-*-* } .-1 } + b = 2; +// { dg-warning "unused assignment .b." "" { target *-*-* } .-1 } + b = 3; +// { dg-warning "unused assignment .b." "" { target *-*-* } .-1 } + b = 4; +// { dg-warning "unused assignment .b." "" { target *-*-* } .-1 } + b = 5; + return b +}
