Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
b11a76b
tests: modify s390x vector test to be robust to instruction scheduling
durin42 Jun 23, 2026
810d2db
Update `doc_cfg` hide/show syntax
GuillaumeGomez May 25, 2026
dc7a2ce
Update `alloc` and `core` to new `doc_cfg` syntax
GuillaumeGomez Jun 11, 2026
f22107c
Update rustdoc to use new `doc_cfg` syntax
GuillaumeGomez Jun 12, 2026
3c92a3c
Move `doc-cfg` tests into the right location
GuillaumeGomez May 20, 2026
c383e5c
Add more regression tests for `doc(auto_cfg())`
GuillaumeGomez Jun 13, 2026
a3ad068
Update syntax information for `doc(auto_cfg(hide/show()))` in the rus…
GuillaumeGomez Jun 13, 2026
9653fd0
Add rustdoc-json test for `doc_cfg`
GuillaumeGomez Jun 17, 2026
702445c
Remove doc_cfg show/hide conflict error and correctly handle `values(…
GuillaumeGomez Jun 24, 2026
247e6e1
Add missing links in integer docs
GuillaumeGomez Jun 26, 2026
87f3254
Cross-referencing tuple_trait tracking issue, source and the Unstable…
peter-lyons-kehl Jun 22, 2026
e7da124
Improve documentation on lint passes
nnethercote Jun 15, 2026
e983dd7
Fix a comment
nnethercote Jun 16, 2026
969db62
Rename `*CombinedModuleLateLintPass`
nnethercote Jun 18, 2026
cd8d703
Rename some lint pass things
nnethercote Jun 18, 2026
879aba1
Make `LintStore::*_lint_passes` non-`pub`
nnethercote Jun 18, 2026
67f85db
Add proc macro for unused assignments and corresponding test
chenyukang Jun 27, 2026
480ddb0
Add regression test for unexpected pointer dereference issue
chenyukang Jun 27, 2026
4bbd8fc
Use infer tys for synthetic params when lowering const paths point to…
mu001999 Jun 8, 2026
1af87c1
Fix doc comment on get_debug_as_hex.
m-ou-se Jun 27, 2026
36cd136
Fix doc comment on FormattingOptions::new().
m-ou-se Jun 27, 2026
74830a7
Move `check_target_feature` into the attribute parser
obeis Jun 23, 2026
fc35c23
add crashtests
cyrgani Jun 23, 2026
cf443cb
add smoketest for std::net::hostname
RalfJung Jun 27, 2026
eb804b3
Rollup merge of #157871 - GuillaumeGomez:doc_cfg-syntax, r=Urgau
JonathanBrouwer Jun 27, 2026
20ca3e1
Rollup merge of #158234 - peter-lyons-kehl:unstable_book_tuple_trait,…
JonathanBrouwer Jun 27, 2026
6208d47
Rollup merge of #158480 - RalfJung:hostname-test, r=joboet
JonathanBrouwer Jun 27, 2026
ad69585
Rollup merge of #157625 - mu001999:fix-155834, r=JohnTitor
JonathanBrouwer Jun 27, 2026
dd01a7d
Rollup merge of #158290 - cyrgani:tests-incoming, r=nnethercote
JonathanBrouwer Jun 27, 2026
e2c515a
Rollup merge of #158306 - durin42:llvm-23-s390x-vec, r=JohnTitor
JonathanBrouwer Jun 27, 2026
ee6105a
Rollup merge of #158313 - obeis:move-check_target_feature, r=Jonathan…
JonathanBrouwer Jun 27, 2026
9b15098
Rollup merge of #158431 - nnethercote:more-lint-cleanups, r=Urgau
JonathanBrouwer Jun 27, 2026
522f803
Rollup merge of #158452 - GuillaumeGomez:missing-links, r=JohnTitor
JonathanBrouwer Jun 27, 2026
45a870a
Rollup merge of #158467 - chenyukang:yukang-rework-on-fix-unused_assi…
JonathanBrouwer Jun 27, 2026
0cad03c
Rollup merge of #158472 - chenyukang:yukang-fix-154568-unexpected-poi…
JonathanBrouwer Jun 27, 2026
30cb835
Rollup merge of #158475 - m-ou-se:fix-doc-comment, r=JonathanBrouwer
JonathanBrouwer Jun 27, 2026
a85522c
Rollup merge of #158476 - m-ou-se:doc-comment-fmt-options-new, r=Jona…
JonathanBrouwer Jun 27, 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
34 changes: 24 additions & 10 deletions compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::attributes::AttributeSafety;
use crate::session_diagnostics::{
EmptyExportName, NakedFunctionIncompatibleAttribute, NullOnExport, NullOnObjcClass,
NullOnObjcSelector, ObjcClassExpectedStringLiteral, ObjcSelectorExpectedStringLiteral,
SanitizeInvalidStatic,
SanitizeInvalidStatic, TargetFeatureOnLangItem,
};
use crate::target_checking::Policy::AllowSilent;

Expand Down Expand Up @@ -524,15 +524,6 @@ impl CombineAttributeParser for TargetFeatureParser {
was_forced: false,
};
const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);
const STABILITY: AttributeStability = AttributeStability::Stable;

fn extend(
cx: &mut AcceptContext<'_, '_>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item> {
parse_tf_attribute(cx, args)
}

const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
Allow(Target::Fn),
Allow(Target::Method(MethodKind::Inherent)),
Expand All @@ -544,6 +535,29 @@ impl CombineAttributeParser for TargetFeatureParser {
Warn(Target::MacroDef),
Warn(Target::MacroCall),
]);
const STABILITY: AttributeStability = AttributeStability::Stable;

fn extend(
cx: &mut AcceptContext<'_, '_>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item> {
parse_tf_attribute(cx, args)
}

fn finalize_check(cx: &FinalizeContext<'_, '_>, attr_span: Span) {
// `#[target_feature]` is incompatible with lang item functions,
// except on WASM where calling target-feature functions is safe (see #84988).
if !cx.sess().target.is_like_wasm && !cx.sess().opts.actually_rustdoc {
// `#[panic_handler]` is checked first so it takes priority in the diagnostic.
let lang_kind = cx
.all_attrs
.iter()
.find_map(|a| [sym::panic_handler, sym::lang].into_iter().find(|&s| a.word_is(s)));
if let Some(kind) = lang_kind {
cx.emit_err(TargetFeatureOnLangItem { attr_span, kind, item_span: cx.target_span });
}
}
}
}

pub(crate) struct ForceTargetFeatureParser;
Expand Down
170 changes: 133 additions & 37 deletions compiler/rustc_attr_parsing/src/attributes/doc.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
use rustc_ast::ast::{AttrStyle, LitKind, MetaItemLit};
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, IndexEntry};
use rustc_errors::{Applicability, msg};
use rustc_feature::AttributeStability;
use rustc_hir::Target;
use rustc_hir::attrs::{
AttributeKind, CfgEntry, CfgHideShow, CfgInfo, DocAttribute, DocInline, HideOrShow,
AttributeKind, CfgEntry, CfgHideShow, DocAttribute, DocCfgHideShow, DocCfgHideShowValue,
DocInline, HideOrShow,
};
use rustc_session::errors::feature_err;
use rustc_span::{Span, Symbol, edition, sym};
use thin_vec::ThinVec;

use super::prelude::{ALL_TARGETS, AllowedTargets};
use super::{AcceptMapping, AttributeParser, template};
use crate::context::{AcceptContext, FinalizeContext};
use crate::diagnostics::{
AttrCrateLevelOnly, DocAliasDuplicated, DocAutoCfgExpectsHideOrShow,
DocAutoCfgHideShowExpectsList, DocAutoCfgHideShowUnexpectedItem, DocAutoCfgWrongLiteral,
DocTestLiteral, DocTestTakesList, DocTestUnknown, DocUnknownAny, DocUnknownInclude,
DocUnknownPasses, DocUnknownPlugins, DocUnknownSpotlight, ExpectedNameValue, ExpectedNoArgs,
IllFormedAttributeInput, MalformedDoc,
DocAutoCfgHideShowExpectsList, DocAutoCfgHideShowNoIdentBeforeValues,
DocAutoCfgHideShowUnexpectedItem, DocAutoCfgHideShowUnexpectedItemAfterValues,
DocAutoCfgHideShowValuesMix, DocAutoCfgWrongLiteral, DocTestLiteral, DocTestTakesList,
DocTestUnknown, DocUnknownAny, DocUnknownInclude, DocUnknownPasses, DocUnknownPlugins,
DocUnknownSpotlight, ExpectedNameValue, ExpectedNoArgs, IllFormedAttributeInput, MalformedDoc,
};
use crate::parser::{
ArgParser, MetaItemListParser, MetaItemOrLitParser, MetaItemParser, OwnedPathParser,
};
use crate::parser::{ArgParser, MetaItemOrLitParser, MetaItemParser, OwnedPathParser};
use crate::session_diagnostics::{
DocAliasBadChar, DocAliasEmpty, DocAliasMalformed, DocAliasStartEnd, DocAttrNotCrateLevel,
DocAttributeNotAttribute, DocKeywordNotKeyword, UnusedDuplicate,
Expand Down Expand Up @@ -304,6 +308,81 @@ impl DocParser {
}
}

// Parses the `doc(auto_cfg(hide/show(..., values())))` attribute.
fn parse_auto_cfg_values(
&self,
cx: &mut AcceptContext<'_, '_>,
list: &MetaItemListParser,
values: &mut Option<DocCfgHideShow>,
) {
let mut cfg_values = DocCfgHideShow::new();

let mut values_set = FxHashSet::default();
for item in list.mixed() {
match item {
// If it's a string literal, all good.
MetaItemOrLitParser::Lit(MetaItemLit {
kind: LitKind::Str(symbol, _),
span,
..
}) => match &mut cfg_values {
DocCfgHideShow::Any(any_span) => {
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
DocAutoCfgHideShowValuesMix { value_span: *span },
*any_span,
);
}
DocCfgHideShow::List(symbols) => {
if values_set.insert(symbol) {
symbols.push(DocCfgHideShowValue::new(*symbol, *span));
}
}
},
// If it's any other kind of literal, then it's wrong and we emit a lint.
MetaItemOrLitParser::Lit(lit) => cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
DocAutoCfgHideShowUnexpectedItem { attr_name: lit.symbol },
lit.span,
),
// If it's a list, then only `any()` and `none()` are allowed and they must not
// contain any item.
MetaItemOrLitParser::MetaItemParser(sub_item) => {
if let Some(ident) = sub_item.ident()
&& [sym::any, sym::none].contains(&ident.name)
&& let ArgParser::List(list) = sub_item.args()
&& list.mixed().count() == 0
{
if ident.name == sym::any {
if let DocCfgHideShow::List(values) = &cfg_values
&& let Some(value) = values.first()
{
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
DocAutoCfgHideShowValuesMix { value_span: value.span },
sub_item.span(),
);
} else {
cfg_values.merge_with(&DocCfgHideShow::Any(sub_item.span()));
}
} else {
cfg_values.push_none(sub_item.span());
}
} else {
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
DocAutoCfgHideShowUnexpectedItem {
attr_name: sub_item.ident().unwrap().name,
},
sub_item.span(),
);
}
}
}
}
*values = Some(cfg_values);
}

fn parse_auto_cfg(
&mut self,
cx: &mut AcceptContext<'_, '_>,
Expand All @@ -315,7 +394,7 @@ impl DocParser {
self.attribute.auto_cfg_change.push((true, path.span()));
}
ArgParser::List(list) => {
for meta in list.mixed() {
'main: for meta in list.mixed() {
let MetaItemOrLitParser::MetaItemParser(item) = meta else {
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
Expand All @@ -324,6 +403,8 @@ impl DocParser {
);
continue;
};
// Only `hide` and `show` are allowed in `auto_cfg` if it's a list, and both
// must be a list.
let (kind, attr_name) = match item.path().word_sym() {
Some(sym::hide) => (HideOrShow::Hide, sym::hide),
Some(sym::show) => (HideOrShow::Show, sym::show),
Expand All @@ -345,56 +426,71 @@ impl DocParser {
continue;
};

let mut cfg_hide_show = CfgHideShow { kind, values: ThinVec::new() };
let mut cfg_hide_show = CfgHideShow { kind, values: FxIndexMap::default() };

let mut cfg_names = FxHashSet::default();
let mut values = None;
for item in list.mixed() {
let MetaItemOrLitParser::MetaItemParser(sub_item) = item else {
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
DocAutoCfgHideShowUnexpectedItem { attr_name },
item.span(),
);
continue;
continue 'main;
};
match sub_item.args() {
a @ (ArgParser::NoArgs | ArgParser::NameValue(_)) => {
ArgParser::NoArgs if values.is_none() => {
let Some(name) = sub_item.path().word_sym() else {
// FIXME: remove this method once merged and uncomment the line
// below instead.
// cx.expected_identifier(sub_item.path().span());
cx.adcx().expected_identifier(sub_item.path().span());
continue 'main;
};
cfg_names.insert(name);
}
// The only accepted list is `values()`.
ArgParser::List(list) if values.is_none() => {
let Some(sym::values) = sub_item.path().word_sym() else {
cx.adcx().expected_identifier(sub_item.path().span());
continue 'main;
};
if cfg_names.is_empty() {
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
MalformedDoc,
sub_item.path().span(),
);
continue;
};
if let Ok(CfgEntry::NameValue { name, value, .. }) =
super::cfg::parse_name_value(
name,
sub_item.path().span(),
a.as_name_value(),
DocAutoCfgHideShowNoIdentBeforeValues,
sub_item.span(),
cx,
)
{
cfg_hide_show.values.push(CfgInfo {
name,
name_span: sub_item.path().span(),
// If `value` is `Some`, `a.name_value()` will always return
// `Some` as well.
value: value
.map(|v| (v, a.as_name_value().unwrap().value_span)),
})
);
continue 'main;
}
self.parse_auto_cfg_values(cx, list, &mut values);
}
_ => {
// No `name = value` is allowed.
ArgParser::NameValue(_) => {
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
DocAutoCfgHideShowUnexpectedItem { attr_name },
sub_item.span(),
);
continue;
}
// If `values()` was already used, no item should come after it.
_ => {
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
DocAutoCfgHideShowUnexpectedItemAfterValues,
sub_item.span(),
);
}
}
}

let values = values.unwrap_or(DocCfgHideShow::new_with_only_key(item.span()));
#[allow(rustc::potential_query_instability)]
for cfg_name in &cfg_names {
match cfg_hide_show.values.entry(*cfg_name) {
IndexEntry::Vacant(v) => {
v.insert(values.clone());
}
IndexEntry::Occupied(mut o) => {
o.get_mut().merge_with(&values);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_attr_parsing/src/attributes/link_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,7 @@ impl NoArgsAttributeParser for FfiPureParser {
const STABILITY: AttributeStability = unstable!(ffi_pure);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::FfiPure;

fn finalize_check(attr_span: Span, cx: &FinalizeContext<'_, '_>) {
fn finalize_check(cx: &FinalizeContext<'_, '_>, attr_span: Span) {
// `#[ffi_const]` functions cannot be `#[ffi_pure]`.
if cx.all_attrs.iter().any(|a| a.word_is(sym::ffi_const)) {
cx.emit_err(BothFfiConstAndPure { attr_span });
Expand Down
21 changes: 15 additions & 6 deletions compiler/rustc_attr_parsing/src/attributes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ pub(crate) trait SingleAttributeParser: 'static {
/// reject incompatible combinations. `attr_span` is the span of this attribute.
///
/// Defaults to a no-op.
fn finalize_check(_attr_span: Span, _cx: &FinalizeContext<'_, '_>) {}
fn finalize_check(_cx: &FinalizeContext<'_, '_>, _attr_span: Span) {}
}

/// Use in combination with [`SingleAttributeParser`].
Expand Down Expand Up @@ -187,7 +187,7 @@ impl<T: SingleAttributeParser> AttributeParser for Single<T> {

fn finalize(self, cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
let (kind, span) = self.1?;
T::finalize_check(span, cx);
T::finalize_check(cx, span);
Some(kind)
}
}
Expand Down Expand Up @@ -275,7 +275,7 @@ pub(crate) trait NoArgsAttributeParser: 'static {
/// `attr_span` is the span of this attribute.
///
/// Defaults to a no-op.
fn finalize_check(_attr_span: Span, _cx: &FinalizeContext<'_, '_>) {}
fn finalize_check(_cx: &FinalizeContext<'_, '_>, _attr_span: Span) {}
}

pub(crate) struct WithoutArgs<T: NoArgsAttributeParser>(PhantomData<T>);
Expand All @@ -299,8 +299,8 @@ impl<T: NoArgsAttributeParser> SingleAttributeParser for WithoutArgs<T> {
Some(T::CREATE(cx.attr_span))
}

fn finalize_check(attr_span: Span, cx: &FinalizeContext<'_, '_>) {
T::finalize_check(attr_span, cx)
fn finalize_check(cx: &FinalizeContext<'_, '_>, attr_span: Span) {
T::finalize_check(cx, attr_span)
}
}

Expand Down Expand Up @@ -335,6 +335,14 @@ pub(crate) trait CombineAttributeParser: 'static {
cx: &mut AcceptContext<'_, '_>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item>;

/// Optional cross-attribute validation, run once during finalization after all
/// attributes on the item have been parsed. Has access to the sibling attributes via
/// [`FinalizeContext::all_attrs`], so it can reject incompatible combinations.
/// `attr_span` is the span of the first attribute that was encountered.
///
/// Defaults to a no-op.
fn finalize_check(_cx: &FinalizeContext<'_, '_>, _attr_span: Span) {}
}

/// Use in combination with [`CombineAttributeParser`].
Expand Down Expand Up @@ -367,8 +375,9 @@ impl<T: CombineAttributeParser> AttributeParser for Combine<T> {
const ALLOWED_TARGETS: AllowedTargets<'_> = T::ALLOWED_TARGETS;
const SAFETY: AttributeSafety = T::SAFETY;

fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
fn finalize(self, cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
if let Some(first_span) = self.first_span {
T::finalize_check(cx, first_span);
Some(T::CONVERT(self.items, first_span))
} else {
None
Expand Down
Loading
Loading