Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions gcc/rust/Make-lang.in
Original file line number Diff line number Diff line change
Expand Up @@ -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-checker.o \
rust/rust-unused-collector.o \
rust/rust-unused-context.o \
rust/rust-readonly-check.o \
rust/rust-hir-type-check-path.o \
rust/rust-unsafe-checker.o \
Expand Down Expand Up @@ -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 \
-I $(srcdir)/rust/checks/errors/privacy \
-I $(srcdir)/rust/checks/errors/borrowck \
-I $(srcdir)/rust/checks/errors/feature \
Expand Down Expand Up @@ -502,6 +506,11 @@ rust/%.o: rust/checks/lints/%.cc
$(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
$(POSTCOMPILE)

# build unused checking pass files in rust folder
rust/%.o: rust/checks/lints/unused/%.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) $<
Expand Down
27 changes: 27 additions & 0 deletions gcc/rust/checks/lints/rust-lint-scan-deadcode.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#ifndef RUST_HIR_SCAN_DEADCODE
#define RUST_HIR_SCAN_DEADCODE

#include "options.h"
#include "rust-hir-full-decls.h"
#include "rust-hir-map.h"
#include "rust-lint-marklive.h"
Expand Down Expand Up @@ -133,6 +134,32 @@ class ScanDeadcode : public MarkLiveBase
item->accept_vis (*this);
}

void visit (HIR::ConstantItem &item) override
{
if (!flag_unused_check_2_0)
return;
std::string var_name = item.get_identifier ().as_string ();
bool starts_with_under_score = var_name.at (0) == '_';
HirId hirId = item.get_mappings ().get_hirid ();
if (should_warn (hirId) && !starts_with_under_score)
rust_warning_at (item.get_locus (), OPT_Wunused_variable,
"deadcode const item %qs",
item.get_identifier ().as_string ().c_str ());
}

void visit (HIR::StaticItem &item) override
{
if (!flag_unused_check_2_0)
return;
std::string var_name = item.get_identifier ().as_string ();
bool starts_with_under_score = var_name.at (0) == '_';
HirId hirId = item.get_mappings ().get_hirid ();
if (should_warn (hirId) && !starts_with_under_score)
rust_warning_at (item.get_locus (), OPT_Wunused_variable,
"deadcode static item %qs",
item.get_identifier ().as_string ().c_str ());
}

private:
std::set<HirId> live_symbols;
Resolver::Resolver *resolver;
Expand Down
103 changes: 103 additions & 0 deletions gcc/rust/checks/lints/unused/rust-unused-checker.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// 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-checker.h"
#include "rust-hir-expr.h"
#include "rust-hir-item.h"

#include "options.h"

namespace Rust {
namespace Analysis {
UnusedChecker::UnusedChecker ()
: nr_context (
Resolver2_0::ImmutableNameResolutionContext::get ().resolver ()),
mappings (Analysis::Mappings::get ()), unused_context (UnusedContext ())
{}
void
UnusedChecker::go (HIR::Crate &crate)
{
UnusedCollector collector (unused_context);
collector.go (crate);
for (auto &item : crate.get_items ())
item->accept_vis (*this);
}

void
UnusedChecker::visit (HIR::TraitItemFunc &item)
{
// TODO: check trait item functions if they are not derived.
}

void
UnusedChecker::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_context.is_variable_used (id) && var_name != "self"
&& !starts_with_under_score)
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 != "self" && !starts_with_under_score)
rust_warning_at (pattern.get_locus (), OPT_Wunused_variable,
"unused mut %qs",
pattern.get_identifier ().as_string ().c_str ());
}
void

UnusedChecker::visit (HIR::AssignmentExpr &expr)

{
const auto &lhs = expr.get_lhs ();
auto s = lhs.as_string ();
std::string var_name = s.substr (0, s.find (':'));
bool starts_with_under_score = var_name.compare (0, 1, "_") == 0;
NodeId ast_node_id = lhs.get_mappings ().get_nodeid ();
NodeId def_id = nr_context.lookup (ast_node_id).value ();
HirId id = mappings.lookup_node_to_hir (def_id).value ();
if (unused_context.is_variable_assigned (id, lhs.get_mappings ().get_hirid ())
&& !starts_with_under_score)
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 ();
bool starts_with_under_score = var_name.compare (0, 1, "_") == 0;
auto id = pattern.get_mappings ().get_hirid ();
if (!unused_context.is_variable_used (id) && var_name != "self"
&& !starts_with_under_score)
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 != "self" && !starts_with_under_score)
rust_warning_at (pattern.get_locus (), OPT_Wunused_variable,
"unused mut %qs",
pattern.get_identifier ().as_string ().c_str ());
}

} // namespace Analysis
} // namespace Rust
46 changes: 46 additions & 0 deletions gcc/rust/checks/lints/unused/rust-unused-checker.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// 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-pattern.h"
#include "rust-hir-visitor.h"
#include "rust-unused-collector.h"
#include "rust-immutable-name-resolution-context.h"

namespace Rust {
namespace Analysis {
class UnusedChecker : public HIR::DefaultHIRVisitor
{
public:
UnusedChecker ();
void go (HIR::Crate &crate);

private:
const Resolver2_0::NameResolutionContext &nr_context;
Analysis::Mappings &mappings;
UnusedContext unused_context;

using HIR::DefaultHIRVisitor::visit;
virtual void visit (HIR::TraitItemFunc &decl) 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
92 changes: 92 additions & 0 deletions gcc/rust/checks/lints/unused/rust-unused-collector.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// 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-collector.h"
#include "rust-hir-expr.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 {
UnusedCollector::UnusedCollector (UnusedContext &context)
: nr_context (
Resolver2_0::ImmutableNameResolutionContext::get ().resolver ()),
mappings (Analysis::Mappings::get ()), unused_context (context)
{}
void
UnusedCollector::go (HIR::Crate &crate)
{
for (auto &item : crate.get_items ())
item->accept_vis (*this);
}

void
UnusedCollector::visit (HIR::PathInExpression &expr)
{
mark_path_used (expr);
walk (expr);
}

void
UnusedCollector::visit (HIR::QualifiedPathInExpression &expr)
{
mark_path_used (expr);
walk (expr);
}

void
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
70 changes: 70 additions & 0 deletions gcc/rust/checks/lints/unused/rust-unused-collector.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// 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-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-context.h"

namespace Rust {
namespace Analysis {
class UnusedCollector : public HIR::DefaultHIRVisitor
{
public:
UnusedCollector (UnusedContext &context);
void go (HIR::Crate &crate);

private:
const Resolver2_0::NameResolutionContext &nr_context;
Analysis::Mappings &mappings;
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 ();
NodeId id = nr_context.lookup (ast_node_id).value ();
HirId def_id = mappings.lookup_node_to_hir (id).value ();
return def_id;
}

template <typename T> void mark_path_used (T &path_expr)
{
auto def_id = get_def_id (path_expr);
unused_context.add_variable (def_id);
unused_context.remove_assign (def_id);
}
};
} // namespace Analysis
} // namespace Rust
Loading