From ab88635125684c8f8ae99e3f1cd67892c75e8a83 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] gccrs: implement unused variable checker on HIR. This change moves the unused variable checker from the type resolver to HIR. We can now use the HIR Default Visitor, and it will be much more easier to implement other unused lints with this change. gcc/rust/ChangeLog: * Make-lang.in: Add new files rules in Makefile. * lang.opt: Add new flag. * rust-session-manager.cc (Session::compile_crate): Execute new variable checker. * 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. gcc/testsuite/ChangeLog: * rust/compile/static_item_0.rs: New test. * rust/compile/template_function_0.rs: New test. Signed-off-by: Lucas Ly Ba --- gcc/rust/Make-lang.in | 9 ++ .../unused-var/rust-unused-var-checker.cc | 82 +++++++++++++++++++ .../unused-var/rust-unused-var-checker.h | 45 ++++++++++ .../unused-var/rust-unused-var-collector.cc | 79 ++++++++++++++++++ .../unused-var/rust-unused-var-collector.h | 59 +++++++++++++ .../unused-var/rust-unused-var-context.cc | 58 +++++++++++++ .../unused-var/rust-unused-var-context.h | 39 +++++++++ gcc/rust/lang.opt | 4 + gcc/rust/rust-session-manager.cc | 8 +- gcc/testsuite/rust/compile/static_item_0.rs | 3 + .../rust/compile/template_function_0.rs | 7 ++ 11 files changed, 392 insertions(+), 1 deletion(-) 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 create mode 100644 gcc/testsuite/rust/compile/static_item_0.rs create mode 100644 gcc/testsuite/rust/compile/template_function_0.rs diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in index e5a8a5eb462b..a79729bae057 100644 --- a/gcc/rust/Make-lang.in +++ b/gcc/rust/Make-lang.in @@ -200,6 +200,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-hir-type-check-path.o \ rust/rust-unsafe-checker.o \ @@ -432,6 +435,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/checks/errors/feature \ @@ -502,6 +506,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 000000000000..c6cfd5bb2da6 --- /dev/null +++ b/gcc/rust/checks/lints/unused-var/rust-unused-var-checker.cc @@ -0,0 +1,82 @@ +// 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 (std::make_unique ()) +{} +void +UnusedVarChecker::go (HIR::Crate &crate) +{ + UnusedVarCollector collector (*unused_var_context); + collector.go (crate); + for (auto &item : crate.get_items ()) + item->accept_vis (*this); +} +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::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" + && !starts_with_under_score) + rust_warning_at (pattern.get_locus (), OPT_Wunused_variable, + "unused name '%s'", + pattern.get_identifier ().as_string ().c_str ()); +} +} // 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 000000000000..d916caa2d0ad --- /dev/null +++ b/gcc/rust/checks/lints/unused-var/rust-unused-var-checker.h @@ -0,0 +1,45 @@ +// 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-pattern.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; + std::unique_ptr 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; +}; +} // 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 000000000000..deeabdef5503 --- /dev/null +++ b/gcc/rust/checks/lints/unused-var/rust-unused-var-collector.cc @@ -0,0 +1,79 @@ +// 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-hir-pattern.h" +#include "rust-immutable-name-resolution-context.h" + +namespace Rust { +namespace Analysis { +UnusedVarCollector::UnusedVarCollector (UnusedVarContext &context) + : nr_context ( + Resolver2_0::ImmutableNameResolutionContext::get ().resolver ()), + mappings (Analysis::Mappings::get ()), unused_var_context (context) +{} +void +UnusedVarCollector::go (HIR::Crate &crate) +{ + for (auto &item : crate.get_items ()) + item->accept_vis (*this); +} + +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 (HIR::IdentifierPattern &pattern) +{ + auto id = pattern.get_mappings ().get_hirid (); + unused_var_context.add_variable (id); +} + +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 000000000000..ed338405692f --- /dev/null +++ b/gcc/rust/checks/lints/unused-var/rust-unused-var-collector.h @@ -0,0 +1,59 @@ +// 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-pattern.h" +#include "rust-hir-visitor.h" +#include "rust-mapping-common.h" +#include "rust-name-resolution-context.h" +#include "rust-unused-var-context.h" +#include "rust-name-resolver.h" + +namespace Rust { +namespace Analysis { +class UnusedVarCollector : public HIR::DefaultHIRVisitor +{ +public: + UnusedVarCollector (UnusedVarContext &context); + 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::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; + + template void mark_path_used (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); + } +}; +} // 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 000000000000..728d61d217d2 --- /dev/null +++ b/gcc/rust/checks/lints/unused-var/rust-unused-var-context.cc @@ -0,0 +1,58 @@ +// 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 { + +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 000000000000..14f89da7810c --- /dev/null +++ b/gcc/rust/checks/lints/unused-var/rust-unused-var-context.h @@ -0,0 +1,39 @@ +// 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: + void add_variable (HirId id); + void mark_used (HirId id); + + bool is_variable_used (HirId id) const; + + std::string as_string () const; + +private: + std::map is_used; +}; + +} // namespace Analysis +} // namespace Rust diff --git a/gcc/rust/lang.opt b/gcc/rust/lang.opt index d9824f1a5ac2..67a2ae075c95 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 641811846832..8b496d00bf0e 100644 --- a/gcc/rust/rust-session-manager.cc +++ b/gcc/rust/rust-session-manager.cc @@ -38,6 +38,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" @@ -733,7 +734,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); + HIR::ReadonlyChecker ().go (hir); // metadata diff --git a/gcc/testsuite/rust/compile/static_item_0.rs b/gcc/testsuite/rust/compile/static_item_0.rs new file mode 100644 index 000000000000..69d8ec40927d --- /dev/null +++ b/gcc/testsuite/rust/compile/static_item_0.rs @@ -0,0 +1,3 @@ +// { dg-additional-options "-frust-unused-check-2.0" } +static TEST: usize = 1; +// { dg-warning "unused name" "" { target *-*-* } .-1 } \ No newline at end of file diff --git a/gcc/testsuite/rust/compile/template_function_0.rs b/gcc/testsuite/rust/compile/template_function_0.rs new file mode 100644 index 000000000000..92e3f21cd054 --- /dev/null +++ b/gcc/testsuite/rust/compile/template_function_0.rs @@ -0,0 +1,7 @@ +// { dg-additional-options "-frust-unused-check-2.0" } +#[lang = "sized"] +pub trait Sized {} + +pub fn test (a: usize) -> () { + // { dg-warning "unused name" "" { target *-*-* } .-1 } +} \ No newline at end of file From 4cf082b4bee255b5539128bdbbdca58473e9148f Mon Sep 17 00:00:00 2001 From: Lucas Ly Ba Date: Fri, 14 Nov 2025 20:36:32 +0000 Subject: [PATCH 2/5] 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 --- .../unused-var/rust-unused-var-checker.cc | 39 +++++++++++++------ .../unused-var/rust-unused-var-checker.h | 6 ++- .../unused-var/rust-unused-var-collector.cc | 15 ++++++- .../unused-var/rust-unused-var-collector.h | 18 ++++++--- .../unused-var/rust-unused-var-context.cc | 20 ++++++++++ .../unused-var/rust-unused-var-context.h | 6 ++- gcc/testsuite/rust/compile/issue-4260_0.rs | 22 +++++++++++ 7 files changed, 105 insertions(+), 21 deletions(-) create mode 100644 gcc/testsuite/rust/compile/issue-4260_0.rs 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..02dba8dc1dc8 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 @@ // . #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 ()) + 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,29 @@ 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 s = lhs.as_string (); + std::string var_name = s.substr (0, s.find (':')); + bool starts_with_under_score = var_name.compare (0, 1, "_") == 0; + 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 // . +#include "rust-hir-expr.h" #include "rust-hir-item.h" #include "rust-hir-pattern.h" #include "rust-hir-visitor.h" @@ -33,13 +34,14 @@ class UnusedVarChecker : public HIR::DefaultHIRVisitor private: const Resolver2_0::NameResolutionContext &nr_context; Analysis::Mappings &mappings; - std::unique_ptr 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 @@ // . #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 @@ class UnusedVarCollector : public HIR::DefaultHIRVisitor 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 void mark_path_used (T &path_expr) + template 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 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 is_used; + std::map> 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 +} From 80ba1df74cb8503c8f80f4343f2dc580ff946fa6 Mon Sep 17 00:00:00 2001 From: Lucas Ly Ba Date: Fri, 14 Nov 2025 21:07:00 +0000 Subject: [PATCH 3/5] gccrs: refactor unused var lint gcc/rust/ChangeLog: * checks/lints/unused-var/rust-unused-var-checker.cc (UnusedVarChecker::visit): Change unused name warning to unused variable warning. * checks/lints/unused-var/rust-unused-var-collector.cc (UnusedVarCollector::visit): Remove useless methods. * checks/lints/unused-var/rust-unused-var-collector.h: Same here. * checks/lints/unused-var/rust-unused-var-context.cc (UnusedVarContext::add_variable): Add used variables to set. (UnusedVarContext::mark_used): Remove method. (UnusedVarContext::is_variable_used): Check if the set contains the hir id linked to a variable. (UnusedVarContext::as_string): Refactor method for new set. * checks/lints/unused-var/rust-unused-var-context.h: Refactor methods. * lang.opt: Change description for unused check flag. gcc/testsuite/ChangeLog: * rust/compile/static_item_0.rs: Modify warning output. * rust/compile/template_function_0.rs: Modify warning output. Signed-off-by: Lucas Ly Ba --- .../unused-var/rust-unused-var-checker.cc | 11 +++++---- .../unused-var/rust-unused-var-collector.cc | 23 +++---------------- .../unused-var/rust-unused-var-collector.h | 5 +--- .../unused-var/rust-unused-var-context.cc | 16 ++++--------- .../unused-var/rust-unused-var-context.h | 4 +--- gcc/rust/lang.opt | 2 +- gcc/testsuite/rust/compile/static_item_0.rs | 2 +- .../rust/compile/template_function_0.rs | 4 ++-- 8 files changed, 19 insertions(+), 48 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 02dba8dc1dc8..6bee9760096e 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 @@ -44,8 +44,9 @@ 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 %qs", + if (!unused_context.is_variable_used (id) && !starts_with_under_score) + rust_warning_at (item.get_locus (), OPT_Wunused_variable, + "unused variable %qs", item.get_identifier ().as_string ().c_str ()); } @@ -55,8 +56,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 %qs", + if (!unused_context.is_variable_used (id) && !starts_with_under_score) + rust_warning_at (item.get_locus (), OPT_Wunused_variable, "unused variable %qs", item.get_identifier ().as_string ().c_str ()); } @@ -74,7 +75,7 @@ UnusedVarChecker::visit (HIR::IdentifierPattern &pattern) 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 %qs", + "unused variable %qs", pattern.get_identifier ().as_string ().c_str ()); } void 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 e9e98c5f2b68..0d601dd51972 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 @@ -39,42 +39,25 @@ UnusedVarCollector::go (HIR::Crate &crate) item->accept_vis (*this); } -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 (HIR::IdentifierPattern &pattern) -{ - unused_var_context.add_variable (pattern.get_mappings ().get_hirid ()); -} - void UnusedVarCollector::visit (HIR::PathInExpression &expr) { mark_path_used (expr); + walk (expr); } void UnusedVarCollector::visit (HIR::QualifiedPathInExpression &expr) { mark_path_used (expr); + walk (expr); } void UnusedVarCollector::visit (HIR::StructExprFieldIdentifier &ident) { mark_path_used (ident); + walk (ident); } void UnusedVarCollector::visit (HIR::AssignmentExpr &expr) 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 e3fed0c29466..d792e7d6c055 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 @@ -42,9 +42,6 @@ class UnusedVarCollector : public HIR::DefaultHIRVisitor using HIR::DefaultHIRVisitor::visit; 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 (HIR::AssignmentExpr &expr) override; @@ -59,7 +56,7 @@ class UnusedVarCollector : public HIR::DefaultHIRVisitor template 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.add_variable (def_id); unused_var_context.remove_assign (def_id); } }; 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 435fba46a8f7..50bb85b40d38 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 @@ -23,22 +23,15 @@ namespace Analysis { 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; + used_vars.emplace (id); } bool UnusedVarContext::is_variable_used (HirId id) const { - auto it = is_used.find (id); - return it != is_used.end () && it->second; + return used_vars.find (id) != used_vars.end (); } void @@ -66,10 +59,9 @@ UnusedVarContext::as_string () const { std::stringstream ss; ss << "UnusedVarContext: "; - for (const auto &pair : is_used) + for (const auto &v : used_vars) { - ss << "HirId: " << pair.first << " Used: " << (pair.second ? "Yes" : "No") - << "\n"; + ss << "HirId: " << v << "\n"; } return ss.str (); } 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 bde793defb8a..75d358abbbad 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 @@ -25,9 +25,7 @@ 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); @@ -35,7 +33,7 @@ class UnusedVarContext std::string as_string () const; private: - std::map is_used; + std::unordered_set used_vars; std::map> assigned_vars; }; diff --git a/gcc/rust/lang.opt b/gcc/rust/lang.opt index 67a2ae075c95..3c708bafe9f7 100644 --- a/gcc/rust/lang.opt +++ b/gcc/rust/lang.opt @@ -235,6 +235,6 @@ 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 +Use the new unused variable check implementation. ; This comment is to ensure we retain the blank line above. diff --git a/gcc/testsuite/rust/compile/static_item_0.rs b/gcc/testsuite/rust/compile/static_item_0.rs index 69d8ec40927d..3fc74715c6d9 100644 --- a/gcc/testsuite/rust/compile/static_item_0.rs +++ b/gcc/testsuite/rust/compile/static_item_0.rs @@ -1,3 +1,3 @@ // { dg-additional-options "-frust-unused-check-2.0" } static TEST: usize = 1; -// { dg-warning "unused name" "" { target *-*-* } .-1 } \ No newline at end of file +// { dg-warning "unused variable .TEST." "" { target *-*-* } .-1 } diff --git a/gcc/testsuite/rust/compile/template_function_0.rs b/gcc/testsuite/rust/compile/template_function_0.rs index 92e3f21cd054..4d12d948e60a 100644 --- a/gcc/testsuite/rust/compile/template_function_0.rs +++ b/gcc/testsuite/rust/compile/template_function_0.rs @@ -3,5 +3,5 @@ pub trait Sized {} pub fn test (a: usize) -> () { - // { dg-warning "unused name" "" { target *-*-* } .-1 } -} \ No newline at end of file + // { dg-warning "unused variable .a." "" { target *-*-* } .-1 } +} From 9353bdf211782b395776ec4fb32d92913d45a2ca Mon Sep 17 00:00:00 2001 From: Lucas Ly Ba Date: Mon, 17 Nov 2025 13:53:14 +0000 Subject: [PATCH 4/5] gccrs: change unused lint files name to unused gcc/rust/ChangeLog: * Make-lang.in: Compile the right files. * checks/lints/unused-var/rust-unused-var-checker.cc: Move to... * checks/lints/unused/rust-unused-checker.cc: ...here. * checks/lints/unused-var/rust-unused-var-checker.h: Move to... * checks/lints/unused/rust-unused-checker.h: ...here. * checks/lints/unused-var/rust-unused-var-collector.cc: Move to... * checks/lints/unused/rust-unused-collector.cc: ...here. * checks/lints/unused-var/rust-unused-var-collector.h: Move to... * checks/lints/unused/rust-unused-collector.h: ...here. * checks/lints/unused-var/rust-unused-var-context.cc: Move to... * checks/lints/unused/rust-unused-context.cc: ...here. * checks/lints/unused-var/rust-unused-var-context.h: Move to... * checks/lints/unused/rust-unused-context.h: ...here. * rust-session-manager.cc (Session::compile_crate): Call the right method. Signed-off-by: Lucas Ly Ba --- gcc/rust/Make-lang.in | 12 ++++---- .../rust-unused-checker.cc} | 29 +++++++++---------- .../rust-unused-checker.h} | 8 ++--- .../rust-unused-collector.cc} | 19 ++++++------ .../rust-unused-collector.h} | 14 ++++----- .../rust-unused-context.cc} | 16 +++++----- .../rust-unused-context.h} | 2 +- gcc/rust/rust-session-manager.cc | 4 +-- 8 files changed, 50 insertions(+), 54 deletions(-) rename gcc/rust/checks/lints/{unused-var/rust-unused-var-checker.cc => unused/rust-unused-checker.cc} (78%) rename gcc/rust/checks/lints/{unused-var/rust-unused-var-checker.h => unused/rust-unused-checker.h} (90%) rename gcc/rust/checks/lints/{unused-var/rust-unused-var-collector.cc => unused/rust-unused-collector.cc} (73%) rename gcc/rust/checks/lints/{unused-var/rust-unused-var-collector.h => unused/rust-unused-collector.h} (84%) rename gcc/rust/checks/lints/{unused-var/rust-unused-var-context.cc => unused/rust-unused-context.cc} (78%) rename gcc/rust/checks/lints/{unused-var/rust-unused-var-context.h => unused/rust-unused-context.h} (98%) diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in index a79729bae057..bb063f1f102f 100644 --- a/gcc/rust/Make-lang.in +++ b/gcc/rust/Make-lang.in @@ -200,9 +200,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-unused-checker.o \ + rust/rust-unused-collector.o \ + rust/rust-unused-context.o \ rust/rust-readonly-check.o \ rust/rust-hir-type-check-path.o \ rust/rust-unsafe-checker.o \ @@ -435,7 +435,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/lints/unused \ -I $(srcdir)/rust/checks/errors/privacy \ -I $(srcdir)/rust/checks/errors/borrowck \ -I $(srcdir)/rust/checks/errors/feature \ @@ -506,8 +506,8 @@ 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 +# build unused checking pass files in rust folder +rust/%.o: rust/checks/lints/unused/%.cc $(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $< $(POSTCOMPILE) diff --git a/gcc/rust/checks/lints/unused-var/rust-unused-var-checker.cc b/gcc/rust/checks/lints/unused/rust-unused-checker.cc similarity index 78% rename from gcc/rust/checks/lints/unused-var/rust-unused-var-checker.cc rename to gcc/rust/checks/lints/unused/rust-unused-checker.cc index 6bee9760096e..3585c0537cbf 100644 --- a/gcc/rust/checks/lints/unused-var/rust-unused-var-checker.cc +++ b/gcc/rust/checks/lints/unused/rust-unused-checker.cc @@ -16,7 +16,7 @@ // along with GCC; see the file COPYING3. If not see // . -#include "rust-unused-var-checker.h" +#include "rust-unused-checker.h" #include "rust-hir-expr.h" #include "rust-hir-item.h" @@ -24,22 +24,21 @@ namespace Rust { namespace Analysis { -UnusedVarChecker::UnusedVarChecker () +UnusedChecker::UnusedChecker () : nr_context ( Resolver2_0::ImmutableNameResolutionContext::get ().resolver ()), - mappings (Analysis::Mappings::get ()), - unused_var_context (UnusedVarContext ()) + mappings (Analysis::Mappings::get ()), unused_context (UnusedContext ()) {} void -UnusedVarChecker::go (HIR::Crate &crate) +UnusedChecker::go (HIR::Crate &crate) { - UnusedVarCollector collector (unused_var_context); + UnusedCollector collector (unused_context); collector.go (crate); for (auto &item : crate.get_items ()) item->accept_vis (*this); } void -UnusedVarChecker::visit (HIR::ConstantItem &item) +UnusedChecker::visit (HIR::ConstantItem &item) { std::string var_name = item.get_identifier ().as_string (); bool starts_with_under_score = var_name.compare (0, 1, "_") == 0; @@ -51,28 +50,29 @@ UnusedVarChecker::visit (HIR::ConstantItem &item) } void -UnusedVarChecker::visit (HIR::StaticItem &item) +UnusedChecker::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_context.is_variable_used (id) && !starts_with_under_score) - rust_warning_at (item.get_locus (), OPT_Wunused_variable, "unused variable %qs", + rust_warning_at (item.get_locus (), OPT_Wunused_variable, + "unused variable %qs", item.get_identifier ().as_string ().c_str ()); } void -UnusedVarChecker::visit (HIR::TraitItemFunc &item) +UnusedChecker::visit (HIR::TraitItemFunc &item) { // TODO: check trait item functions if they are not derived. } void -UnusedVarChecker::visit (HIR::IdentifierPattern &pattern) +UnusedChecker::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_context.is_variable_used (id) && var_name != "self" && !starts_with_under_score) rust_warning_at (pattern.get_locus (), OPT_Wunused_variable, "unused variable %qs", @@ -80,7 +80,7 @@ UnusedVarChecker::visit (HIR::IdentifierPattern &pattern) } void -UnusedVarChecker::visit (HIR::AssignmentExpr &expr) +UnusedChecker::visit (HIR::AssignmentExpr &expr) { const auto &lhs = expr.get_lhs (); @@ -90,8 +90,7 @@ UnusedVarChecker::visit (HIR::AssignmentExpr &expr) 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 ()) + if (unused_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 ()); diff --git a/gcc/rust/checks/lints/unused-var/rust-unused-var-checker.h b/gcc/rust/checks/lints/unused/rust-unused-checker.h similarity index 90% rename from gcc/rust/checks/lints/unused-var/rust-unused-var-checker.h rename to gcc/rust/checks/lints/unused/rust-unused-checker.h index bb9c8ac85d64..6f5f8badefeb 100644 --- a/gcc/rust/checks/lints/unused-var/rust-unused-var-checker.h +++ b/gcc/rust/checks/lints/unused/rust-unused-checker.h @@ -20,21 +20,21 @@ #include "rust-hir-item.h" #include "rust-hir-pattern.h" #include "rust-hir-visitor.h" +#include "rust-unused-collector.h" #include "rust-immutable-name-resolution-context.h" -#include "rust-unused-var-collector.h" namespace Rust { namespace Analysis { -class UnusedVarChecker : public HIR::DefaultHIRVisitor +class UnusedChecker : public HIR::DefaultHIRVisitor { public: - UnusedVarChecker (); + UnusedChecker (); void go (HIR::Crate &crate); private: const Resolver2_0::NameResolutionContext &nr_context; Analysis::Mappings &mappings; - UnusedVarContext unused_var_context; + UnusedContext unused_context; using HIR::DefaultHIRVisitor::visit; virtual void visit (HIR::TraitItemFunc &decl) override; diff --git a/gcc/rust/checks/lints/unused-var/rust-unused-var-collector.cc b/gcc/rust/checks/lints/unused/rust-unused-collector.cc similarity index 73% rename from gcc/rust/checks/lints/unused-var/rust-unused-var-collector.cc rename to gcc/rust/checks/lints/unused/rust-unused-collector.cc index 0d601dd51972..530c6b0ce9c4 100644 --- a/gcc/rust/checks/lints/unused-var/rust-unused-var-collector.cc +++ b/gcc/rust/checks/lints/unused/rust-unused-collector.cc @@ -16,55 +16,54 @@ // along with GCC; see the file COPYING3. If not see // . -#include "rust-unused-var-collector.h" +#include "rust-unused-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 { -UnusedVarCollector::UnusedVarCollector (UnusedVarContext &context) +UnusedCollector::UnusedCollector (UnusedContext &context) : nr_context ( Resolver2_0::ImmutableNameResolutionContext::get ().resolver ()), - mappings (Analysis::Mappings::get ()), unused_var_context (context) + mappings (Analysis::Mappings::get ()), unused_context (context) {} void -UnusedVarCollector::go (HIR::Crate &crate) +UnusedCollector::go (HIR::Crate &crate) { for (auto &item : crate.get_items ()) item->accept_vis (*this); } void -UnusedVarCollector::visit (HIR::PathInExpression &expr) +UnusedCollector::visit (HIR::PathInExpression &expr) { mark_path_used (expr); walk (expr); } void -UnusedVarCollector::visit (HIR::QualifiedPathInExpression &expr) +UnusedCollector::visit (HIR::QualifiedPathInExpression &expr) { mark_path_used (expr); walk (expr); } void -UnusedVarCollector::visit (HIR::StructExprFieldIdentifier &ident) +UnusedCollector::visit (HIR::StructExprFieldIdentifier &ident) { mark_path_used (ident); walk (ident); } void -UnusedVarCollector::visit (HIR::AssignmentExpr &expr) +UnusedCollector::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); + unused_context.add_assign (def_id, id); visit_outer_attrs (expr); expr.get_rhs ().accept_vis (*this); } diff --git a/gcc/rust/checks/lints/unused-var/rust-unused-var-collector.h b/gcc/rust/checks/lints/unused/rust-unused-collector.h similarity index 84% rename from gcc/rust/checks/lints/unused-var/rust-unused-var-collector.h rename to gcc/rust/checks/lints/unused/rust-unused-collector.h index d792e7d6c055..7f3ad88fdca6 100644 --- a/gcc/rust/checks/lints/unused-var/rust-unused-var-collector.h +++ b/gcc/rust/checks/lints/unused/rust-unused-collector.h @@ -17,27 +17,25 @@ // . #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-mapping-common.h" #include "rust-name-resolution-context.h" -#include "rust-unused-var-context.h" -#include "rust-name-resolver.h" +#include "rust-unused-context.h" namespace Rust { namespace Analysis { -class UnusedVarCollector : public HIR::DefaultHIRVisitor +class UnusedCollector : public HIR::DefaultHIRVisitor { public: - UnusedVarCollector (UnusedVarContext &context); + UnusedCollector (UnusedContext &context); void go (HIR::Crate &crate); private: const Resolver2_0::NameResolutionContext &nr_context; Analysis::Mappings &mappings; - UnusedVarContext &unused_var_context; + UnusedContext &unused_context; using HIR::DefaultHIRVisitor::visit; virtual void visit (HIR::PathInExpression &expr) override; @@ -56,8 +54,8 @@ class UnusedVarCollector : public HIR::DefaultHIRVisitor template void mark_path_used (T &path_expr) { auto def_id = get_def_id (path_expr); - unused_var_context.add_variable (def_id); - unused_var_context.remove_assign (def_id); + unused_context.add_variable (def_id); + unused_context.remove_assign (def_id); } }; } // namespace Analysis diff --git a/gcc/rust/checks/lints/unused-var/rust-unused-var-context.cc b/gcc/rust/checks/lints/unused/rust-unused-context.cc similarity index 78% rename from gcc/rust/checks/lints/unused-var/rust-unused-var-context.cc rename to gcc/rust/checks/lints/unused/rust-unused-context.cc index 50bb85b40d38..d975865ed588 100644 --- a/gcc/rust/checks/lints/unused-var/rust-unused-var-context.cc +++ b/gcc/rust/checks/lints/unused/rust-unused-context.cc @@ -16,38 +16,38 @@ // along with GCC; see the file COPYING3. If not see // . -#include "rust-unused-var-context.h" +#include "rust-unused-context.h" namespace Rust { namespace Analysis { void -UnusedVarContext::add_variable (HirId id) +UnusedContext::add_variable (HirId id) { used_vars.emplace (id); } bool -UnusedVarContext::is_variable_used (HirId id) const +UnusedContext::is_variable_used (HirId id) const { return used_vars.find (id) != used_vars.end (); } void -UnusedVarContext::add_assign (HirId id_def, HirId id) +UnusedContext::add_assign (HirId id_def, HirId id) { assigned_vars[id_def].push_back (id); } void -UnusedVarContext::remove_assign (HirId id_def) +UnusedContext::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) +UnusedContext::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) @@ -55,10 +55,10 @@ UnusedVarContext::is_variable_assigned (HirId id_def, HirId id) } std::string -UnusedVarContext::as_string () const +UnusedContext::as_string () const { std::stringstream ss; - ss << "UnusedVarContext: "; + ss << "UnusedContext: "; for (const auto &v : used_vars) { ss << "HirId: " << v << "\n"; diff --git a/gcc/rust/checks/lints/unused-var/rust-unused-var-context.h b/gcc/rust/checks/lints/unused/rust-unused-context.h similarity index 98% rename from gcc/rust/checks/lints/unused-var/rust-unused-var-context.h rename to gcc/rust/checks/lints/unused/rust-unused-context.h index 75d358abbbad..0a9faf53c8c3 100644 --- a/gcc/rust/checks/lints/unused-var/rust-unused-var-context.h +++ b/gcc/rust/checks/lints/unused/rust-unused-context.h @@ -21,7 +21,7 @@ namespace Rust { namespace Analysis { -class UnusedVarContext +class UnusedContext { public: void add_variable (HirId id); diff --git a/gcc/rust/rust-session-manager.cc b/gcc/rust/rust-session-manager.cc index 8b496d00bf0e..293e4bc65f2d 100644 --- a/gcc/rust/rust-session-manager.cc +++ b/gcc/rust/rust-session-manager.cc @@ -38,7 +38,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-unused-checker.h" #include "rust-readonly-check.h" #include "rust-hir-dump.h" #include "rust-ast-dump.h" @@ -736,7 +736,7 @@ Session::compile_crate (const char *filename) Analysis::ScanDeadcode::Scan (hir); if (flag_unused_check_2_0) - Analysis::UnusedVarChecker ().go (hir); + Analysis::UnusedChecker ().go (hir); else Analysis::UnusedVariables::Lint (*ctx); From 85f2a83e220d490d0ad96392af97288e70a4a176 Mon Sep 17 00:00:00 2001 From: Lucas Ly Ba Date: Mon, 17 Nov 2025 16:13:33 +0000 Subject: [PATCH 5/5] gccrs: add unused mut lint gcc/rust/ChangeLog: * checks/lints/unused/rust-unused-checker.cc (UnusedChecker::UnusedChecker): Add warning for identifier pattern and field ident pattern in struct (UnusedChecker::visit): Add methods. * checks/lints/unused/rust-unused-checker.h: Same here. * checks/lints/unused/rust-unused-collector.cc (UnusedCollector::UnusedCollector): Collect unused mut variables (UnusedCollector::visit): Add methods. * checks/lints/unused/rust-unused-collector.h: Same here. * checks/lints/unused/rust-unused-context.cc (UnusedContext::remove_assign): Add methods for unused mut set. (UnusedContext::add_mut): Same here. (UnusedContext::remove_mut): Same here. (UnusedContext::is_mut_used): Same here. * checks/lints/unused/rust-unused-context.h: Same here. gcc/testsuite/ChangeLog: * rust/compile/unused-mut-identifier_0.rs: New test. * rust/compile/unused-mut-struct-field_0.rs: New test. Signed-off-by: Lucas Ly Ba --- .../lints/unused/rust-unused-checker.cc | 26 +++++++++++++++++++ .../checks/lints/unused/rust-unused-checker.h | 1 + .../lints/unused/rust-unused-collector.cc | 20 ++++++++++++++ .../lints/unused/rust-unused-collector.h | 8 ++++++ .../lints/unused/rust-unused-context.cc | 19 ++++++++++++++ .../checks/lints/unused/rust-unused-context.h | 8 ++++++ .../rust/compile/unused-mut-identifier_0.rs | 6 +++++ .../rust/compile/unused-mut-struct-field_0.rs | 17 ++++++++++++ 8 files changed, 105 insertions(+) create mode 100644 gcc/testsuite/rust/compile/unused-mut-identifier_0.rs create mode 100644 gcc/testsuite/rust/compile/unused-mut-struct-field_0.rs diff --git a/gcc/rust/checks/lints/unused/rust-unused-checker.cc b/gcc/rust/checks/lints/unused/rust-unused-checker.cc index 3585c0537cbf..fc3fb1348a97 100644 --- a/gcc/rust/checks/lints/unused/rust-unused-checker.cc +++ b/gcc/rust/checks/lints/unused/rust-unused-checker.cc @@ -77,6 +77,12 @@ UnusedChecker::visit (HIR::IdentifierPattern &pattern) rust_warning_at (pattern.get_locus (), OPT_Wunused_variable, "unused variable %qs", pattern.get_identifier ().as_string ().c_str ()); + + if (pattern.is_mut () && !unused_context.is_mut_used (id) + && var_name != "self" && !starts_with_under_score) + rust_warning_at (pattern.get_locus (), OPT_Wunused_variable, + "unused mut %qs", + pattern.get_identifier ().as_string ().c_str ()); } void @@ -95,5 +101,25 @@ UnusedChecker::visit (HIR::AssignmentExpr &expr) rust_warning_at (lhs.get_locus (), OPT_Wunused_variable, "unused assignment %qs", var_name.c_str ()); } + +void +UnusedChecker::visit (HIR::StructPatternFieldIdent &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_context.is_variable_used (id) && var_name != "self" + && !starts_with_under_score) + rust_warning_at (pattern.get_locus (), OPT_Wunused_variable, + "unused variable %qs", + pattern.get_identifier ().as_string ().c_str ()); + + if (pattern.is_mut () && !unused_context.is_mut_used (id) + && var_name != "self" && !starts_with_under_score) + rust_warning_at (pattern.get_locus (), OPT_Wunused_variable, + "unused mut %qs", + pattern.get_identifier ().as_string ().c_str ()); +} + } // namespace Analysis } // namespace Rust diff --git a/gcc/rust/checks/lints/unused/rust-unused-checker.h b/gcc/rust/checks/lints/unused/rust-unused-checker.h index 6f5f8badefeb..690435ce455e 100644 --- a/gcc/rust/checks/lints/unused/rust-unused-checker.h +++ b/gcc/rust/checks/lints/unused/rust-unused-checker.h @@ -42,6 +42,7 @@ class UnusedChecker : public HIR::DefaultHIRVisitor virtual void visit (HIR::StaticItem &item) override; virtual void visit (HIR::IdentifierPattern &identifier) override; virtual void visit (HIR::AssignmentExpr &identifier) override; + virtual void visit (HIR::StructPatternFieldIdent &identifier) override; }; } // namespace Analysis } // namespace Rust diff --git a/gcc/rust/checks/lints/unused/rust-unused-collector.cc b/gcc/rust/checks/lints/unused/rust-unused-collector.cc index 530c6b0ce9c4..b07b09e4a362 100644 --- a/gcc/rust/checks/lints/unused/rust-unused-collector.cc +++ b/gcc/rust/checks/lints/unused/rust-unused-collector.cc @@ -58,15 +58,35 @@ UnusedCollector::visit (HIR::StructExprFieldIdentifier &ident) mark_path_used (ident); walk (ident); } + void UnusedCollector::visit (HIR::AssignmentExpr &expr) { auto def_id = get_def_id (expr.get_lhs ()); HirId id = expr.get_lhs ().get_mappings ().get_hirid (); + unused_context.remove_mut (def_id); unused_context.add_assign (def_id, id); visit_outer_attrs (expr); expr.get_rhs ().accept_vis (*this); } +void +UnusedCollector::visit (HIR::IdentifierPattern &pattern) +{ + if (pattern.is_mut ()) + unused_context.add_mut (pattern.get_mappings ().get_hirid ()); + + walk (pattern); +} + +void +UnusedCollector::visit (HIR::StructPatternFieldIdent &pattern) +{ + if (pattern.is_mut ()) + unused_context.add_mut (pattern.get_mappings ().get_hirid ()); + + walk (pattern); +} + } // namespace Analysis } // namespace Rust diff --git a/gcc/rust/checks/lints/unused/rust-unused-collector.h b/gcc/rust/checks/lints/unused/rust-unused-collector.h index 7f3ad88fdca6..ad10677cf879 100644 --- a/gcc/rust/checks/lints/unused/rust-unused-collector.h +++ b/gcc/rust/checks/lints/unused/rust-unused-collector.h @@ -38,11 +38,19 @@ class UnusedCollector : public HIR::DefaultHIRVisitor UnusedContext &unused_context; using HIR::DefaultHIRVisitor::visit; + + // Unused var virtual void visit (HIR::PathInExpression &expr) override; virtual void visit (HIR::StructExprFieldIdentifier &ident) override; virtual void visit (HIR::QualifiedPathInExpression &expr) override; + + // Unused assignments virtual void visit (HIR::AssignmentExpr &expr) override; + // Unused mut + virtual void visit (HIR::IdentifierPattern &pattern) override; + virtual void visit (HIR::StructPatternFieldIdent &pattern) override; + template HirId get_def_id (T &path_expr) { NodeId ast_node_id = path_expr.get_mappings ().get_nodeid (); diff --git a/gcc/rust/checks/lints/unused/rust-unused-context.cc b/gcc/rust/checks/lints/unused/rust-unused-context.cc index d975865ed588..29142f4838ed 100644 --- a/gcc/rust/checks/lints/unused/rust-unused-context.cc +++ b/gcc/rust/checks/lints/unused/rust-unused-context.cc @@ -46,6 +46,7 @@ UnusedContext::remove_assign (HirId id_def) if (assigned_vars.find (id_def) != assigned_vars.end ()) assigned_vars[id_def].pop_back (); } + bool UnusedContext::is_variable_assigned (HirId id_def, HirId id) { @@ -54,6 +55,24 @@ UnusedContext::is_variable_assigned (HirId id_def, HirId id) != assigned_vec.end (); } +void +UnusedContext::add_mut (HirId id) +{ + mutable_vars.emplace (id); +} + +void +UnusedContext::remove_mut (HirId id) +{ + mutable_vars.erase (id); +} + +bool +UnusedContext::is_mut_used (HirId id) const +{ + return mutable_vars.find (id) == mutable_vars.end (); +} + std::string UnusedContext::as_string () const { diff --git a/gcc/rust/checks/lints/unused/rust-unused-context.h b/gcc/rust/checks/lints/unused/rust-unused-context.h index 0a9faf53c8c3..832779d7dbf8 100644 --- a/gcc/rust/checks/lints/unused/rust-unused-context.h +++ b/gcc/rust/checks/lints/unused/rust-unused-context.h @@ -24,16 +24,24 @@ namespace Analysis { class UnusedContext { public: + // Unused var void add_variable (HirId id); bool is_variable_used (HirId id) const; + + // Assigned var void add_assign (HirId id_def, HirId id); void remove_assign (HirId id_def); bool is_variable_assigned (HirId id_def, HirId id); + // Mutable var + void add_mut (HirId id); + void remove_mut (HirId id); + bool is_mut_used (HirId id) const; std::string as_string () const; private: std::unordered_set used_vars; + std::unordered_set mutable_vars; std::map> assigned_vars; }; diff --git a/gcc/testsuite/rust/compile/unused-mut-identifier_0.rs b/gcc/testsuite/rust/compile/unused-mut-identifier_0.rs new file mode 100644 index 000000000000..58d1b09e2267 --- /dev/null +++ b/gcc/testsuite/rust/compile/unused-mut-identifier_0.rs @@ -0,0 +1,6 @@ +// { dg-additional-options "-frust-unused-check-2.0" } +pub fn a() ->i32 { + let mut x = 2; +// { dg-warning "unused mut .x." "" { target *-*-* } .-1 } + return x +} diff --git a/gcc/testsuite/rust/compile/unused-mut-struct-field_0.rs b/gcc/testsuite/rust/compile/unused-mut-struct-field_0.rs new file mode 100644 index 000000000000..1662dd3ba1c4 --- /dev/null +++ b/gcc/testsuite/rust/compile/unused-mut-struct-field_0.rs @@ -0,0 +1,17 @@ +// { dg-additional-options "-frust-unused-check-2.0" } +struct Point { x: i32, y: i32 } +// { dg-warning "field is never read: .x." "" { target *-*-* } .-1 } +// { dg-warning "field is never read: .y." "" { target *-*-* } .-2 } + +pub fn main() -> (i32, i32){ + let p = Point { x: 1, y: 2 }; + + match p { + Point { mut x, mut y } => { +// { dg-warning "unused mut .x." "" { target *-*-* } .-1 } +// { dg-warning "unused mut .y." "" { target *-*-* } .-2 } + return (x,y) + } + } +} +