Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
d351448
add more tests for `unused_results` lints
WaffleLapkin Feb 27, 2026
8958cb9
don't emit `unused_results` lint for tuples of trivial types
WaffleLapkin Feb 27, 2026
7f3bbe3
Report unused features
mu001999 Feb 5, 2026
28b6bcb
Add tests for unused-features
mu001999 Feb 5, 2026
d0a182f
Remove unused features in compiler
mu001999 Mar 1, 2026
60f457a
Allow unused features in tools
mu001999 Feb 12, 2026
253c696
Make non-queries available to `rustc_with_all_queries`.
nnethercote Feb 27, 2026
40b2274
Change how query vtables are constructed.
nnethercote Feb 27, 2026
f538526
vec/mod.rs: add missing period in "ie." in docs
DanielEScherzer Mar 2, 2026
2672810
Remove unused features in library tests
mu001999 Feb 20, 2026
b4b3057
Remove unused features in tests
mu001999 Feb 28, 2026
66786fc
fix next-solver ICE on PointeeSized goals
TaKO8Ki Feb 1, 2026
6cda9bf
replace issue number with link
TaKO8Ki Feb 1, 2026
ca74063
simplify test case and add revisions for both solvers
TaKO8Ki Mar 2, 2026
3d91c3e
Rollup merge of #151962 - TaKO8Ki:pointee-sized-next-solver-ice, r=lcnr
JonathanBrouwer Mar 2, 2026
41d8026
Rollup merge of #153161 - nnethercote:rejig-rustc_with_all_queries, r…
JonathanBrouwer Mar 2, 2026
0972842
Rollup merge of #152164 - mu001999-contrib:lint/unused_features, r=Jo…
JonathanBrouwer Mar 2, 2026
7268260
Rollup merge of #153191 - WaffleLapkin:shouldnt_use_trivial_tuples, r…
JonathanBrouwer Mar 2, 2026
299e863
Rollup merge of #153273 - DanielEScherzer:patch-2, r=joboet
JonathanBrouwer Mar 2, 2026
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
6 changes: 5 additions & 1 deletion compiler/rustc_driver_impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,11 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send))
}
}

Some(Linker::codegen_and_build_linker(tcx, &*compiler.codegen_backend))
let linker = Linker::codegen_and_build_linker(tcx, &*compiler.codegen_backend);

tcx.report_unused_features();

Some(linker)
});

// Linking is done outside the `compiler.enter()` so that the
Expand Down
2 changes: 0 additions & 2 deletions compiler/rustc_errors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
#![allow(rustc::direct_use_of_rustc_type_ir)]
#![cfg_attr(bootstrap, feature(assert_matches))]
#![feature(associated_type_defaults)]
#![feature(box_patterns)]
#![feature(default_field_values)]
#![feature(error_reporter)]
#![feature(macro_metavar_expr_concat)]
#![feature(negative_impls)]
#![feature(never_type)]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1330,7 +1330,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
safety: AttributeSafety::Normal,
template: template!(NameValueStr: "name"),
duplicates: ErrorFollowing,
gate: Gated{
gate: Gated {
feature: sym::rustc_attrs,
message: "use of an internal attribute",
check: Features::rustc_attrs,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_feature/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,5 +137,5 @@ pub use builtin_attrs::{
pub use removed::REMOVED_LANG_FEATURES;
pub use unstable::{
DEPENDENT_FEATURES, EnabledLangFeature, EnabledLibFeature, Features, INCOMPATIBLE_FEATURES,
UNSTABLE_LANG_FEATURES,
TRACK_FEATURE, UNSTABLE_LANG_FEATURES,
};
16 changes: 14 additions & 2 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,18 @@
use std::path::PathBuf;
use std::time::{SystemTime, UNIX_EPOCH};

use rustc_data_structures::AtomicRef;
use rustc_data_structures::fx::FxHashSet;
use rustc_span::{Span, Symbol, sym};

use super::{Feature, to_nonzero};

fn default_track_feature(_: Symbol) {}

/// Recording used features in the dependency graph so incremental can
/// replay used features when needed.
pub static TRACK_FEATURE: AtomicRef<fn(Symbol)> = AtomicRef::new(&(default_track_feature as _));

#[derive(PartialEq)]
enum FeatureStatus {
Default,
Expand Down Expand Up @@ -103,7 +110,12 @@ impl Features {

/// Is the given feature enabled (via `#[feature(...)]`)?
pub fn enabled(&self, feature: Symbol) -> bool {
self.enabled_features.contains(&feature)
if self.enabled_features.contains(&feature) {
TRACK_FEATURE(feature);
true
} else {
false
}
}
}

Expand All @@ -124,7 +136,7 @@ macro_rules! declare_features {
impl Features {
$(
pub fn $feature(&self) -> bool {
self.enabled_features.contains(&sym::$feature)
self.enabled(sym::$feature)
}
)*

Expand Down
23 changes: 22 additions & 1 deletion compiler/rustc_interface/src/callbacks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
use std::fmt;

use rustc_errors::DiagInner;
use rustc_middle::dep_graph::TaskDepsRef;
use rustc_middle::dep_graph::{DepNodeIndex, QuerySideEffect, TaskDepsRef};
use rustc_middle::ty::tls;
use rustc_span::Symbol;

fn track_span_parent(def_id: rustc_span::def_id::LocalDefId) {
tls::with_context_opt(|icx| {
Expand Down Expand Up @@ -51,6 +52,25 @@ fn track_diagnostic<R>(diagnostic: DiagInner, f: &mut dyn FnMut(DiagInner) -> R)
})
}

fn track_feature(feature: Symbol) {
tls::with_context_opt(|icx| {
let Some(icx) = icx else {
return;
};
let tcx = icx.tcx;

if let Some(dep_node_index) = tcx.sess.used_features.lock().get(&feature).copied() {
tcx.dep_graph.read_index(DepNodeIndex::from_u32(dep_node_index));
} else {
let dep_node_index = tcx
.dep_graph
.encode_side_effect(tcx, QuerySideEffect::CheckFeature { symbol: feature });
tcx.sess.used_features.lock().insert(feature, dep_node_index.as_u32());
tcx.dep_graph.read_index(dep_node_index);
}
})
}

/// This is a callback from `rustc_hir` as it cannot access the implicit state
/// in `rustc_middle` otherwise.
fn def_id_debug(def_id: rustc_hir::def_id::DefId, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Expand All @@ -70,4 +90,5 @@ pub fn setup_callbacks() {
rustc_span::SPAN_TRACK.swap(&(track_span_parent as fn(_)));
rustc_hir::def_id::DEF_ID_DEBUG.swap(&(def_id_debug as fn(_, &mut fmt::Formatter<'_>) -> _));
rustc_errors::TRACK_DIAGNOSTIC.swap(&(track_diagnostic as _));
rustc_feature::TRACK_FEATURE.swap(&(track_feature as _));
}
32 changes: 16 additions & 16 deletions compiler/rustc_lint/src/unused/must_use.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,13 +108,6 @@ impl IsTyMustUse {
_ => self,
}
}

fn yes(self) -> Option<MustUsePath> {
match self {
Self::Yes(must_use_path) => Some(must_use_path),
_ => None,
}
}
}

/// A path through a type to a `must_use` source. Contains useful info for the lint.
Expand Down Expand Up @@ -254,16 +247,23 @@ pub fn is_ty_must_use<'tcx>(
// Default to `expr`.
let elem_exprs = elem_exprs.iter().chain(iter::repeat(expr));

let nested_must_use = tys
.iter()
.zip(elem_exprs)
.enumerate()
.filter_map(|(i, (ty, expr))| {
is_ty_must_use(cx, ty, expr, simplify_uninhabited).yes().map(|path| (i, path))
})
.collect::<Vec<_>>();
let mut all_trivial = true;
let mut nested_must_use = Vec::new();

tys.iter().zip(elem_exprs).enumerate().for_each(|(i, (ty, expr))| {
let must_use = is_ty_must_use(cx, ty, expr, simplify_uninhabited);

all_trivial &= matches!(must_use, IsTyMustUse::Trivial);
if let IsTyMustUse::Yes(path) = must_use {
nested_must_use.push((i, path));
}
});

if !nested_must_use.is_empty() {
if all_trivial {
// If all tuple elements are trivial, mark the whole tuple as such.
// i.e. don't emit `unused_results` for types such as `((), ())`
IsTyMustUse::Trivial
} else if !nested_must_use.is_empty() {
IsTyMustUse::Yes(MustUsePath::TupleElement(nested_must_use))
} else {
IsTyMustUse::No
Expand Down
5 changes: 0 additions & 5 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1088,11 +1088,6 @@ declare_lint! {
/// crate-level [`feature` attributes].
///
/// [`feature` attributes]: https://doc.rust-lang.org/nightly/unstable-book/
///
/// Note: This lint is currently not functional, see [issue #44232] for
/// more details.
///
/// [issue #44232]: https://github.com/rust-lang/rust/issues/44232
pub UNUSED_FEATURES,
Warn,
"unused features found in crate-level `#[feature]` directives"
Expand Down
71 changes: 52 additions & 19 deletions compiler/rustc_macros/src/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use syn::{
};

mod kw {
syn::custom_keyword!(non_query);
syn::custom_keyword!(query);
}

Expand Down Expand Up @@ -54,12 +55,37 @@ struct Query {
modifiers: QueryModifiers,
}

impl Parse for Query {
/// Declaration of a non-query dep kind.
/// ```ignore (illustrative)
/// /// Doc comment for `MyNonQuery`.
/// // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doc_comments
/// non_query MyNonQuery
/// // ^^^^^^^^^^ name
/// ```
struct NonQuery {
doc_comments: Vec<Attribute>,
name: Ident,
}

enum QueryEntry {
Query(Query),
NonQuery(NonQuery),
}

impl Parse for QueryEntry {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let mut doc_comments = check_attributes(input.call(Attribute::parse_outer)?)?;

// Try the non-query case first.
if input.parse::<kw::non_query>().is_ok() {
let name: Ident = input.parse()?;
return Ok(QueryEntry::NonQuery(NonQuery { doc_comments, name }));
}

// Parse the query declaration. Like `query type_of(key: DefId) -> Ty<'tcx>`
input.parse::<kw::query>()?;
if input.parse::<kw::query>().is_err() {
return Err(input.error("expected `query` or `non_query`"));
}
let name: Ident = input.parse()?;

// `(key: DefId)`
Expand All @@ -84,7 +110,7 @@ impl Parse for Query {
doc_comments.push(doc_comment_from_desc(&modifiers.desc.expr_list)?);
}

Ok(Query { doc_comments, modifiers, name, key_pat, key_ty, return_ty })
Ok(QueryEntry::Query(Query { doc_comments, modifiers, name, key_pat, key_ty, return_ty }))
}
}

Expand Down Expand Up @@ -375,9 +401,9 @@ fn add_to_analyzer_stream(query: &Query, analyzer_stream: &mut proc_macro2::Toke
// macro producing a higher order macro that has all its token in the macro declaration we lose
// any meaningful spans, resulting in rust-analyzer being unable to make the connection between
// the query name and the corresponding providers field. The trick to fix this is to have
// `rustc_queries` emit a field access with the given name's span which allows it to successfully
// show references / go to definition to the corresponding provider assignment which is usually
// the more interesting place.
// `rustc_queries` emit a field access with the given name's span which allows it to
// successfully show references / go to definition to the corresponding provider assignment
// which is usually the more interesting place.
let ra_hint = quote! {
let crate::query::Providers { #name: _, .. };
};
Expand All @@ -393,9 +419,10 @@ fn add_to_analyzer_stream(query: &Query, analyzer_stream: &mut proc_macro2::Toke
}

pub(super) fn rustc_queries(input: TokenStream) -> TokenStream {
let queries = parse_macro_input!(input as List<Query>);
let queries = parse_macro_input!(input as List<QueryEntry>);

let mut query_stream = quote! {};
let mut non_query_stream = quote! {};
let mut helpers = HelperTokenStreams::default();
let mut analyzer_stream = quote! {};
let mut errors = quote! {};
Expand All @@ -411,6 +438,18 @@ pub(super) fn rustc_queries(input: TokenStream) -> TokenStream {
}

for query in queries.0 {
let query = match query {
QueryEntry::Query(query) => query,
QueryEntry::NonQuery(NonQuery { doc_comments, name }) => {
// Get the exceptional non-query case out of the way first.
non_query_stream.extend(quote! {
#(#doc_comments)*
#name,
});
continue;
}
};

let Query { doc_comments, name, key_ty, return_ty, modifiers, .. } = &query;

// Normalize an absent return type into `-> ()` to make macro-rules parsing easier.
Expand Down Expand Up @@ -486,24 +525,18 @@ pub(super) fn rustc_queries(input: TokenStream) -> TokenStream {
let HelperTokenStreams { description_fns_stream, cache_on_disk_if_fns_stream } = helpers;

TokenStream::from(quote! {
/// Higher-order macro that invokes the specified macro with a prepared
/// list of all query signatures (including modifiers).
///
/// This allows multiple simpler macros to each have access to the list
/// of queries.
/// Higher-order macro that invokes the specified macro with (a) a list of all query
/// signatures (including modifiers), and (b) a list of non-query names. This allows
/// multiple simpler macros to each have access to these lists.
#[macro_export]
macro_rules! rustc_with_all_queries {
(
// The macro to invoke once, on all queries (plus extras).
// The macro to invoke once, on all queries and non-queries.
$macro:ident!

// Within [], an optional list of extra "query" signatures to
// pass to the given macro, in addition to the actual queries.
$( [$($extra_fake_queries:tt)*] )?
) => {
$macro! {
$( $($extra_fake_queries)* )?
#query_stream
queries { #query_stream }
non_queries { #non_query_stream }
}
}
}
Expand Down
Loading
Loading