From: Ryutaro Okada <[email protected]>

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 <[email protected]>
---
This change was merged into the gccrs repository and is posted here for
upstream visibility and potential drive-by review, as requested by GCC
release managers.
Each commit email contains a link to its details on github from where you can
find the Pull-Request and associated discussions.


Commit on github: 
https://github.com/Rust-GCC/gccrs/commit/38bccd0ece07aaa52ae906036fb428edec74655d

The commit has been mentioned in the following pull-request(s):
 - https://github.com/Rust-GCC/gccrs/pull/4283

 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       |  8 ++
 11 files changed, 393 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 e5a8a5eb4..a79729bae 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 000000000..c6cfd5bb2
--- /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
+// <http://www.gnu.org/licenses/>.
+
+#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<UnusedVarContext> ())
+{}
+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 000000000..d916caa2d
--- /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
+// <http://www.gnu.org/licenses/>.
+
+#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<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;
+};
+} // 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 000000000..deeabdef5
--- /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
+// <http://www.gnu.org/licenses/>.
+
+#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 000000000..ed3384056
--- /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
+// <http://www.gnu.org/licenses/>.
+
+#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 <typename T> 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 000000000..728d61d21
--- /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
+// <http://www.gnu.org/licenses/>.
+
+#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 000000000..14f89da78
--- /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
+// <http://www.gnu.org/licenses/>.
+
+#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<HirId, bool> is_used;
+};
+
+} // namespace Analysis
+} // namespace Rust
diff --git a/gcc/rust/lang.opt b/gcc/rust/lang.opt
index d9824f1a5..67a2ae075 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 bb8567694..1a278e4d0 100644
--- a/gcc/rust/rust-session-manager.cc
+++ b/gcc/rust/rust-session-manager.cc
@@ -39,6 +39,7 @@
 #include "rust-cfg-parser.h"
 #include "rust-lint-scan-deadcode.h"
 #include "rust-lint-unused-var.h"
+#include "rust-unused-var-checker.h"
 #include "rust-readonly-check.h"
 #include "rust-hir-dump.h"
 #include "rust-ast-dump.h"
@@ -781,7 +782,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 000000000..69d8ec409
--- /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 000000000..4e1c2c7c9
--- /dev/null
+++ b/gcc/testsuite/rust/compile/template_function_0.rs
@@ -0,0 +1,8 @@
+// { dg-additional-options "-frust-unused-check-2.0" }
+#![feature(lang_items)]
+#[lang = "sized"]
+pub trait Sized {}
+
+pub fn test<T> (a: usize) -> () {
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}

base-commit: f947b54cfc0360a0748280e77814b377dd077012
-- 
2.52.0

Reply via email to