diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in index 8cdf6c983312..cfaa355aa4c8 100644 --- a/gcc/rust/Make-lang.in +++ b/gcc/rust/Make-lang.in @@ -208,6 +208,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-readonly-check2.o \ rust/rust-hir-type-check-path.o \ @@ -441,6 +444,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/util \ @@ -510,6 +514,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..62392c63e6f8 --- /dev/null +++ b/gcc/rust/checks/lints/unused-var/rust-unused-var-context.h @@ -0,0 +1,37 @@ +// 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 c88f46757535..b6bd14627328 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" @@ -736,7 +737,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); + Analysis::ReadonlyCheck::Lint (*ctx); // 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