diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in index 8cdf6c98331..cfaa355aa4c 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 00000000000..d828bbc5ca1 --- /dev/null +++ b/gcc/rust/checks/lints/unused-var/rust-unused-var-checker.cc @@ -0,0 +1,128 @@ +// 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 (*UnusedVarContext::get ()) +{} +void +UnusedVarChecker::go (HIR::Crate &crate) +{ + UnusedVarCollector ().go (crate); + for (auto &item : crate.get_items ()) + item->accept_vis (*this); +} +void +UnusedVarChecker::visit (HIR::LetStmt &stmt) +{ + HIR::Pattern &pattern = stmt.get_pattern (); + check_variable (pattern); + walk (stmt); +} +void +UnusedVarChecker::visit_function_param (HIR::FunctionParam ¶m) +{ + check_variable (param.get_param_name ()); +} + +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::check_variable (HIR::Pattern &pattern) +{ + switch (pattern.get_pattern_type ()) + { + case HIR::Pattern::PatternType::IDENTIFIER: + check_variable_identifier ( + static_cast (pattern)); + break; + case HIR::Pattern::PatternType::TUPLE: + check_variable_tuple (static_cast (pattern)); + break; + default: + break; + } +} +void +UnusedVarChecker::check_variable_identifier (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 ()); +} +void +UnusedVarChecker::check_variable_tuple (HIR::TuplePattern &pattern) +{ + switch (pattern.get_items ().get_item_type ()) + { + case HIR::TuplePatternItems::ItemType::NO_REST: + { + auto items + = static_cast (pattern.get_items ()); + for (auto &item : items.get_patterns ()) + check_variable (*item); + } + break; + default: + break; + } +} +} // 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 00000000000..130313c75cc --- /dev/null +++ b/gcc/rust/checks/lints/unused-var/rust-unused-var-checker.h @@ -0,0 +1,48 @@ +// 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-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; + UnusedVarContext &unused_var_context; + + using HIR::DefaultHIRVisitor::visit; + virtual void visit (HIR::LetStmt &stmt) override; + virtual void visit_function_param (HIR::FunctionParam ¶m) override; + virtual void visit (HIR::TraitItemFunc &decl) override; + virtual void visit (HIR::ConstantItem &item) override; + virtual void visit (HIR::StaticItem &item) override; + void check_variable_identifier (HIR::IdentifierPattern &identifier); + void check_variable_tuple (HIR::TuplePattern &pattern); + void check_variable (HIR::Pattern &pattern); +}; +} // 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 00000000000..16fa02f4d61 --- /dev/null +++ b/gcc/rust/checks/lints/unused-var/rust-unused-var-collector.cc @@ -0,0 +1,130 @@ +// 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-immutable-name-resolution-context.h" + +namespace Rust { +namespace Analysis { +UnusedVarCollector::UnusedVarCollector () + : nr_context ( + Resolver2_0::ImmutableNameResolutionContext::get ().resolver ()), + mappings (Analysis::Mappings::get ()), + unused_var_context (*UnusedVarContext::get ()) +{} +void +UnusedVarCollector::go (HIR::Crate &crate) +{ + for (auto &item : crate.get_items ()) + item->accept_vis (*this); +} +void +UnusedVarCollector::visit (HIR::LetStmt &stmt) +{ + HIR::Pattern &pattern = stmt.get_pattern (); + collect_variable (pattern); + walk (stmt); +} + +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_function_param (HIR::FunctionParam ¶m) +{ + collect_variable (param.get_param_name ()); +} + +void +UnusedVarCollector::visit_closure_param (HIR::ClosureParam ¶m) +{ + collect_variable (param.get_pattern ()); +} + +void +UnusedVarCollector::collect_variable (HIR::Pattern &pattern) +{ + switch (pattern.get_pattern_type ()) + { + case HIR::Pattern::PatternType::IDENTIFIER: + { + auto &identifier = static_cast (pattern); + auto id = identifier.get_mappings ().get_hirid (); + unused_var_context.add_variable (id); + } + break; + case HIR::Pattern::PatternType::TUPLE: + { + collect_variable_tuple (static_cast (pattern)); + } + break; + default: + break; + } +} +void +UnusedVarCollector::collect_variable_tuple (HIR::TuplePattern &pattern) +{ + switch (pattern.get_items ().get_item_type ()) + { + case HIR::TuplePatternItems::ItemType::NO_REST: + { + auto &items + = static_cast (pattern.get_items ()); + for (auto &sub : items.get_patterns ()) + collect_variable (*sub); + } + break; + default: + break; + } +} + +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 00000000000..ce1f5d1e7aa --- /dev/null +++ b/gcc/rust/checks/lints/unused-var/rust-unused-var-collector.h @@ -0,0 +1,71 @@ +// 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-visitor.h" +#include "rust-name-resolution-context.h" +#include "rust-name-resolver.h" +#include "rust-unused-var-context.h" + +namespace Rust { +namespace Analysis { +class UnusedVarCollector : public HIR::DefaultHIRVisitor +{ +public: + UnusedVarCollector (); + 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::LetStmt &stmt) override; + 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::QualifiedPathInExpression &expr) override; + virtual void visit_function_param (HIR::FunctionParam ¶m) override; + virtual void visit_closure_param (HIR::ClosureParam ¶m) override; + + void collect_variable (HIR::Pattern &pattern); + void collect_variable_tuple (HIR::TuplePattern &pattern); + + template void mark_path_used (T &path_expr) + { + NodeId ast_node_id = path_expr.get_mappings ().get_nodeid (); + NodeId def_id; + + if (auto id = nr_context.lookup (ast_node_id)) + def_id = *id; + else + return; + + auto hir_id = mappings.lookup_node_to_hir (def_id); + if (!hir_id) + return; + + unused_var_context.mark_used (hir_id.value ()); + } +}; +} // 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 00000000000..2271aeeee06 --- /dev/null +++ b/gcc/rust/checks/lints/unused-var/rust-unused-var-context.cc @@ -0,0 +1,69 @@ +// 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 { + +UnusedVarContext * +UnusedVarContext::get () +{ + static UnusedVarContext *instance; + if (instance == nullptr) + instance = new UnusedVarContext (); + return instance; +} + +UnusedVarContext::~UnusedVarContext () {} + +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 00000000000..2672923b07a --- /dev/null +++ b/gcc/rust/checks/lints/unused-var/rust-unused-var-context.h @@ -0,0 +1,41 @@ +// 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: + static UnusedVarContext *get (); + ~UnusedVarContext (); + + void add_variable (HirId id); + void mark_used (HirId id); + + bool is_variable_used (HirId id) const; + + std::string as_string () const; + +private: + UnusedVarContext () = default; + std::map is_used; +}; +} // namespace Analysis +} // namespace Rust