Skip to content
Merged
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
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3539,6 +3539,7 @@ dependencies = [
"rustc_abi",
"rustc_ast",
"rustc_ast_pretty",
"rustc_data_structures",
"rustc_errors",
"rustc_feature",
"rustc_hir",
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ast_lowering/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1397,7 +1397,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
// create a fake body so that the entire rest of the compiler doesn't have to deal with
// this as a special case.
return self.lower_fn_body(decl, contract, |this| {
if attrs.iter().any(|a| a.has_name(sym::rustc_intrinsic))
if find_attr!(attrs, AttributeKind::RustcIntrinsic)
|| this.tcx.is_sdylib_interface_build()
{
let span = this.lower_span(span);
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_attr_parsing/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ edition = "2024"
rustc_abi = { path = "../rustc_abi" }
rustc_ast = { path = "../rustc_ast" }
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_feature = { path = "../rustc_feature" }
rustc_hir = { path = "../rustc_hir" }
Expand Down
112 changes: 111 additions & 1 deletion compiler/rustc_attr_parsing/src/attributes/cfg_select.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,35 @@
use rustc_ast::token::Token;
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::{AttrStyle, NodeId, token};
use rustc_data_structures::fx::FxHashMap;
use rustc_feature::{AttributeTemplate, Features};
use rustc_hir::attrs::CfgEntry;
use rustc_hir::{AttrPath, Target};
use rustc_parse::exp;
use rustc_parse::parser::{Parser, Recovery};
use rustc_session::Session;
use rustc_span::{ErrorGuaranteed, Span, sym};
use rustc_session::lint::BuiltinLintDiag;
use rustc_session::lint::builtin::UNREACHABLE_CFG_SELECT_PREDICATES;
use rustc_span::{ErrorGuaranteed, Span, Symbol, sym};

use crate::parser::MetaItemOrLitParser;
use crate::{AttributeParser, ParsedDescription, ShouldEmit, parse_cfg_entry};

#[derive(Clone)]
pub enum CfgSelectPredicate {
Cfg(CfgEntry),
Wildcard(Token),
}

impl CfgSelectPredicate {
fn span(&self) -> Span {
match self {
CfgSelectPredicate::Cfg(cfg_entry) => cfg_entry.span(),
CfgSelectPredicate::Wildcard(token) => token.span,
}
}
}

#[derive(Default)]
pub struct CfgSelectBranches {
/// All the conditional branches.
Expand Down Expand Up @@ -115,5 +128,102 @@ pub fn parse_cfg_select(
}
}

if let Some(features) = features
&& features.enabled(sym::cfg_select)
{
let it = branches
.reachable
.iter()
.map(|(entry, _, _)| CfgSelectPredicate::Cfg(entry.clone()))
.chain(branches.wildcard.as_ref().map(|(t, _, _)| CfgSelectPredicate::Wildcard(*t)))
.chain(
branches.unreachable.iter().map(|(entry, _, _)| CfgSelectPredicate::clone(entry)),
);

lint_unreachable(p, it, lint_node_id);
}

Ok(branches)
}

fn lint_unreachable(
p: &mut Parser<'_>,
predicates: impl Iterator<Item = CfgSelectPredicate>,
lint_node_id: NodeId,
) {
// Symbols that have a known value.
let mut known = FxHashMap::<Symbol, bool>::default();
let mut wildcard_span = None;
let mut it = predicates;

let branch_is_unreachable = |predicate: CfgSelectPredicate, wildcard_span| {
let span = predicate.span();
p.psess.buffer_lint(
UNREACHABLE_CFG_SELECT_PREDICATES,
span,
lint_node_id,
BuiltinLintDiag::UnreachableCfg { span, wildcard_span },
);
};

for predicate in &mut it {
let CfgSelectPredicate::Cfg(ref cfg_entry) = predicate else {
wildcard_span = Some(predicate.span());
break;
};

match cfg_entry {
CfgEntry::Bool(true, _) => {
wildcard_span = Some(predicate.span());
break;
}
CfgEntry::Bool(false, _) => continue,
CfgEntry::NameValue { name, value, .. } => match value {
None => {
// `name` will be false in all subsequent branches.
let current = known.insert(*name, false);

match current {
None => continue,
Some(false) => {
branch_is_unreachable(predicate, None);
break;
}
Some(true) => {
// this branch will be taken, so all subsequent branches are unreachable.
break;
}
}
}
Some(_) => { /* for now we don't bother solving these */ }
},
CfgEntry::Not(inner, _) => match &**inner {
CfgEntry::NameValue { name, value: None, .. } => {
// `name` will be true in all subsequent branches.
let current = known.insert(*name, true);

match current {
None => continue,
Some(true) => {
branch_is_unreachable(predicate, None);
break;
}
Some(false) => {
// this branch will be taken, so all subsequent branches are unreachable.
break;
}
}
}
_ => { /* for now we don't bother solving these */ }
},
CfgEntry::All(_, _) | CfgEntry::Any(_, _) => {
/* for now we don't bother solving these */
}
CfgEntry::Version(..) => { /* don't bother solving these */ }
}
}

for predicate in it {
branch_is_unreachable(predicate, wildcard_span)
}
}
19 changes: 19 additions & 0 deletions compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,7 @@ impl<S: Stage> CombineAttributeParser<S> for RustcMirParser {
.collect()
}
}

pub(crate) struct RustcNonConstTraitMethodParser;

impl<S: Stage> NoArgsAttributeParser<S> for RustcNonConstTraitMethodParser {
Expand Down Expand Up @@ -810,3 +811,21 @@ impl<S: Stage> SingleAttributeParser<S> for RustcDefPath {
Some(AttributeKind::RustcDefPath(cx.attr_span))
}
}

pub(crate) struct RustcIntrinsicParser;

impl<S: Stage> NoArgsAttributeParser<S> for RustcIntrinsicParser {
const PATH: &[Symbol] = &[sym::rustc_intrinsic];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcIntrinsic;
}

pub(crate) struct RustcIntrinsicConstStableIndirectParser;

impl<S: Stage> NoArgsAttributeParser<S> for RustcIntrinsicConstStableIndirectParser {
const PATH: &'static [Symbol] = &[sym::rustc_intrinsic_const_stable_indirect];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcIntrinsicConstStableIndirect;
}
2 changes: 2 additions & 0 deletions compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,8 @@ attribute_parsers!(
Single<WithoutArgs<RustcEffectiveVisibilityParser>>,
Single<WithoutArgs<RustcHasIncoherentInherentImplsParser>>,
Single<WithoutArgs<RustcHiddenTypeOfOpaquesParser>>,
Single<WithoutArgs<RustcIntrinsicConstStableIndirectParser>>,
Single<WithoutArgs<RustcIntrinsicParser>>,
Single<WithoutArgs<RustcLintOptTyParser>>,
Single<WithoutArgs<RustcLintQueryInstabilityParser>>,
Single<WithoutArgs<RustcLintUntrackedQueryInformationParser>>,
Expand Down
18 changes: 2 additions & 16 deletions compiler/rustc_builtin_macros/src/cfg_select.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::{Expr, ast};
use rustc_attr_parsing as attr;
use rustc_attr_parsing::{
CfgSelectBranches, CfgSelectPredicate, EvalConfigResult, parse_cfg_select,
};
use rustc_attr_parsing::{CfgSelectBranches, EvalConfigResult, parse_cfg_select};
use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacResult, MacroExpanderResult};
use rustc_span::{Ident, Span, sym};
use smallvec::SmallVec;

use crate::errors::{CfgSelectNoMatches, CfgSelectUnreachable};
use crate::errors::CfgSelectNoMatches;

/// This intermediate structure is used to emit parse errors for the branches that are not chosen.
/// The `MacResult` instance below parses all branches, emitting any errors it encounters, but only
Expand Down Expand Up @@ -75,18 +73,6 @@ pub(super) fn expand_cfg_select<'cx>(
ecx.current_expansion.lint_node_id,
) {
Ok(mut branches) => {
if let Some((underscore, _, _)) = branches.wildcard {
// Warn for every unreachable predicate. We store the fully parsed branch for rustfmt.
for (predicate, _, _) in &branches.unreachable {
let span = match predicate {
CfgSelectPredicate::Wildcard(underscore) => underscore.span,
CfgSelectPredicate::Cfg(cfg) => cfg.span(),
};
let err = CfgSelectUnreachable { span, wildcard_span: underscore.span };
ecx.dcx().emit_warn(err);
}
}

if let Some((selected_tts, selected_span)) = branches.pop_first_match(|cfg| {
matches!(attr::eval_config_entry(&ecx.sess, cfg), EvalConfigResult::True)
}) {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,7 @@ impl<'a> TraitDef<'a> {
.filter(|a| {
a.has_any_name(&[
sym::allow,
sym::expect,
sym::warn,
sym::deny,
sym::forbid,
Expand Down
11 changes: 0 additions & 11 deletions compiler/rustc_builtin_macros/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1086,17 +1086,6 @@ pub(crate) struct CfgSelectNoMatches {
pub span: Span,
}

#[derive(Diagnostic)]
#[diag("unreachable predicate")]
pub(crate) struct CfgSelectUnreachable {
#[primary_span]
#[label("this predicate is never reached")]
pub span: Span,

#[label("always matches")]
pub wildcard_span: Span,
}

#[derive(Diagnostic)]
#[diag("`#[eii_declaration(...)]` is only valid on macros")]
pub(crate) struct EiiExternTargetExpectedMacro {
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,8 @@ declare_features! (
(unstable, cfg_sanitize, "1.41.0", Some(39699)),
/// Allows `cfg(sanitizer_cfi_generalize_pointers)` and `cfg(sanitizer_cfi_normalize_integers)`.
(unstable, cfg_sanitizer_cfi, "1.77.0", Some(89653)),
/// Provides a native way to easily manage multiple conditional flags without having to rewrite each clause multiple times.
(unstable, cfg_select, "CURRENT_RUSTC_VERSION", Some(115585)),
/// Allows `cfg(target(abi = "..."))`.
(unstable, cfg_target_compact, "1.63.0", Some(96901)),
/// Allows `cfg(target_has_atomic_load_store = "...")`.
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_hir/src/attrs/data_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1120,6 +1120,12 @@ pub enum AttributeKind {
/// Represents `#[rustc_if_this_changed]`
RustcIfThisChanged(Span, Option<Symbol>),

/// Represents `#[rustc_intrinsic]`
RustcIntrinsic,

/// Represents `#[rustc_intrinsic_const_stable_indirect]`
RustcIntrinsicConstStableIndirect,

/// Represents `#[rustc_layout]`
RustcLayout(ThinVec<RustcLayoutType>),

Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_hir/src/attrs/encode_cross_crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ impl AttributeKind {
RustcHasIncoherentInherentImpls => Yes,
RustcHiddenTypeOfOpaques => No,
RustcIfThisChanged(..) => No,
RustcIntrinsic => Yes,
RustcIntrinsicConstStableIndirect => No,
RustcLayout(..) => No,
RustcLayoutScalarValidRangeEnd(..) => Yes,
RustcLayoutScalarValidRangeStart(..) => Yes,
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_lint/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,13 @@ lint_unpredictable_fn_pointer_comparisons = function pointer comparisons do not

lint_unqualified_local_imports = `use` of a local item without leading `self::`, `super::`, or `crate::`

lint_unreachable_cfg_select_predicate = unreachable configuration predicate
.label = this configuration predicate is never reached

lint_unreachable_cfg_select_predicate_wildcard = unreachable configuration predicate
.label = always matches
.label2 = this configuration predicate is never reached

lint_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe
.label = usage of unsafe attribute
lint_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)`
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_lint/src/early/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,14 @@ pub fn decorate_builtin_lint(
}
.decorate_lint(diag);
}
BuiltinLintDiag::UnreachableCfg { span, wildcard_span } => match wildcard_span {
Some(wildcard_span) => {
lints::UnreachableCfgSelectPredicateWildcard { span, wildcard_span }
.decorate_lint(diag)
}
None => lints::UnreachableCfgSelectPredicate { span }.decorate_lint(diag),
},

BuiltinLintDiag::UnusedCrateDependency { extern_crate, local_crate } => {
lints::UnusedCrateDependency { extern_crate, local_crate }.decorate_lint(diag)
}
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_lint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,9 @@ fn register_builtins(store: &mut LintStore) {
UNUSED_ASSIGNMENTS,
DEAD_CODE,
UNUSED_MUT,
// FIXME: add this lint when it becomes stable,
// see https://github.com/rust-lang/rust/issues/115585.
// UNREACHABLE_CFG_SELECT_PREDICATES,
UNREACHABLE_CODE,
UNREACHABLE_PATTERNS,
UNUSED_MUST_USE,
Expand Down
17 changes: 17 additions & 0 deletions compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3340,3 +3340,20 @@ pub(crate) struct UnknownCrateTypesSuggestion {
pub span: Span,
pub snippet: Symbol,
}

#[derive(LintDiagnostic)]
#[diag(lint_unreachable_cfg_select_predicate)]
pub(crate) struct UnreachableCfgSelectPredicate {
#[label]
pub span: Span,
}

#[derive(LintDiagnostic)]
#[diag(lint_unreachable_cfg_select_predicate_wildcard)]
pub(crate) struct UnreachableCfgSelectPredicateWildcard {
#[label(lint_label2)]
pub span: Span,

#[label]
pub wildcard_span: Span,
}
29 changes: 29 additions & 0 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ declare_lint_pass! {
UNKNOWN_LINTS,
UNNAMEABLE_TEST_ITEMS,
UNNAMEABLE_TYPES,
UNREACHABLE_CFG_SELECT_PREDICATES,
UNREACHABLE_CODE,
UNREACHABLE_PATTERNS,
UNSAFE_ATTR_OUTSIDE_UNSAFE,
Expand Down Expand Up @@ -855,6 +856,34 @@ declare_lint! {
"detects unreachable patterns"
}

declare_lint! {
/// The `unreachable_cfg_select_predicates` lint detects unreachable configuration
/// predicates in the `cfg_select!` macro.
///
/// ### Example
///
/// ```rust
/// #![feature(cfg_select)]
/// cfg_select! {
/// _ => (),
/// windows => (),
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// This usually indicates a mistake in how the predicates are specified or
/// ordered. In this example, the `_` predicate will always match, so the
/// `windows` is impossible to reach. Remember, arms match in order, you
/// probably wanted to put the `windows` case above the `_` case.
pub UNREACHABLE_CFG_SELECT_PREDICATES,
Warn,
"detects unreachable configuration predicates in the cfg_select macro",
@feature_gate = cfg_select;
}

declare_lint! {
/// The `overlapping_range_endpoints` lint detects `match` arms that have [range patterns] that
/// overlap on their endpoints.
Expand Down
Loading
Loading