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

commit r16-6843-gd8d32ffe61e8ac255872bfa5179bdc6d09323fe0
Author: Lucas Ly Ba <[email protected]>
Date:   Wed Jan 7 11:46:50 2026 +0000

    gccrs: add unused label lint
    
    gcc/rust/ChangeLog:
    
            * checks/lints/unused/rust-unused-checker.cc 
(UnusedChecker::visit_loop_label):
            Add warning for unused label in LoopLabel expr.
            * checks/lints/unused/rust-unused-checker.h: Likewise.
            * checks/lints/unused/rust-unused-collector.cc 
(UnusedCollector::visit):
            Check in BreakExpr and ContinueExpr if a label is used.
            * checks/lints/unused/rust-unused-collector.h: Likewise.
            * checks/lints/unused/rust-unused-context.cc 
(UnusedContext::add_label):
            Method helper.
            (UnusedContext::is_label_used): Likewise
            * checks/lints/unused/rust-unused-context.h: Likewise.
    
    gcc/testsuite/ChangeLog:
    
            * rust/compile/unused-label_0.rs: New test.
    
    Signed-off-by: Lucas Ly Ba <[email protected]>

Diff:
---
 .../checks/lints/unused/rust-unused-checker.cc     | 11 +++++++++
 gcc/rust/checks/lints/unused/rust-unused-checker.h |  1 +
 .../checks/lints/unused/rust-unused-collector.cc   | 18 ++++++++++++++
 .../checks/lints/unused/rust-unused-collector.h    | 10 ++++++++
 .../checks/lints/unused/rust-unused-context.cc     | 13 ++++++++++
 gcc/rust/checks/lints/unused/rust-unused-context.h |  6 +++++
 gcc/testsuite/rust/compile/unused-label_0.rs       | 28 ++++++++++++++++++++++
 7 files changed, 87 insertions(+)

diff --git a/gcc/rust/checks/lints/unused/rust-unused-checker.cc 
b/gcc/rust/checks/lints/unused/rust-unused-checker.cc
index 235acd6a4065..751306171deb 100644
--- a/gcc/rust/checks/lints/unused/rust-unused-checker.cc
+++ b/gcc/rust/checks/lints/unused/rust-unused-checker.cc
@@ -124,5 +124,16 @@ UnusedChecker::visit (HIR::EmptyStmt &stmt)
                   "unnecessary trailing semicolons");
 }
 
+void
+UnusedChecker::visit_loop_label (HIR::LoopLabel &label)
+{
+  auto lifetime = label.get_lifetime ();
+  std::string var_name = lifetime.to_string ();
+  auto id = lifetime.get_mappings ().get_hirid ();
+  if (!unused_context.is_label_used (id) && var_name[0] != '_')
+    rust_warning_at (lifetime.get_locus (), OPT_Wunused_variable,
+                    "unused label %qs", lifetime.to_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 061d0c6f0029..eeb94679ddd2 100644
--- a/gcc/rust/checks/lints/unused/rust-unused-checker.h
+++ b/gcc/rust/checks/lints/unused/rust-unused-checker.h
@@ -44,6 +44,7 @@ private:
   virtual void visit (HIR::AssignmentExpr &identifier) override;
   virtual void visit (HIR::StructPatternFieldIdent &identifier) override;
   virtual void visit (HIR::EmptyStmt &stmt) override;
+  virtual void visit_loop_label (HIR::LoopLabel &label) 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 b00fa84ff162..6767678c6f36 100644
--- a/gcc/rust/checks/lints/unused/rust-unused-collector.cc
+++ b/gcc/rust/checks/lints/unused/rust-unused-collector.cc
@@ -88,5 +88,23 @@ UnusedCollector::visit (HIR::StructPatternFieldIdent 
&pattern)
   walk (pattern);
 }
 
+void
+UnusedCollector::visit (HIR::BreakExpr &expr)
+{
+  if (!expr.has_label ())
+    return;
+  mark_label_used (expr.get_label ());
+  walk (expr);
+}
+
+void
+UnusedCollector::visit (HIR::ContinueExpr &expr)
+{
+  if (!expr.has_label ())
+    return;
+  mark_label_used (expr.get_label ());
+  walk (expr);
+}
+
 } // 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 6bcdcb868bc2..1944089d16c5 100644
--- a/gcc/rust/checks/lints/unused/rust-unused-collector.h
+++ b/gcc/rust/checks/lints/unused/rust-unused-collector.h
@@ -51,6 +51,10 @@ private:
   virtual void visit (HIR::IdentifierPattern &pattern) override;
   virtual void visit (HIR::StructPatternFieldIdent &pattern) override;
 
+  // Unused label
+  virtual void visit (HIR::BreakExpr &expr) override;
+  virtual void visit (HIR::ContinueExpr &expr) override;
+
   template <typename T> HirId get_def_id (T &path_expr)
   {
     NodeId ast_node_id = path_expr.get_mappings ().get_nodeid ();
@@ -65,6 +69,12 @@ private:
     unused_context.add_variable (def_id);
     unused_context.remove_assign (def_id);
   }
+
+  template <typename T> void mark_label_used (T &path_expr)
+  {
+    auto def_id = get_def_id (path_expr);
+    unused_context.add_label (def_id);
+  }
 };
 } // namespace Analysis
 } // namespace Rust
diff --git a/gcc/rust/checks/lints/unused/rust-unused-context.cc 
b/gcc/rust/checks/lints/unused/rust-unused-context.cc
index 40862edc3fc9..9b26e01bccee 100644
--- a/gcc/rust/checks/lints/unused/rust-unused-context.cc
+++ b/gcc/rust/checks/lints/unused/rust-unused-context.cc
@@ -73,6 +73,19 @@ UnusedContext::is_mut_used (HirId id) const
   return mutable_vars.find (id) == mutable_vars.end ();
 }
 
+void
+UnusedContext::add_label (HirId id)
+
+{
+  used_labels.emplace (id);
+}
+
+bool
+UnusedContext::is_label_used (HirId id) const
+{
+  return used_labels.find (id) != used_labels.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 9ab67ff35e88..1405ebb64965 100644
--- a/gcc/rust/checks/lints/unused/rust-unused-context.h
+++ b/gcc/rust/checks/lints/unused/rust-unused-context.h
@@ -37,12 +37,18 @@ public:
   void add_mut (HirId id);
   void remove_mut (HirId id);
   bool is_mut_used (HirId id) const;
+
+  // Unused label
+  void add_label (HirId id);
+  bool is_label_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;
+  std::unordered_set<HirId> used_labels;
 };
 
 } // namespace Analysis
diff --git a/gcc/testsuite/rust/compile/unused-label_0.rs 
b/gcc/testsuite/rust/compile/unused-label_0.rs
new file mode 100644
index 000000000000..3c868976de51
--- /dev/null
+++ b/gcc/testsuite/rust/compile/unused-label_0.rs
@@ -0,0 +1,28 @@
+// { dg-additional-options "-frust-unused-check-2.0" }
+
+pub fn foo_1() {
+    'a: loop {
+        break 'a;
+    }
+}
+
+pub fn foo_2() {
+    'a: loop {
+// { dg-warning "unused label ..a." "" { target *-*-* } .-1 }
+        break;
+    }
+}
+
+
+pub fn bar_1() {
+    'a: loop {
+        continue 'a;
+    }
+}
+
+pub fn bar_2() {
+    'a: loop {
+// { dg-warning "unused label ..a." "" { target *-*-* } .-1 }
+        continue;
+    }
+}

Reply via email to