https://gcc.gnu.org/g:fe65006e77a434872ffbae3c0343dff4e7c31013

commit r16-6362-gfe65006e77a434872ffbae3c0343dff4e7c31013
Author: Lucas Ly Ba <[email protected]>
Date:   Mon Nov 17 16:13:33 2025 +0000

    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 <[email protected]>

Diff:
---
 .../checks/lints/unused/rust-unused-checker.cc     | 25 ++++++++++++++++++++++
 gcc/rust/checks/lints/unused/rust-unused-checker.h |  1 +
 .../checks/lints/unused/rust-unused-collector.cc   | 20 +++++++++++++++++
 .../checks/lints/unused/rust-unused-collector.h    |  8 +++++++
 .../checks/lints/unused/rust-unused-context.cc     | 19 ++++++++++++++++
 gcc/rust/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, 104 insertions(+)

diff --git a/gcc/rust/checks/lints/unused/rust-unused-checker.cc 
b/gcc/rust/checks/lints/unused/rust-unused-checker.cc
index 9f8394ef90e8..3ec69e22b46a 100644
--- a/gcc/rust/checks/lints/unused/rust-unused-checker.cc
+++ b/gcc/rust/checks/lints/unused/rust-unused-checker.cc
@@ -76,6 +76,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 != Values::Keywords::SELF && var_name[0] != '_')
+    rust_warning_at (pattern.get_locus (), OPT_Wunused_variable,
+                    "unused mut %qs",
+                    pattern.get_identifier ().as_string ().c_str ());
 }
 void
 
@@ -92,5 +98,24 @@ 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 ();
+  auto id = pattern.get_mappings ().get_hirid ();
+  if (!unused_context.is_variable_used (id)
+      && var_name != Values::Keywords::SELF && var_name[0] != '_')
+    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 != Values::Keywords::SELF && var_name[0] != '_')
+    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 @@ private:
   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 @@ private:
   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 <typename T> 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<HirId> used_vars;
+  std::unordered_set<HirId> mutable_vars;
   std::map<HirId, std::vector<HirId>> 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)
+        }
+    }
+}
+

Reply via email to