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] 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