Merge ref 'dc47a69ed9' from rust-lang/rust
Pull recent changes from https://github.com/rust-lang/rust via Josh. Upstream ref:dc47a69ed9Filtered ref: c2685dab4096194918f1cb9c503817d592c20e94 Upstream diff:f520900083...dc47a69ed9This merge was created using https://github.com/rust-lang/josh-sync.
This commit is contained in:
commit
96f2d590a7
416 changed files with 6618 additions and 5297 deletions
1
.mailmap
1
.mailmap
|
|
@ -431,6 +431,7 @@ Lzu Tao <taolzu@gmail.com>
|
|||
Maik Klein <maikklein@googlemail.com>
|
||||
Maja Kądziołka <maya@compilercrim.es> <github@compilercrim.es>
|
||||
Maja Kądziołka <maya@compilercrim.es> <kuba@kadziolka.net>
|
||||
Makai <m4kai410@gmail.com>
|
||||
Malo Jaffré <jaffre.malo@gmail.com>
|
||||
Manish Goregaokar <manishsmail@gmail.com>
|
||||
Mara Bos <m-ou.se@m-ou.se>
|
||||
|
|
|
|||
|
|
@ -4011,7 +4011,6 @@ dependencies = [
|
|||
"itertools",
|
||||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_attr_parsing",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_fluent_macro",
|
||||
|
|
@ -4433,7 +4432,6 @@ dependencies = [
|
|||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_ast_lowering",
|
||||
"rustc_ast_pretty",
|
||||
"rustc_attr_parsing",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
|
|
@ -4870,6 +4868,7 @@ dependencies = [
|
|||
"indexmap",
|
||||
"itertools",
|
||||
"minifier",
|
||||
"proc-macro2",
|
||||
"pulldown-cmark-escape",
|
||||
"regex",
|
||||
"rustdoc-json-types",
|
||||
|
|
|
|||
|
|
@ -141,16 +141,11 @@ impl Path {
|
|||
/// Check if this path is potentially a trivial const arg, i.e., one that can _potentially_
|
||||
/// be represented without an anon const in the HIR.
|
||||
///
|
||||
/// If `allow_mgca_arg` is true (as should be the case in most situations when
|
||||
/// `#![feature(min_generic_const_args)]` is enabled), then this always returns true
|
||||
/// because all paths are valid.
|
||||
///
|
||||
/// Otherwise, it returns true iff the path has exactly one segment, and it has no generic args
|
||||
/// Returns true iff the path has exactly one segment, and it has no generic args
|
||||
/// (i.e., it is _potentially_ a const parameter).
|
||||
#[tracing::instrument(level = "debug", ret)]
|
||||
pub fn is_potential_trivial_const_arg(&self, allow_mgca_arg: bool) -> bool {
|
||||
allow_mgca_arg
|
||||
|| self.segments.len() == 1 && self.segments.iter().all(|seg| seg.args.is_none())
|
||||
pub fn is_potential_trivial_const_arg(&self) -> bool {
|
||||
self.segments.len() == 1 && self.segments.iter().all(|seg| seg.args.is_none())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1385,6 +1380,15 @@ pub enum UnsafeSource {
|
|||
UserProvided,
|
||||
}
|
||||
|
||||
/// Track whether under `feature(min_generic_const_args)` this anon const
|
||||
/// was explicitly disambiguated as an anon const or not through the use of
|
||||
/// `const { ... }` syntax.
|
||||
#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy, Walkable)]
|
||||
pub enum MgcaDisambiguation {
|
||||
AnonConst,
|
||||
Direct,
|
||||
}
|
||||
|
||||
/// A constant (expression) that's not an item or associated item,
|
||||
/// but needs its own `DefId` for type-checking, const-eval, etc.
|
||||
/// These are usually found nested inside types (e.g., array lengths)
|
||||
|
|
@ -1394,6 +1398,7 @@ pub enum UnsafeSource {
|
|||
pub struct AnonConst {
|
||||
pub id: NodeId,
|
||||
pub value: Box<Expr>,
|
||||
pub mgca_disambiguation: MgcaDisambiguation,
|
||||
}
|
||||
|
||||
/// An expression.
|
||||
|
|
@ -1412,26 +1417,20 @@ impl Expr {
|
|||
///
|
||||
/// This will unwrap at most one block level (curly braces). After that, if the expression
|
||||
/// is a path, it mostly dispatches to [`Path::is_potential_trivial_const_arg`].
|
||||
/// See there for more info about `allow_mgca_arg`.
|
||||
///
|
||||
/// The only additional thing to note is that when `allow_mgca_arg` is false, this function
|
||||
/// will only allow paths with no qself, before dispatching to the `Path` function of
|
||||
/// the same name.
|
||||
/// This function will only allow paths with no qself, before dispatching to the `Path`
|
||||
/// function of the same name.
|
||||
///
|
||||
/// Does not ensure that the path resolves to a const param/item, the caller should check this.
|
||||
/// This also does not consider macros, so it's only correct after macro-expansion.
|
||||
pub fn is_potential_trivial_const_arg(&self, allow_mgca_arg: bool) -> bool {
|
||||
pub fn is_potential_trivial_const_arg(&self) -> bool {
|
||||
let this = self.maybe_unwrap_block();
|
||||
if allow_mgca_arg {
|
||||
matches!(this.kind, ExprKind::Path(..))
|
||||
if let ExprKind::Path(None, path) = &this.kind
|
||||
&& path.is_potential_trivial_const_arg()
|
||||
{
|
||||
true
|
||||
} else {
|
||||
if let ExprKind::Path(None, path) = &this.kind
|
||||
&& path.is_potential_trivial_const_arg(allow_mgca_arg)
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,9 @@ use crate::ast::{
|
|||
Expr, ExprKind, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NormalAttr, Path,
|
||||
PathSegment, Safety,
|
||||
};
|
||||
use crate::token::{self, CommentKind, Delimiter, InvisibleOrigin, MetaVarKind, Token};
|
||||
use crate::token::{
|
||||
self, CommentKind, Delimiter, DocFragmentKind, InvisibleOrigin, MetaVarKind, Token,
|
||||
};
|
||||
use crate::tokenstream::{
|
||||
DelimSpan, LazyAttrTokenStream, Spacing, TokenStream, TokenStreamIter, TokenTree,
|
||||
};
|
||||
|
|
@ -179,15 +181,21 @@ impl AttributeExt for Attribute {
|
|||
}
|
||||
|
||||
/// Returns the documentation and its kind if this is a doc comment or a sugared doc comment.
|
||||
/// * `///doc` returns `Some(("doc", CommentKind::Line))`.
|
||||
/// * `/** doc */` returns `Some(("doc", CommentKind::Block))`.
|
||||
/// * `#[doc = "doc"]` returns `Some(("doc", CommentKind::Line))`.
|
||||
/// * `///doc` returns `Some(("doc", DocFragmentKind::Sugared(CommentKind::Line)))`.
|
||||
/// * `/** doc */` returns `Some(("doc", DocFragmentKind::Sugared(CommentKind::Block)))`.
|
||||
/// * `#[doc = "doc"]` returns `Some(("doc", DocFragmentKind::Raw))`.
|
||||
/// * `#[doc(...)]` returns `None`.
|
||||
fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
|
||||
fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind)> {
|
||||
match &self.kind {
|
||||
AttrKind::DocComment(kind, data) => Some((*data, *kind)),
|
||||
AttrKind::DocComment(kind, data) => Some((*data, DocFragmentKind::Sugared(*kind))),
|
||||
AttrKind::Normal(normal) if normal.item.path == sym::doc => {
|
||||
normal.item.value_str().map(|s| (s, CommentKind::Line))
|
||||
if let Some(value) = normal.item.value_str()
|
||||
&& let Some(value_span) = normal.item.value_span()
|
||||
{
|
||||
Some((value, DocFragmentKind::Raw(value_span)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
|
|
@ -220,6 +228,24 @@ impl AttributeExt for Attribute {
|
|||
fn is_automatically_derived_attr(&self) -> bool {
|
||||
self.has_name(sym::automatically_derived)
|
||||
}
|
||||
|
||||
fn is_doc_hidden(&self) -> bool {
|
||||
self.has_name(sym::doc)
|
||||
&& self.meta_item_list().is_some_and(|l| list_contains_name(&l, sym::hidden))
|
||||
}
|
||||
|
||||
fn is_doc_keyword_or_attribute(&self) -> bool {
|
||||
if self.has_name(sym::doc)
|
||||
&& let Some(items) = self.meta_item_list()
|
||||
{
|
||||
for item in items {
|
||||
if item.has_name(sym::keyword) || item.has_name(sym::attribute) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Attribute {
|
||||
|
|
@ -300,6 +326,25 @@ impl AttrItem {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the span in:
|
||||
///
|
||||
/// ```text
|
||||
/// #[attribute = "value"]
|
||||
/// ^^^^^^^
|
||||
/// ```
|
||||
///
|
||||
/// It returns `None` in any other cases like:
|
||||
///
|
||||
/// ```text
|
||||
/// #[attr("value")]
|
||||
/// ```
|
||||
fn value_span(&self) -> Option<Span> {
|
||||
match &self.args {
|
||||
AttrArgs::Eq { expr, .. } => Some(expr.span),
|
||||
AttrArgs::Delimited(_) | AttrArgs::Empty => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn meta(&self, span: Span) -> Option<MetaItem> {
|
||||
Some(MetaItem {
|
||||
unsafety: Safety::Default,
|
||||
|
|
@ -820,7 +865,7 @@ pub trait AttributeExt: Debug {
|
|||
/// * `/** doc */` returns `Some(("doc", CommentKind::Block))`.
|
||||
/// * `#[doc = "doc"]` returns `Some(("doc", CommentKind::Line))`.
|
||||
/// * `#[doc(...)]` returns `None`.
|
||||
fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)>;
|
||||
fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind)>;
|
||||
|
||||
/// Returns outer or inner if this is a doc attribute or a sugared doc
|
||||
/// comment, otherwise None.
|
||||
|
|
@ -830,6 +875,12 @@ pub trait AttributeExt: Debug {
|
|||
/// commented module (for inner doc) vs within its parent module (for outer
|
||||
/// doc).
|
||||
fn doc_resolution_scope(&self) -> Option<AttrStyle>;
|
||||
|
||||
/// Returns `true` if this attribute contains `doc(hidden)`.
|
||||
fn is_doc_hidden(&self) -> bool;
|
||||
|
||||
/// Returns `true` is this attribute contains `doc(keyword)` or `doc(attribute)`.
|
||||
fn is_doc_keyword_or_attribute(&self) -> bool;
|
||||
}
|
||||
|
||||
// FIXME(fn_delegation): use function delegation instead of manually forwarding
|
||||
|
|
@ -902,7 +953,7 @@ impl Attribute {
|
|||
AttributeExt::is_proc_macro_attr(self)
|
||||
}
|
||||
|
||||
pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
|
||||
AttributeExt::doc_str_and_comment_kind(self)
|
||||
pub fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind)> {
|
||||
AttributeExt::doc_str_and_fragment_kind(self)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,31 @@ use rustc_span::{Ident, Symbol};
|
|||
use crate::ast;
|
||||
use crate::util::case::Case;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
|
||||
/// Represents the kind of doc comment it is, ie `///` or `#[doc = ""]`.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Debug, HashStable_Generic)]
|
||||
pub enum DocFragmentKind {
|
||||
/// A sugared doc comment: `///` or `//!` or `/**` or `/*!`.
|
||||
Sugared(CommentKind),
|
||||
/// A "raw" doc comment: `#[doc = ""]`. The `Span` represents the string literal.
|
||||
Raw(Span),
|
||||
}
|
||||
|
||||
impl DocFragmentKind {
|
||||
pub fn is_sugared(self) -> bool {
|
||||
matches!(self, Self::Sugared(_))
|
||||
}
|
||||
|
||||
/// If it is `Sugared`, it will return its associated `CommentKind`, otherwise it will return
|
||||
/// `CommentKind::Line`.
|
||||
pub fn comment_kind(self) -> CommentKind {
|
||||
match self {
|
||||
Self::Sugared(kind) => kind,
|
||||
Self::Raw(_) => CommentKind::Line,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Debug, HashStable_Generic)]
|
||||
pub enum CommentKind {
|
||||
Line,
|
||||
Block,
|
||||
|
|
|
|||
|
|
@ -415,6 +415,7 @@ macro_rules! common_visitor_and_walkers {
|
|||
UnsafeBinderCastKind,
|
||||
BinOpKind,
|
||||
BlockCheckMode,
|
||||
MgcaDisambiguation,
|
||||
BorrowKind,
|
||||
BoundAsyncness,
|
||||
BoundConstness,
|
||||
|
|
|
|||
|
|
@ -489,7 +489,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
arg
|
||||
};
|
||||
|
||||
let anon_const = AnonConst { id: node_id, value: const_value };
|
||||
let anon_const = AnonConst {
|
||||
id: node_id,
|
||||
value: const_value,
|
||||
mgca_disambiguation: MgcaDisambiguation::AnonConst,
|
||||
};
|
||||
generic_args.push(AngleBracketedArg::Arg(GenericArg::Const(anon_const)));
|
||||
} else {
|
||||
real_args.push(arg);
|
||||
|
|
|
|||
|
|
@ -1219,7 +1219,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
.and_then(|partial_res| partial_res.full_res())
|
||||
{
|
||||
if !res.matches_ns(Namespace::TypeNS)
|
||||
&& path.is_potential_trivial_const_arg(false)
|
||||
&& path.is_potential_trivial_const_arg()
|
||||
{
|
||||
debug!(
|
||||
"lower_generic_arg: Lowering type argument as const argument: {:?}",
|
||||
|
|
@ -2287,11 +2287,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
) -> &'hir hir::ConstArg<'hir> {
|
||||
let tcx = self.tcx;
|
||||
|
||||
let ct_kind = if path
|
||||
.is_potential_trivial_const_arg(tcx.features().min_generic_const_args())
|
||||
&& (tcx.features().min_generic_const_args()
|
||||
|| matches!(res, Res::Def(DefKind::ConstParam, _)))
|
||||
{
|
||||
let is_trivial_path = path.is_potential_trivial_const_arg()
|
||||
&& matches!(res, Res::Def(DefKind::ConstParam, _));
|
||||
let ct_kind = if is_trivial_path || tcx.features().min_generic_const_args() {
|
||||
let qpath = self.lower_qpath(
|
||||
ty_id,
|
||||
&None,
|
||||
|
|
@ -2370,6 +2368,53 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self), ret)]
|
||||
fn lower_expr_to_const_arg_direct(&mut self, expr: &Expr) -> hir::ConstArg<'hir> {
|
||||
let overly_complex_const = |this: &mut Self| {
|
||||
let e = this.dcx().struct_span_err(
|
||||
expr.span,
|
||||
"complex const arguments must be placed inside of a `const` block",
|
||||
);
|
||||
|
||||
ConstArg { hir_id: this.next_id(), kind: hir::ConstArgKind::Error(expr.span, e.emit()) }
|
||||
};
|
||||
|
||||
match &expr.kind {
|
||||
ExprKind::Path(qself, path) => {
|
||||
let qpath = self.lower_qpath(
|
||||
expr.id,
|
||||
qself,
|
||||
path,
|
||||
ParamMode::Explicit,
|
||||
AllowReturnTypeNotation::No,
|
||||
// FIXME(mgca): update for `fn foo() -> Bar<FOO<impl Trait>>` support
|
||||
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
|
||||
None,
|
||||
);
|
||||
|
||||
ConstArg { hir_id: self.next_id(), kind: hir::ConstArgKind::Path(qpath) }
|
||||
}
|
||||
ExprKind::Underscore => ConstArg {
|
||||
hir_id: self.lower_node_id(expr.id),
|
||||
kind: hir::ConstArgKind::Infer(expr.span, ()),
|
||||
},
|
||||
ExprKind::Block(block, _) => {
|
||||
if let [stmt] = block.stmts.as_slice()
|
||||
&& let StmtKind::Expr(expr) = &stmt.kind
|
||||
&& matches!(
|
||||
expr.kind,
|
||||
ExprKind::Block(..) | ExprKind::Path(..) | ExprKind::Struct(..)
|
||||
)
|
||||
{
|
||||
return self.lower_expr_to_const_arg_direct(expr);
|
||||
}
|
||||
|
||||
overly_complex_const(self)
|
||||
}
|
||||
_ => overly_complex_const(self),
|
||||
}
|
||||
}
|
||||
|
||||
/// See [`hir::ConstArg`] for when to use this function vs
|
||||
/// [`Self::lower_anon_const_to_anon_const`].
|
||||
fn lower_anon_const_to_const_arg(&mut self, anon: &AnonConst) -> &'hir hir::ConstArg<'hir> {
|
||||
|
|
@ -2379,6 +2424,22 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
#[instrument(level = "debug", skip(self))]
|
||||
fn lower_anon_const_to_const_arg_direct(&mut self, anon: &AnonConst) -> hir::ConstArg<'hir> {
|
||||
let tcx = self.tcx;
|
||||
|
||||
// We cannot change parsing depending on feature gates available,
|
||||
// we can only require feature gates to be active as a delayed check.
|
||||
// Thus we just parse anon consts generally and make the real decision
|
||||
// making in ast lowering.
|
||||
// FIXME(min_generic_const_args): revisit once stable
|
||||
if tcx.features().min_generic_const_args() {
|
||||
return match anon.mgca_disambiguation {
|
||||
MgcaDisambiguation::AnonConst => {
|
||||
let lowered_anon = self.lower_anon_const_to_anon_const(anon);
|
||||
ConstArg { hir_id: self.next_id(), kind: hir::ConstArgKind::Anon(lowered_anon) }
|
||||
}
|
||||
MgcaDisambiguation::Direct => self.lower_expr_to_const_arg_direct(&anon.value),
|
||||
};
|
||||
}
|
||||
|
||||
// Unwrap a block, so that e.g. `{ P }` is recognised as a parameter. Const arguments
|
||||
// currently have to be wrapped in curly brackets, so it's necessary to special-case.
|
||||
let expr = if let ExprKind::Block(block, _) = &anon.value.kind
|
||||
|
|
@ -2390,12 +2451,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
} else {
|
||||
&anon.value
|
||||
};
|
||||
|
||||
let maybe_res =
|
||||
self.resolver.get_partial_res(expr.id).and_then(|partial_res| partial_res.full_res());
|
||||
if let ExprKind::Path(qself, path) = &expr.kind
|
||||
&& path.is_potential_trivial_const_arg(tcx.features().min_generic_const_args())
|
||||
&& (tcx.features().min_generic_const_args()
|
||||
|| matches!(maybe_res, Some(Res::Def(DefKind::ConstParam, _))))
|
||||
&& path.is_potential_trivial_const_arg()
|
||||
&& matches!(maybe_res, Some(Res::Def(DefKind::ConstParam, _)))
|
||||
{
|
||||
let qpath = self.lower_qpath(
|
||||
expr.id,
|
||||
|
|
@ -2403,7 +2464,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
path,
|
||||
ParamMode::Explicit,
|
||||
AllowReturnTypeNotation::No,
|
||||
// FIXME(mgca): update for `fn foo() -> Bar<FOO<impl Trait>>` support
|
||||
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
|
||||
None,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -517,6 +517,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
|
|||
gate_all!(fn_delegation, "functions delegation is not yet fully implemented");
|
||||
gate_all!(postfix_match, "postfix match is experimental");
|
||||
gate_all!(mut_ref, "mutable by-reference bindings are experimental");
|
||||
gate_all!(min_generic_const_args, "unbraced const blocks as const args are experimental");
|
||||
gate_all!(global_registration, "global registration is experimental");
|
||||
gate_all!(return_type_notation, "return type notation is experimental");
|
||||
gate_all!(pin_ergonomics, "pinned reference syntax is experimental");
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use std::borrow::Cow;
|
|||
use std::sync::Arc;
|
||||
|
||||
use rustc_ast::attr::AttrIdGenerator;
|
||||
use rustc_ast::token::{self, CommentKind, Delimiter, Token, TokenKind};
|
||||
use rustc_ast::token::{self, CommentKind, Delimiter, DocFragmentKind, Token, TokenKind};
|
||||
use rustc_ast::tokenstream::{Spacing, TokenStream, TokenTree};
|
||||
use rustc_ast::util::classify;
|
||||
use rustc_ast::util::comments::{Comment, CommentStyle};
|
||||
|
|
@ -381,15 +381,24 @@ fn space_between(tt1: &TokenTree, tt2: &TokenTree) -> bool {
|
|||
}
|
||||
|
||||
pub fn doc_comment_to_string(
|
||||
comment_kind: CommentKind,
|
||||
fragment_kind: DocFragmentKind,
|
||||
attr_style: ast::AttrStyle,
|
||||
data: Symbol,
|
||||
) -> String {
|
||||
match (comment_kind, attr_style) {
|
||||
(CommentKind::Line, ast::AttrStyle::Outer) => format!("///{data}"),
|
||||
(CommentKind::Line, ast::AttrStyle::Inner) => format!("//!{data}"),
|
||||
(CommentKind::Block, ast::AttrStyle::Outer) => format!("/**{data}*/"),
|
||||
(CommentKind::Block, ast::AttrStyle::Inner) => format!("/*!{data}*/"),
|
||||
match fragment_kind {
|
||||
DocFragmentKind::Sugared(comment_kind) => match (comment_kind, attr_style) {
|
||||
(CommentKind::Line, ast::AttrStyle::Outer) => format!("///{data}"),
|
||||
(CommentKind::Line, ast::AttrStyle::Inner) => format!("//!{data}"),
|
||||
(CommentKind::Block, ast::AttrStyle::Outer) => format!("/**{data}*/"),
|
||||
(CommentKind::Block, ast::AttrStyle::Inner) => format!("/*!{data}*/"),
|
||||
},
|
||||
DocFragmentKind::Raw(_) => {
|
||||
format!(
|
||||
"#{}[doc = {:?}]",
|
||||
if attr_style == ast::AttrStyle::Inner { "!" } else { "" },
|
||||
data.to_string(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -665,7 +674,11 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
|||
self.word("]");
|
||||
}
|
||||
ast::AttrKind::DocComment(comment_kind, data) => {
|
||||
self.word(doc_comment_to_string(*comment_kind, attr.style, *data));
|
||||
self.word(doc_comment_to_string(
|
||||
DocFragmentKind::Sugared(*comment_kind),
|
||||
attr.style,
|
||||
*data,
|
||||
));
|
||||
self.hardbreak()
|
||||
}
|
||||
}
|
||||
|
|
@ -1029,7 +1042,8 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
|||
|
||||
/* Other */
|
||||
token::DocComment(comment_kind, attr_style, data) => {
|
||||
doc_comment_to_string(comment_kind, attr_style, data).into()
|
||||
doc_comment_to_string(DocFragmentKind::Sugared(comment_kind), attr_style, data)
|
||||
.into()
|
||||
}
|
||||
token::Eof => "<eof>".into(),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,8 +14,29 @@ attr_parsing_deprecated_item_suggestion =
|
|||
.help = add `#![feature(deprecated_suggestion)]` to the crate root
|
||||
.note = see #94785 for more details
|
||||
|
||||
attr_parsing_doc_alias_bad_char =
|
||||
{$char_} character isn't allowed in {$attr_str}
|
||||
|
||||
attr_parsing_doc_alias_empty =
|
||||
{$attr_str} attribute cannot have empty value
|
||||
|
||||
attr_parsing_doc_alias_malformed =
|
||||
doc alias attribute expects a string `#[doc(alias = "a")]` or a list of strings `#[doc(alias("a", "b"))]`
|
||||
|
||||
attr_parsing_doc_alias_start_end =
|
||||
{$attr_str} cannot start or end with ' '
|
||||
|
||||
attr_parsing_doc_attribute_not_attribute =
|
||||
nonexistent builtin attribute `{$attribute}` used in `#[doc(attribute = "...")]`
|
||||
.help = only existing builtin attributes are allowed in core/std
|
||||
|
||||
attr_parsing_doc_keyword_not_keyword =
|
||||
nonexistent keyword `{$keyword}` used in `#[doc(keyword = "...")]`
|
||||
.help = only existing keywords are allowed in core/std
|
||||
|
||||
attr_parsing_empty_confusables =
|
||||
expected at least one confusable name
|
||||
|
||||
attr_parsing_empty_link_name =
|
||||
link name must not be empty
|
||||
.label = empty link name
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@ impl<S: Stage> CombineAttributeParser<S> for AllowInternalUnstableParser {
|
|||
]);
|
||||
const TEMPLATE: AttributeTemplate = template!(Word, List: &["feat1, feat2, ..."]);
|
||||
|
||||
fn extend<'c>(
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
args: &'c ArgParser<'_>,
|
||||
fn extend(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
args: &ArgParser,
|
||||
) -> impl IntoIterator<Item = Self::Item> {
|
||||
parse_unstable(cx, args, <Self as CombineAttributeParser<S>>::PATH[0])
|
||||
.into_iter()
|
||||
|
|
@ -39,9 +39,9 @@ impl<S: Stage> CombineAttributeParser<S> for UnstableFeatureBoundParser {
|
|||
]);
|
||||
const TEMPLATE: AttributeTemplate = template!(Word, List: &["feat1, feat2, ..."]);
|
||||
|
||||
fn extend<'c>(
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
args: &'c ArgParser<'_>,
|
||||
fn extend(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
args: &ArgParser,
|
||||
) -> impl IntoIterator<Item = Self::Item> {
|
||||
if !cx.features().staged_api() {
|
||||
cx.emit_err(session_diagnostics::StabilityOutsideStd { span: cx.attr_span });
|
||||
|
|
@ -67,17 +67,17 @@ impl<S: Stage> CombineAttributeParser<S> for AllowConstFnUnstableParser {
|
|||
]);
|
||||
const TEMPLATE: AttributeTemplate = template!(Word, List: &["feat1, feat2, ..."]);
|
||||
|
||||
fn extend<'c>(
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
args: &'c ArgParser<'_>,
|
||||
) -> impl IntoIterator<Item = Self::Item> + 'c {
|
||||
fn extend(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
args: &ArgParser,
|
||||
) -> impl IntoIterator<Item = Self::Item> {
|
||||
parse_unstable(cx, args, <Self as CombineAttributeParser<S>>::PATH[0])
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_unstable<S: Stage>(
|
||||
cx: &AcceptContext<'_, '_, S>,
|
||||
args: &ArgParser<'_>,
|
||||
args: &ArgParser,
|
||||
symbol: Symbol,
|
||||
) -> impl IntoIterator<Item = Symbol> {
|
||||
let mut res = Vec::new();
|
||||
|
|
|
|||
|
|
@ -35,9 +35,9 @@ const CFG_ATTR_TEMPLATE: AttributeTemplate = template!(
|
|||
"https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute"
|
||||
);
|
||||
|
||||
pub fn parse_cfg<'c, S: Stage>(
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
args: &'c ArgParser<'_>,
|
||||
pub fn parse_cfg<S: Stage>(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
args: &ArgParser,
|
||||
) -> Option<CfgEntry> {
|
||||
let ArgParser::List(list) = args else {
|
||||
cx.expected_list(cx.attr_span);
|
||||
|
|
@ -52,7 +52,7 @@ pub fn parse_cfg<'c, S: Stage>(
|
|||
|
||||
pub fn parse_cfg_entry<S: Stage>(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
item: &MetaItemOrLitParser<'_>,
|
||||
item: &MetaItemOrLitParser,
|
||||
) -> Result<CfgEntry, ErrorGuaranteed> {
|
||||
Ok(match item {
|
||||
MetaItemOrLitParser::MetaItemParser(meta) => match meta.args() {
|
||||
|
|
@ -98,7 +98,7 @@ pub fn parse_cfg_entry<S: Stage>(
|
|||
|
||||
fn parse_cfg_entry_version<S: Stage>(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
list: &MetaItemListParser<'_>,
|
||||
list: &MetaItemListParser,
|
||||
meta_span: Span,
|
||||
) -> Result<CfgEntry, ErrorGuaranteed> {
|
||||
try_gate_cfg(sym::version, meta_span, cx.sess(), cx.features_option());
|
||||
|
|
@ -130,7 +130,7 @@ fn parse_cfg_entry_version<S: Stage>(
|
|||
|
||||
fn parse_cfg_entry_target<S: Stage>(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
list: &MetaItemListParser<'_>,
|
||||
list: &MetaItemListParser,
|
||||
meta_span: Span,
|
||||
) -> Result<CfgEntry, ErrorGuaranteed> {
|
||||
if let Some(features) = cx.features_option()
|
||||
|
|
@ -171,7 +171,7 @@ fn parse_cfg_entry_target<S: Stage>(
|
|||
Ok(CfgEntry::All(result, list.span))
|
||||
}
|
||||
|
||||
fn parse_name_value<S: Stage>(
|
||||
pub(crate) fn parse_name_value<S: Stage>(
|
||||
name: Symbol,
|
||||
name_span: Span,
|
||||
value: Option<&NameValueParser>,
|
||||
|
|
|
|||
|
|
@ -2,10 +2,7 @@ use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, Nod
|
|||
use rustc_ast_pretty::pprust;
|
||||
use rustc_feature::{Features, GatedCfg, find_gated_cfg};
|
||||
use rustc_hir::RustcVersion;
|
||||
use rustc_hir::lints::AttributeLintKind;
|
||||
use rustc_session::Session;
|
||||
use rustc_session::config::ExpectedValues;
|
||||
use rustc_session::lint::builtin::UNEXPECTED_CFGS;
|
||||
use rustc_session::lint::{BuiltinLintDiag, Lint};
|
||||
use rustc_session::parse::feature_err;
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
|
@ -37,44 +34,6 @@ pub struct Condition {
|
|||
pub span: Span,
|
||||
}
|
||||
|
||||
/// Tests if a cfg-pattern matches the cfg set
|
||||
pub fn cfg_matches(
|
||||
cfg: &MetaItemInner,
|
||||
sess: &Session,
|
||||
lint_emitter: impl CfgMatchesLintEmitter,
|
||||
features: Option<&Features>,
|
||||
) -> bool {
|
||||
eval_condition(cfg, sess, features, &mut |cfg| {
|
||||
try_gate_cfg(cfg.name, cfg.span, sess, features);
|
||||
match sess.psess.check_config.expecteds.get(&cfg.name) {
|
||||
Some(ExpectedValues::Some(values)) if !values.contains(&cfg.value) => {
|
||||
lint_emitter.emit_span_lint(
|
||||
sess,
|
||||
UNEXPECTED_CFGS,
|
||||
cfg.span,
|
||||
BuiltinLintDiag::AttributeLint(AttributeLintKind::UnexpectedCfgValue(
|
||||
(cfg.name, cfg.name_span),
|
||||
cfg.value.map(|v| (v, cfg.value_span.unwrap())),
|
||||
)),
|
||||
);
|
||||
}
|
||||
None if sess.psess.check_config.exhaustive_names => {
|
||||
lint_emitter.emit_span_lint(
|
||||
sess,
|
||||
UNEXPECTED_CFGS,
|
||||
cfg.span,
|
||||
BuiltinLintDiag::AttributeLint(AttributeLintKind::UnexpectedCfgName(
|
||||
(cfg.name, cfg.name_span),
|
||||
cfg.value.map(|v| (v, cfg.value_span.unwrap())),
|
||||
)),
|
||||
);
|
||||
}
|
||||
_ => { /* not unexpected */ }
|
||||
}
|
||||
sess.psess.config.contains(&(cfg.name, cfg.value))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn try_gate_cfg(name: Symbol, span: Span, sess: &Session, features: Option<&Features>) {
|
||||
let gate = find_gated_cfg(|sym| sym == name);
|
||||
if let (Some(feats), Some(gated_cfg)) = (features, gate) {
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ impl<S: Stage> SingleAttributeParser<S> for OptimizeParser {
|
|||
]);
|
||||
const TEMPLATE: AttributeTemplate = template!(List: &["size", "speed", "none"]);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
let Some(list) = args.list() else {
|
||||
cx.expected_list(cx.attr_span);
|
||||
return None;
|
||||
|
|
@ -84,7 +84,7 @@ impl<S: Stage> SingleAttributeParser<S> for CoverageParser {
|
|||
]);
|
||||
const TEMPLATE: AttributeTemplate = template!(OneOf: &[sym::off, sym::on]);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
let Some(args) = args.list() else {
|
||||
cx.expected_specific_argument_and_list(cx.attr_span, &[sym::on, sym::off]);
|
||||
return None;
|
||||
|
|
@ -135,7 +135,7 @@ impl<S: Stage> SingleAttributeParser<S> for ExportNameParser {
|
|||
]);
|
||||
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
let Some(nv) = args.name_value() else {
|
||||
cx.expected_name_value(cx.attr_span, None);
|
||||
return None;
|
||||
|
|
@ -164,7 +164,7 @@ impl<S: Stage> SingleAttributeParser<S> for ObjcClassParser {
|
|||
AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]);
|
||||
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "ClassName");
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
let Some(nv) = args.name_value() else {
|
||||
cx.expected_name_value(cx.attr_span, None);
|
||||
return None;
|
||||
|
|
@ -196,7 +196,7 @@ impl<S: Stage> SingleAttributeParser<S> for ObjcSelectorParser {
|
|||
AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]);
|
||||
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "methodName");
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
let Some(nv) = args.name_value() else {
|
||||
cx.expected_name_value(cx.attr_span, None);
|
||||
return None;
|
||||
|
|
@ -472,10 +472,10 @@ impl<S: Stage> AttributeParser<S> for UsedParser {
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_tf_attribute<'c, S: Stage>(
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
args: &'c ArgParser<'_>,
|
||||
) -> impl IntoIterator<Item = (Symbol, Span)> + 'c {
|
||||
fn parse_tf_attribute<S: Stage>(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
args: &ArgParser,
|
||||
) -> impl IntoIterator<Item = (Symbol, Span)> {
|
||||
let mut features = Vec::new();
|
||||
let ArgParser::List(list) = args else {
|
||||
cx.expected_list(cx.attr_span);
|
||||
|
|
@ -529,10 +529,10 @@ impl<S: Stage> CombineAttributeParser<S> for TargetFeatureParser {
|
|||
};
|
||||
const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);
|
||||
|
||||
fn extend<'c>(
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
args: &'c ArgParser<'_>,
|
||||
) -> impl IntoIterator<Item = Self::Item> + 'c {
|
||||
fn extend(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
args: &ArgParser,
|
||||
) -> impl IntoIterator<Item = Self::Item> {
|
||||
parse_tf_attribute(cx, args)
|
||||
}
|
||||
|
||||
|
|
@ -567,10 +567,10 @@ impl<S: Stage> CombineAttributeParser<S> for ForceTargetFeatureParser {
|
|||
Allow(Target::Method(MethodKind::TraitImpl)),
|
||||
]);
|
||||
|
||||
fn extend<'c>(
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
args: &'c ArgParser<'_>,
|
||||
) -> impl IntoIterator<Item = Self::Item> + 'c {
|
||||
fn extend(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
args: &ArgParser,
|
||||
) -> impl IntoIterator<Item = Self::Item> {
|
||||
parse_tf_attribute(cx, args)
|
||||
}
|
||||
}
|
||||
|
|
@ -599,7 +599,7 @@ impl<S: Stage> SingleAttributeParser<S> for SanitizeParser {
|
|||
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
let Some(list) = args.list() else {
|
||||
cx.expected_list(cx.attr_span);
|
||||
return None;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ impl<S: Stage> SingleAttributeParser<S> for CrateNameParser {
|
|||
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel;
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
let ArgParser::NameValue(n) = args else {
|
||||
cx.expected_name_value(cx.attr_span, None);
|
||||
return None;
|
||||
|
|
@ -35,7 +35,7 @@ impl<S: Stage> SingleAttributeParser<S> for RecursionLimitParser {
|
|||
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N", "https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute");
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel;
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
let ArgParser::NameValue(nv) = args else {
|
||||
cx.expected_name_value(cx.attr_span, None);
|
||||
return None;
|
||||
|
|
@ -58,7 +58,7 @@ impl<S: Stage> SingleAttributeParser<S> for MoveSizeLimitParser {
|
|||
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N");
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel;
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
let ArgParser::NameValue(nv) = args else {
|
||||
cx.expected_name_value(cx.attr_span, None);
|
||||
return None;
|
||||
|
|
@ -81,7 +81,7 @@ impl<S: Stage> SingleAttributeParser<S> for TypeLengthLimitParser {
|
|||
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N");
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel;
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
let ArgParser::NameValue(nv) = args else {
|
||||
cx.expected_name_value(cx.attr_span, None);
|
||||
return None;
|
||||
|
|
@ -104,7 +104,7 @@ impl<S: Stage> SingleAttributeParser<S> for PatternComplexityLimitParser {
|
|||
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N");
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel;
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
let ArgParser::NameValue(nv) = args else {
|
||||
cx.expected_name_value(cx.attr_span, None);
|
||||
return None;
|
||||
|
|
@ -154,7 +154,7 @@ impl<S: Stage> SingleAttributeParser<S> for WindowsSubsystemParser {
|
|||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel;
|
||||
const TEMPLATE: AttributeTemplate = template!(NameValueStr: ["windows", "console"], "https://doc.rust-lang.org/reference/runtime.html#the-windows_subsystem-attribute");
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
let Some(nv) = args.name_value() else {
|
||||
cx.expected_name_value(
|
||||
args.span().unwrap_or(cx.inner_span),
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@ impl<S: Stage> CombineAttributeParser<S> for DebuggerViualizerParser {
|
|||
type Item = DebugVisualizer;
|
||||
const CONVERT: ConvertFn<Self::Item> = |v, _| AttributeKind::DebuggerVisualizer(v);
|
||||
|
||||
fn extend<'c>(
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
args: &'c ArgParser<'_>,
|
||||
) -> impl IntoIterator<Item = Self::Item> + 'c {
|
||||
fn extend(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
args: &ArgParser,
|
||||
) -> impl IntoIterator<Item = Self::Item> {
|
||||
let Some(l) = args.list() else {
|
||||
cx.expected_list(args.span().unwrap_or(cx.attr_span));
|
||||
return None;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ fn get<S: Stage>(
|
|||
cx: &AcceptContext<'_, '_, S>,
|
||||
name: Symbol,
|
||||
param_span: Span,
|
||||
arg: &ArgParser<'_>,
|
||||
arg: &ArgParser,
|
||||
item: &Option<Symbol>,
|
||||
) -> Option<Symbol> {
|
||||
if item.is_some() {
|
||||
|
|
@ -68,7 +68,7 @@ impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
|
|||
NameValueStr: "reason"
|
||||
);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
let features = cx.features();
|
||||
|
||||
let mut since = None;
|
||||
|
|
|
|||
627
compiler/rustc_attr_parsing/src/attributes/doc.rs
Normal file
627
compiler/rustc_attr_parsing/src/attributes/doc.rs
Normal file
|
|
@ -0,0 +1,627 @@
|
|||
use rustc_ast::ast::{AttrStyle, LitKind, MetaItemLit};
|
||||
use rustc_feature::template;
|
||||
use rustc_hir::attrs::{
|
||||
AttributeKind, CfgEntry, CfgHideShow, CfgInfo, DocAttribute, DocInline, HideOrShow,
|
||||
};
|
||||
use rustc_hir::lints::AttributeLintKind;
|
||||
use rustc_span::{Span, Symbol, edition, sym};
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
use super::prelude::{ALL_TARGETS, AllowedTargets};
|
||||
use super::{AcceptMapping, AttributeParser};
|
||||
use crate::context::{AcceptContext, FinalizeContext, Stage};
|
||||
use crate::parser::{ArgParser, MetaItemOrLitParser, MetaItemParser, OwnedPathParser};
|
||||
use crate::session_diagnostics::{
|
||||
DocAliasBadChar, DocAliasEmpty, DocAliasMalformed, DocAliasStartEnd, DocAttributeNotAttribute,
|
||||
DocKeywordNotKeyword,
|
||||
};
|
||||
|
||||
fn check_keyword<S: Stage>(cx: &mut AcceptContext<'_, '_, S>, keyword: Symbol, span: Span) -> bool {
|
||||
// FIXME: Once rustdoc can handle URL conflicts on case insensitive file systems, we
|
||||
// can remove the `SelfTy` case here, remove `sym::SelfTy`, and update the
|
||||
// `#[doc(keyword = "SelfTy")` attribute in `library/std/src/keyword_docs.rs`.
|
||||
if keyword.is_reserved(|| edition::LATEST_STABLE_EDITION)
|
||||
|| keyword.is_weak()
|
||||
|| keyword == sym::SelfTy
|
||||
{
|
||||
return true;
|
||||
}
|
||||
cx.emit_err(DocKeywordNotKeyword { span, keyword });
|
||||
false
|
||||
}
|
||||
|
||||
fn check_attribute<S: Stage>(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
attribute: Symbol,
|
||||
span: Span,
|
||||
) -> bool {
|
||||
// FIXME: This should support attributes with namespace like `diagnostic::do_not_recommend`.
|
||||
if rustc_feature::BUILTIN_ATTRIBUTE_MAP.contains_key(&attribute) {
|
||||
return true;
|
||||
}
|
||||
cx.emit_err(DocAttributeNotAttribute { span, attribute });
|
||||
false
|
||||
}
|
||||
|
||||
fn parse_keyword_and_attribute<S, F>(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
path: &OwnedPathParser,
|
||||
args: &ArgParser,
|
||||
attr_value: &mut Option<(Symbol, Span)>,
|
||||
callback: F,
|
||||
) where
|
||||
S: Stage,
|
||||
F: FnOnce(&mut AcceptContext<'_, '_, S>, Symbol, Span) -> bool,
|
||||
{
|
||||
let Some(nv) = args.name_value() else {
|
||||
cx.expected_name_value(args.span().unwrap_or(path.span()), path.word_sym());
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(value) = nv.value_as_str() else {
|
||||
cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
|
||||
return;
|
||||
};
|
||||
|
||||
if !callback(cx, value, nv.value_span) {
|
||||
return;
|
||||
}
|
||||
|
||||
if attr_value.is_some() {
|
||||
cx.duplicate_key(path.span(), path.word_sym().unwrap());
|
||||
return;
|
||||
}
|
||||
|
||||
*attr_value = Some((value, path.span()));
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub(crate) struct DocParser {
|
||||
attribute: DocAttribute,
|
||||
nb_doc_attrs: usize,
|
||||
}
|
||||
|
||||
impl DocParser {
|
||||
fn parse_single_test_doc_attr_item<S: Stage>(
|
||||
&mut self,
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
mip: &MetaItemParser,
|
||||
) {
|
||||
let path = mip.path();
|
||||
let args = mip.args();
|
||||
|
||||
match path.word_sym() {
|
||||
Some(sym::no_crate_inject) => {
|
||||
if let Err(span) = args.no_args() {
|
||||
cx.expected_no_args(span);
|
||||
return;
|
||||
}
|
||||
|
||||
if self.attribute.no_crate_inject.is_some() {
|
||||
cx.duplicate_key(path.span(), sym::no_crate_inject);
|
||||
return;
|
||||
}
|
||||
|
||||
self.attribute.no_crate_inject = Some(path.span())
|
||||
}
|
||||
Some(sym::attr) => {
|
||||
let Some(list) = args.list() else {
|
||||
cx.expected_list(cx.attr_span);
|
||||
return;
|
||||
};
|
||||
|
||||
// FIXME: convert list into a Vec of `AttributeKind` because current code is awful.
|
||||
for attr in list.mixed() {
|
||||
self.attribute.test_attrs.push(attr.span());
|
||||
}
|
||||
}
|
||||
Some(name) => {
|
||||
cx.emit_lint(
|
||||
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
|
||||
AttributeLintKind::DocTestUnknown { name },
|
||||
path.span(),
|
||||
);
|
||||
}
|
||||
None => {
|
||||
cx.emit_lint(
|
||||
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
|
||||
AttributeLintKind::DocTestLiteral,
|
||||
path.span(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_alias<S: Stage>(
|
||||
&mut self,
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
alias: Symbol,
|
||||
span: Span,
|
||||
) {
|
||||
let attr_str = "`#[doc(alias = \"...\")]`";
|
||||
if alias == sym::empty {
|
||||
cx.emit_err(DocAliasEmpty { span, attr_str });
|
||||
return;
|
||||
}
|
||||
|
||||
let alias_str = alias.as_str();
|
||||
if let Some(c) =
|
||||
alias_str.chars().find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' '))
|
||||
{
|
||||
cx.emit_err(DocAliasBadChar { span, attr_str, char_: c });
|
||||
return;
|
||||
}
|
||||
if alias_str.starts_with(' ') || alias_str.ends_with(' ') {
|
||||
cx.emit_err(DocAliasStartEnd { span, attr_str });
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(first_definition) = self.attribute.aliases.get(&alias).copied() {
|
||||
cx.emit_lint(
|
||||
rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
|
||||
AttributeLintKind::DuplicateDocAlias { first_definition },
|
||||
span,
|
||||
);
|
||||
}
|
||||
|
||||
self.attribute.aliases.insert(alias, span);
|
||||
}
|
||||
|
||||
fn parse_alias<S: Stage>(
|
||||
&mut self,
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
path: &OwnedPathParser,
|
||||
args: &ArgParser,
|
||||
) {
|
||||
match args {
|
||||
ArgParser::NoArgs => {
|
||||
cx.emit_err(DocAliasMalformed { span: args.span().unwrap_or(path.span()) });
|
||||
}
|
||||
ArgParser::List(list) => {
|
||||
for i in list.mixed() {
|
||||
let Some(alias) = i.lit().and_then(|i| i.value_str()) else {
|
||||
cx.expected_string_literal(i.span(), i.lit());
|
||||
continue;
|
||||
};
|
||||
|
||||
self.add_alias(cx, alias, i.span());
|
||||
}
|
||||
}
|
||||
ArgParser::NameValue(nv) => {
|
||||
let Some(alias) = nv.value_as_str() else {
|
||||
cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
|
||||
return;
|
||||
};
|
||||
self.add_alias(cx, alias, nv.value_span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_inline<S: Stage>(
|
||||
&mut self,
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
path: &OwnedPathParser,
|
||||
args: &ArgParser,
|
||||
inline: DocInline,
|
||||
) {
|
||||
if let Err(span) = args.no_args() {
|
||||
cx.expected_no_args(span);
|
||||
return;
|
||||
}
|
||||
|
||||
self.attribute.inline.push((inline, path.span()));
|
||||
}
|
||||
|
||||
fn parse_cfg<S: Stage>(&mut self, cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) {
|
||||
// This function replaces cases like `cfg(all())` with `true`.
|
||||
fn simplify_cfg(cfg_entry: &mut CfgEntry) {
|
||||
match cfg_entry {
|
||||
CfgEntry::All(cfgs, span) if cfgs.is_empty() => {
|
||||
*cfg_entry = CfgEntry::Bool(true, *span)
|
||||
}
|
||||
CfgEntry::Any(cfgs, span) if cfgs.is_empty() => {
|
||||
*cfg_entry = CfgEntry::Bool(false, *span)
|
||||
}
|
||||
CfgEntry::Not(cfg, _) => simplify_cfg(cfg),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
if let Some(mut cfg_entry) = super::cfg::parse_cfg(cx, args) {
|
||||
simplify_cfg(&mut cfg_entry);
|
||||
self.attribute.cfg.push(cfg_entry);
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_auto_cfg<S: Stage>(
|
||||
&mut self,
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
path: &OwnedPathParser,
|
||||
args: &ArgParser,
|
||||
) {
|
||||
match args {
|
||||
ArgParser::NoArgs => {
|
||||
self.attribute.auto_cfg_change.push((true, path.span()));
|
||||
}
|
||||
ArgParser::List(list) => {
|
||||
for meta in list.mixed() {
|
||||
let MetaItemOrLitParser::MetaItemParser(item) = meta else {
|
||||
cx.emit_lint(
|
||||
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
|
||||
AttributeLintKind::DocAutoCfgExpectsHideOrShow,
|
||||
meta.span(),
|
||||
);
|
||||
continue;
|
||||
};
|
||||
let (kind, attr_name) = match item.path().word_sym() {
|
||||
Some(sym::hide) => (HideOrShow::Hide, sym::hide),
|
||||
Some(sym::show) => (HideOrShow::Show, sym::show),
|
||||
_ => {
|
||||
cx.emit_lint(
|
||||
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
|
||||
AttributeLintKind::DocAutoCfgExpectsHideOrShow,
|
||||
item.span(),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let ArgParser::List(list) = item.args() else {
|
||||
cx.emit_lint(
|
||||
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
|
||||
AttributeLintKind::DocAutoCfgHideShowExpectsList { attr_name },
|
||||
item.span(),
|
||||
);
|
||||
continue;
|
||||
};
|
||||
|
||||
let mut cfg_hide_show = CfgHideShow { kind, values: ThinVec::new() };
|
||||
|
||||
for item in list.mixed() {
|
||||
let MetaItemOrLitParser::MetaItemParser(sub_item) = item else {
|
||||
cx.emit_lint(
|
||||
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
|
||||
AttributeLintKind::DocAutoCfgHideShowUnexpectedItem { attr_name },
|
||||
item.span(),
|
||||
);
|
||||
continue;
|
||||
};
|
||||
match sub_item.args() {
|
||||
a @ (ArgParser::NoArgs | ArgParser::NameValue(_)) => {
|
||||
let Some(name) = sub_item.path().word_sym() else {
|
||||
cx.expected_identifier(sub_item.path().span());
|
||||
continue;
|
||||
};
|
||||
if let Ok(CfgEntry::NameValue { name, value, .. }) =
|
||||
super::cfg::parse_name_value(
|
||||
name,
|
||||
sub_item.path().span(),
|
||||
a.name_value(),
|
||||
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.name_value().unwrap().value_span)),
|
||||
})
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
cx.emit_lint(
|
||||
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
|
||||
AttributeLintKind::DocAutoCfgHideShowUnexpectedItem {
|
||||
attr_name,
|
||||
},
|
||||
sub_item.span(),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.attribute.auto_cfg.push((cfg_hide_show, path.span()));
|
||||
}
|
||||
}
|
||||
ArgParser::NameValue(nv) => {
|
||||
let MetaItemLit { kind: LitKind::Bool(bool_value), span, .. } = nv.value_as_lit()
|
||||
else {
|
||||
cx.emit_lint(
|
||||
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
|
||||
AttributeLintKind::DocAutoCfgWrongLiteral,
|
||||
nv.value_span,
|
||||
);
|
||||
return;
|
||||
};
|
||||
self.attribute.auto_cfg_change.push((*bool_value, *span));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_single_doc_attr_item<S: Stage>(
|
||||
&mut self,
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
mip: &MetaItemParser,
|
||||
) {
|
||||
let path = mip.path();
|
||||
let args = mip.args();
|
||||
|
||||
macro_rules! no_args {
|
||||
($ident: ident) => {{
|
||||
if let Err(span) = args.no_args() {
|
||||
cx.expected_no_args(span);
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: It's errorring when the attribute is passed multiple times on the command
|
||||
// line.
|
||||
// The right fix for this would be to only check this rule if the attribute is
|
||||
// not set on the command line but directly in the code.
|
||||
// if self.attribute.$ident.is_some() {
|
||||
// cx.duplicate_key(path.span(), path.word_sym().unwrap());
|
||||
// return;
|
||||
// }
|
||||
|
||||
self.attribute.$ident = Some(path.span());
|
||||
}};
|
||||
}
|
||||
macro_rules! string_arg {
|
||||
($ident: ident) => {{
|
||||
let Some(nv) = args.name_value() else {
|
||||
cx.expected_name_value(args.span().unwrap_or(path.span()), path.word_sym());
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(s) = nv.value_as_str() else {
|
||||
cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
|
||||
return;
|
||||
};
|
||||
|
||||
// FIXME: It's errorring when the attribute is passed multiple times on the command
|
||||
// line.
|
||||
// The right fix for this would be to only check this rule if the attribute is
|
||||
// not set on the command line but directly in the code.
|
||||
// if self.attribute.$ident.is_some() {
|
||||
// cx.duplicate_key(path.span(), path.word_sym().unwrap());
|
||||
// return;
|
||||
// }
|
||||
|
||||
self.attribute.$ident = Some((s, path.span()));
|
||||
}};
|
||||
}
|
||||
|
||||
match path.word_sym() {
|
||||
Some(sym::alias) => self.parse_alias(cx, path, args),
|
||||
Some(sym::hidden) => no_args!(hidden),
|
||||
Some(sym::html_favicon_url) => string_arg!(html_favicon_url),
|
||||
Some(sym::html_logo_url) => string_arg!(html_logo_url),
|
||||
Some(sym::html_no_source) => no_args!(html_no_source),
|
||||
Some(sym::html_playground_url) => string_arg!(html_playground_url),
|
||||
Some(sym::html_root_url) => string_arg!(html_root_url),
|
||||
Some(sym::issue_tracker_base_url) => string_arg!(issue_tracker_base_url),
|
||||
Some(sym::inline) => self.parse_inline(cx, path, args, DocInline::Inline),
|
||||
Some(sym::no_inline) => self.parse_inline(cx, path, args, DocInline::NoInline),
|
||||
Some(sym::masked) => no_args!(masked),
|
||||
Some(sym::cfg) => self.parse_cfg(cx, args),
|
||||
Some(sym::notable_trait) => no_args!(notable_trait),
|
||||
Some(sym::keyword) => parse_keyword_and_attribute(
|
||||
cx,
|
||||
path,
|
||||
args,
|
||||
&mut self.attribute.keyword,
|
||||
check_keyword,
|
||||
),
|
||||
Some(sym::attribute) => parse_keyword_and_attribute(
|
||||
cx,
|
||||
path,
|
||||
args,
|
||||
&mut self.attribute.attribute,
|
||||
check_attribute,
|
||||
),
|
||||
Some(sym::fake_variadic) => no_args!(fake_variadic),
|
||||
Some(sym::search_unbox) => no_args!(search_unbox),
|
||||
Some(sym::rust_logo) => no_args!(rust_logo),
|
||||
Some(sym::auto_cfg) => self.parse_auto_cfg(cx, path, args),
|
||||
Some(sym::test) => {
|
||||
let Some(list) = args.list() else {
|
||||
cx.emit_lint(
|
||||
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
|
||||
AttributeLintKind::DocTestTakesList,
|
||||
args.span().unwrap_or(path.span()),
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
for i in list.mixed() {
|
||||
match i {
|
||||
MetaItemOrLitParser::MetaItemParser(mip) => {
|
||||
self.parse_single_test_doc_attr_item(cx, mip);
|
||||
}
|
||||
MetaItemOrLitParser::Lit(lit) => {
|
||||
cx.unexpected_literal(lit.span);
|
||||
}
|
||||
MetaItemOrLitParser::Err(..) => {
|
||||
// already had an error here, move on.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(sym::spotlight) => {
|
||||
cx.emit_lint(
|
||||
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
|
||||
AttributeLintKind::DocUnknownSpotlight { span: path.span() },
|
||||
path.span(),
|
||||
);
|
||||
}
|
||||
Some(sym::include) if let Some(nv) = args.name_value() => {
|
||||
let inner = match cx.attr_style {
|
||||
AttrStyle::Outer => "",
|
||||
AttrStyle::Inner => "!",
|
||||
};
|
||||
cx.emit_lint(
|
||||
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
|
||||
AttributeLintKind::DocUnknownInclude {
|
||||
inner,
|
||||
value: nv.value_as_lit().symbol,
|
||||
span: path.span(),
|
||||
},
|
||||
path.span(),
|
||||
);
|
||||
}
|
||||
Some(name @ (sym::passes | sym::no_default_passes)) => {
|
||||
cx.emit_lint(
|
||||
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
|
||||
AttributeLintKind::DocUnknownPasses { name, span: path.span() },
|
||||
path.span(),
|
||||
);
|
||||
}
|
||||
Some(sym::plugins) => {
|
||||
cx.emit_lint(
|
||||
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
|
||||
AttributeLintKind::DocUnknownPlugins { span: path.span() },
|
||||
path.span(),
|
||||
);
|
||||
}
|
||||
Some(name) => {
|
||||
cx.emit_lint(
|
||||
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
|
||||
AttributeLintKind::DocUnknownAny { name },
|
||||
path.span(),
|
||||
);
|
||||
}
|
||||
None => {
|
||||
let full_name =
|
||||
path.segments().map(|s| s.as_str()).intersperse("::").collect::<String>();
|
||||
cx.emit_lint(
|
||||
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
|
||||
AttributeLintKind::DocUnknownAny { name: Symbol::intern(&full_name) },
|
||||
path.span(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn accept_single_doc_attr<S: Stage>(
|
||||
&mut self,
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
args: &ArgParser,
|
||||
) {
|
||||
match args {
|
||||
ArgParser::NoArgs => {
|
||||
let suggestions = cx.suggestions();
|
||||
let span = cx.attr_span;
|
||||
cx.emit_lint(
|
||||
rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT,
|
||||
AttributeLintKind::IllFormedAttributeInput { suggestions, docs: None },
|
||||
span,
|
||||
);
|
||||
}
|
||||
ArgParser::List(items) => {
|
||||
for i in items.mixed() {
|
||||
match i {
|
||||
MetaItemOrLitParser::MetaItemParser(mip) => {
|
||||
self.nb_doc_attrs += 1;
|
||||
self.parse_single_doc_attr_item(cx, mip);
|
||||
}
|
||||
MetaItemOrLitParser::Lit(lit) => {
|
||||
cx.expected_name_value(lit.span, None);
|
||||
}
|
||||
MetaItemOrLitParser::Err(..) => {
|
||||
// already had an error here, move on.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ArgParser::NameValue(nv) => {
|
||||
if nv.value_as_str().is_none() {
|
||||
cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
|
||||
} else {
|
||||
unreachable!(
|
||||
"Should have been handled at the same time as sugar-syntaxed doc comments"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Stage> AttributeParser<S> for DocParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
|
||||
&[sym::doc],
|
||||
template!(
|
||||
List: &[
|
||||
"alias",
|
||||
"attribute",
|
||||
"hidden",
|
||||
"html_favicon_url",
|
||||
"html_logo_url",
|
||||
"html_no_source",
|
||||
"html_playground_url",
|
||||
"html_root_url",
|
||||
"issue_tracker_base_url",
|
||||
"inline",
|
||||
"no_inline",
|
||||
"masked",
|
||||
"cfg",
|
||||
"notable_trait",
|
||||
"keyword",
|
||||
"fake_variadic",
|
||||
"search_unbox",
|
||||
"rust_logo",
|
||||
"auto_cfg",
|
||||
"test",
|
||||
"spotlight",
|
||||
"include",
|
||||
"no_default_passes",
|
||||
"passes",
|
||||
"plugins",
|
||||
],
|
||||
NameValueStr: "string"
|
||||
),
|
||||
|this, cx, args| {
|
||||
this.accept_single_doc_attr(cx, args);
|
||||
},
|
||||
)];
|
||||
// FIXME: Currently emitted from 2 different places, generating duplicated warnings.
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
|
||||
// const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
|
||||
// Allow(Target::ExternCrate),
|
||||
// Allow(Target::Use),
|
||||
// Allow(Target::Static),
|
||||
// Allow(Target::Const),
|
||||
// Allow(Target::Fn),
|
||||
// Allow(Target::Mod),
|
||||
// Allow(Target::ForeignMod),
|
||||
// Allow(Target::TyAlias),
|
||||
// Allow(Target::Enum),
|
||||
// Allow(Target::Variant),
|
||||
// Allow(Target::Struct),
|
||||
// Allow(Target::Field),
|
||||
// Allow(Target::Union),
|
||||
// Allow(Target::Trait),
|
||||
// Allow(Target::TraitAlias),
|
||||
// Allow(Target::Impl { of_trait: true }),
|
||||
// Allow(Target::Impl { of_trait: false }),
|
||||
// Allow(Target::AssocConst),
|
||||
// Allow(Target::Method(MethodKind::Inherent)),
|
||||
// Allow(Target::Method(MethodKind::Trait { body: true })),
|
||||
// Allow(Target::Method(MethodKind::Trait { body: false })),
|
||||
// Allow(Target::Method(MethodKind::TraitImpl)),
|
||||
// Allow(Target::AssocTy),
|
||||
// Allow(Target::ForeignFn),
|
||||
// Allow(Target::ForeignStatic),
|
||||
// Allow(Target::ForeignTy),
|
||||
// Allow(Target::MacroDef),
|
||||
// Allow(Target::Crate),
|
||||
// Error(Target::WherePredicate),
|
||||
// ]);
|
||||
|
||||
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
|
||||
if self.nb_doc_attrs != 0 {
|
||||
Some(AttributeKind::Doc(Box::new(self.attribute)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@ impl<S: Stage> SingleAttributeParser<S> for DummyParser {
|
|||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
|
||||
const TEMPLATE: AttributeTemplate = template!(Word); // Anything, really
|
||||
|
||||
fn convert(_: &mut AcceptContext<'_, '_, S>, _: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(_: &mut AcceptContext<'_, '_, S>, _: &ArgParser) -> Option<AttributeKind> {
|
||||
Some(AttributeKind::Dummy)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ impl<S: Stage> SingleAttributeParser<S> for InlineParser {
|
|||
"https://doc.rust-lang.org/reference/attributes/codegen.html#the-inline-attribute"
|
||||
);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
match args {
|
||||
ArgParser::NoArgs => Some(AttributeKind::Inline(InlineAttr::Hint, cx.attr_span)),
|
||||
ArgParser::List(list) => {
|
||||
|
|
@ -77,7 +77,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcForceInlineParser {
|
|||
|
||||
const TEMPLATE: AttributeTemplate = template!(Word, List: &["reason"], NameValueStr: "reason");
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
let reason = match args {
|
||||
ArgParser::NoArgs => None,
|
||||
ArgParser::List(list) => {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ impl<S: Stage> SingleAttributeParser<S> for LinkNameParser {
|
|||
"https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_name-attribute"
|
||||
);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
let Some(nv) = args.name_value() else {
|
||||
cx.expected_name_value(cx.attr_span, None);
|
||||
return None;
|
||||
|
|
@ -62,10 +62,10 @@ impl<S: Stage> CombineAttributeParser<S> for LinkParser {
|
|||
], "https://doc.rust-lang.org/reference/items/external-blocks.html#the-link-attribute");
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); //FIXME Still checked fully in `check_attr.rs`
|
||||
|
||||
fn extend<'c>(
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
args: &'c ArgParser<'_>,
|
||||
) -> impl IntoIterator<Item = Self::Item> + 'c {
|
||||
fn extend(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
args: &ArgParser,
|
||||
) -> impl IntoIterator<Item = Self::Item> {
|
||||
let items = match args {
|
||||
ArgParser::List(list) => list,
|
||||
// This is an edgecase added because making this a hard error would break too many crates
|
||||
|
|
@ -242,7 +242,7 @@ impl<S: Stage> CombineAttributeParser<S> for LinkParser {
|
|||
|
||||
impl LinkParser {
|
||||
fn parse_link_name<S: Stage>(
|
||||
item: &MetaItemParser<'_>,
|
||||
item: &MetaItemParser,
|
||||
name: &mut Option<(Symbol, Span)>,
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
) -> bool {
|
||||
|
|
@ -267,7 +267,7 @@ impl LinkParser {
|
|||
}
|
||||
|
||||
fn parse_link_kind<S: Stage>(
|
||||
item: &MetaItemParser<'_>,
|
||||
item: &MetaItemParser,
|
||||
kind: &mut Option<NativeLibKind>,
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
sess: &Session,
|
||||
|
|
@ -347,7 +347,7 @@ impl LinkParser {
|
|||
}
|
||||
|
||||
fn parse_link_modifiers<S: Stage>(
|
||||
item: &MetaItemParser<'_>,
|
||||
item: &MetaItemParser,
|
||||
modifiers: &mut Option<(Symbol, Span)>,
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
) -> bool {
|
||||
|
|
@ -368,7 +368,7 @@ impl LinkParser {
|
|||
}
|
||||
|
||||
fn parse_link_cfg<S: Stage>(
|
||||
item: &MetaItemParser<'_>,
|
||||
item: &MetaItemParser,
|
||||
cfg: &mut Option<CfgEntry>,
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
sess: &Session,
|
||||
|
|
@ -400,7 +400,7 @@ impl LinkParser {
|
|||
}
|
||||
|
||||
fn parse_link_wasm_import_module<S: Stage>(
|
||||
item: &MetaItemParser<'_>,
|
||||
item: &MetaItemParser,
|
||||
wasm_import_module: &mut Option<(Symbol, Span)>,
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
) -> bool {
|
||||
|
|
@ -421,7 +421,7 @@ impl LinkParser {
|
|||
}
|
||||
|
||||
fn parse_link_import_name_type<S: Stage>(
|
||||
item: &MetaItemParser<'_>,
|
||||
item: &MetaItemParser,
|
||||
import_name_type: &mut Option<(PeImportNameType, Span)>,
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
) -> bool {
|
||||
|
|
@ -478,7 +478,7 @@ impl<S: Stage> SingleAttributeParser<S> for LinkSectionParser {
|
|||
"https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute"
|
||||
);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
let Some(nv) = args.name_value() else {
|
||||
cx.expected_name_value(cx.attr_span, None);
|
||||
return None;
|
||||
|
|
@ -551,7 +551,7 @@ impl<S: Stage> SingleAttributeParser<S> for LinkOrdinalParser {
|
|||
"https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_ordinal-attribute"
|
||||
);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
let ordinal = parse_single_integer(cx, args)?;
|
||||
|
||||
// According to the table at
|
||||
|
|
@ -607,7 +607,7 @@ impl<S: Stage> SingleAttributeParser<S> for LinkageParser {
|
|||
"weak_odr",
|
||||
]);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
let Some(name_value) = args.name_value() else {
|
||||
cx.expected_name_value(cx.attr_span, Some(sym::linkage));
|
||||
return None;
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ impl<S: Stage> SingleAttributeParser<S> for MacroExportParser {
|
|||
Error(Target::Crate),
|
||||
]);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
let local_inner_macros = match args {
|
||||
ArgParser::NoArgs => false,
|
||||
ArgParser::List(list) => {
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ pub(crate) mod confusables;
|
|||
pub(crate) mod crate_level;
|
||||
pub(crate) mod debugger;
|
||||
pub(crate) mod deprecation;
|
||||
pub(crate) mod doc;
|
||||
pub(crate) mod dummy;
|
||||
pub(crate) mod inline;
|
||||
pub(crate) mod link_attrs;
|
||||
|
|
@ -61,7 +62,7 @@ pub(crate) mod traits;
|
|||
pub(crate) mod transparency;
|
||||
pub(crate) mod util;
|
||||
|
||||
type AcceptFn<T, S> = for<'sess> fn(&mut T, &mut AcceptContext<'_, 'sess, S>, &ArgParser<'_>);
|
||||
type AcceptFn<T, S> = for<'sess> fn(&mut T, &mut AcceptContext<'_, 'sess, S>, &ArgParser);
|
||||
type AcceptMapping<T, S> = &'static [(&'static [Symbol], AttributeTemplate, AcceptFn<T, S>)];
|
||||
|
||||
/// An [`AttributeParser`] is a type which searches for syntactic attributes.
|
||||
|
|
@ -132,7 +133,7 @@ pub(crate) trait SingleAttributeParser<S: Stage>: 'static {
|
|||
const TEMPLATE: AttributeTemplate;
|
||||
|
||||
/// Converts a single syntactical attribute to a single semantic attribute, or [`AttributeKind`]
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind>;
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind>;
|
||||
}
|
||||
|
||||
/// Use in combination with [`SingleAttributeParser`].
|
||||
|
|
@ -281,7 +282,7 @@ impl<T: NoArgsAttributeParser<S>, S: Stage> SingleAttributeParser<S> for Without
|
|||
const ALLOWED_TARGETS: AllowedTargets = T::ALLOWED_TARGETS;
|
||||
const TEMPLATE: AttributeTemplate = template!(Word);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
if let Err(span) = args.no_args() {
|
||||
cx.expected_no_args(span);
|
||||
}
|
||||
|
|
@ -314,10 +315,10 @@ pub(crate) trait CombineAttributeParser<S: Stage>: 'static {
|
|||
const TEMPLATE: AttributeTemplate;
|
||||
|
||||
/// Converts a single syntactical attribute to a number of elements of the semantic attribute, or [`AttributeKind`]
|
||||
fn extend<'c>(
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
args: &'c ArgParser<'_>,
|
||||
) -> impl IntoIterator<Item = Self::Item> + 'c;
|
||||
fn extend(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
args: &ArgParser,
|
||||
) -> impl IntoIterator<Item = Self::Item>;
|
||||
}
|
||||
|
||||
/// Use in combination with [`CombineAttributeParser`].
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ impl<S: Stage> SingleAttributeParser<S> for MustUseParser {
|
|||
"https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute"
|
||||
);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
Some(AttributeKind::MustUse {
|
||||
span: cx.attr_span,
|
||||
reason: match args {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ impl<S: Stage> SingleAttributeParser<S> for PathParser {
|
|||
"https://doc.rust-lang.org/reference/items/modules.html#the-path-attribute"
|
||||
);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
let Some(nv) = args.name_value() else {
|
||||
cx.expected_name_value(cx.attr_span, None);
|
||||
return None;
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ impl<S: Stage> SingleAttributeParser<S> for ProcMacroDeriveParser {
|
|||
"https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros"
|
||||
);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
let (trait_name, helper_attrs) = parse_derive_like(cx, args, true)?;
|
||||
Some(AttributeKind::ProcMacroDerive {
|
||||
trait_name: trait_name.expect("Trait name is mandatory, so it is present"),
|
||||
|
|
@ -49,7 +49,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcBuiltinMacroParser {
|
|||
const TEMPLATE: AttributeTemplate =
|
||||
template!(List: &["TraitName", "TraitName, attributes(name1, name2, ...)"]);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
let (builtin_name, helper_attrs) = parse_derive_like(cx, args, false)?;
|
||||
Some(AttributeKind::RustcBuiltinMacro { builtin_name, helper_attrs, span: cx.attr_span })
|
||||
}
|
||||
|
|
@ -57,7 +57,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcBuiltinMacroParser {
|
|||
|
||||
fn parse_derive_like<S: Stage>(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
args: &ArgParser<'_>,
|
||||
args: &ArgParser,
|
||||
trait_name_mandatory: bool,
|
||||
) -> Option<(Option<Symbol>, ThinVec<Symbol>)> {
|
||||
let Some(list) = args.list() else {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ impl<S: Stage> SingleAttributeParser<S> for CustomMirParser {
|
|||
|
||||
const TEMPLATE: AttributeTemplate = template!(List: &[r#"dialect = "...", phase = "...""#]);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
let Some(list) = args.list() else {
|
||||
cx.expected_list(cx.attr_span);
|
||||
return None;
|
||||
|
|
@ -70,7 +70,7 @@ impl<S: Stage> SingleAttributeParser<S> for CustomMirParser {
|
|||
fn extract_value<S: Stage>(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
key: Symbol,
|
||||
arg: &ArgParser<'_>,
|
||||
arg: &ArgParser,
|
||||
span: Span,
|
||||
out_val: &mut Option<(Symbol, Span)>,
|
||||
failed: &mut bool,
|
||||
|
|
|
|||
|
|
@ -26,10 +26,10 @@ impl<S: Stage> CombineAttributeParser<S> for ReprParser {
|
|||
"https://doc.rust-lang.org/reference/type-layout.html#representations"
|
||||
);
|
||||
|
||||
fn extend<'c>(
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
args: &'c ArgParser<'_>,
|
||||
) -> impl IntoIterator<Item = Self::Item> + 'c {
|
||||
fn extend(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
args: &ArgParser,
|
||||
) -> impl IntoIterator<Item = Self::Item> {
|
||||
let mut reprs = Vec::new();
|
||||
|
||||
let Some(list) = args.list() else {
|
||||
|
|
@ -98,10 +98,7 @@ fn int_type_of_word(s: Symbol) -> Option<IntType> {
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_repr<S: Stage>(
|
||||
cx: &AcceptContext<'_, '_, S>,
|
||||
param: &MetaItemParser<'_>,
|
||||
) -> Option<ReprAttr> {
|
||||
fn parse_repr<S: Stage>(cx: &AcceptContext<'_, '_, S>, param: &MetaItemParser) -> Option<ReprAttr> {
|
||||
use ReprAttr::*;
|
||||
|
||||
// FIXME(jdonszelmann): invert the parsing here to match on the word first and then the
|
||||
|
|
@ -192,7 +189,7 @@ enum AlignKind {
|
|||
|
||||
fn parse_repr_align<S: Stage>(
|
||||
cx: &AcceptContext<'_, '_, S>,
|
||||
list: &MetaItemListParser<'_>,
|
||||
list: &MetaItemListParser,
|
||||
param_span: Span,
|
||||
align_kind: AlignKind,
|
||||
) -> Option<ReprAttr> {
|
||||
|
|
@ -278,11 +275,7 @@ impl AlignParser {
|
|||
const PATH: &'static [Symbol] = &[sym::rustc_align];
|
||||
const TEMPLATE: AttributeTemplate = template!(List: &["<alignment in bytes>"]);
|
||||
|
||||
fn parse<'c, S: Stage>(
|
||||
&mut self,
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
args: &'c ArgParser<'_>,
|
||||
) {
|
||||
fn parse<S: Stage>(&mut self, cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) {
|
||||
match args {
|
||||
ArgParser::NoArgs | ArgParser::NameValue(_) => {
|
||||
cx.expected_list(cx.attr_span);
|
||||
|
|
@ -339,11 +332,7 @@ impl AlignStaticParser {
|
|||
const PATH: &'static [Symbol] = &[sym::rustc_align_static];
|
||||
const TEMPLATE: AttributeTemplate = AlignParser::TEMPLATE;
|
||||
|
||||
fn parse<'c, S: Stage>(
|
||||
&mut self,
|
||||
cx: &'c mut AcceptContext<'_, '_, S>,
|
||||
args: &'c ArgParser<'_>,
|
||||
) {
|
||||
fn parse<S: Stage>(&mut self, cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) {
|
||||
self.0.parse(cx, args)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcLayoutScalarValidRangeStartPars
|
|||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
|
||||
const TEMPLATE: AttributeTemplate = template!(List: &["start"]);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
parse_single_integer(cx, args)
|
||||
.map(|n| AttributeKind::RustcLayoutScalarValidRangeStart(Box::new(n), cx.attr_span))
|
||||
}
|
||||
|
|
@ -34,7 +34,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcLayoutScalarValidRangeEndParser
|
|||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
|
||||
const TEMPLATE: AttributeTemplate = template!(List: &["end"]);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
parse_single_integer(cx, args)
|
||||
.map(|n| AttributeKind::RustcLayoutScalarValidRangeEnd(Box::new(n), cx.attr_span))
|
||||
}
|
||||
|
|
@ -49,7 +49,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcObjectLifetimeDefaultParser {
|
|||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
|
||||
const TEMPLATE: AttributeTemplate = template!(Word);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
if let Err(span) = args.no_args() {
|
||||
cx.expected_no_args(span);
|
||||
return None;
|
||||
|
|
@ -68,7 +68,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcSimdMonomorphizeLaneLimitParser
|
|||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
|
||||
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N");
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
let ArgParser::NameValue(nv) = args else {
|
||||
cx.expected_name_value(cx.attr_span, None);
|
||||
return None;
|
||||
|
|
|
|||
|
|
@ -267,7 +267,7 @@ impl<S: Stage> AttributeParser<S> for ConstStabilityParser {
|
|||
/// `name = value`
|
||||
fn insert_value_into_option_or_error<S: Stage>(
|
||||
cx: &AcceptContext<'_, '_, S>,
|
||||
param: &MetaItemParser<'_>,
|
||||
param: &MetaItemParser,
|
||||
item: &mut Option<Symbol>,
|
||||
name: Ident,
|
||||
) -> Option<()> {
|
||||
|
|
@ -289,7 +289,7 @@ fn insert_value_into_option_or_error<S: Stage>(
|
|||
/// its stability information.
|
||||
pub(crate) fn parse_stability<S: Stage>(
|
||||
cx: &AcceptContext<'_, '_, S>,
|
||||
args: &ArgParser<'_>,
|
||||
args: &ArgParser,
|
||||
) -> Option<(Symbol, StabilityLevel)> {
|
||||
let mut feature = None;
|
||||
let mut since = None;
|
||||
|
|
@ -365,7 +365,7 @@ pub(crate) fn parse_stability<S: Stage>(
|
|||
/// attribute, and return the feature name and its stability information.
|
||||
pub(crate) fn parse_unstability<S: Stage>(
|
||||
cx: &AcceptContext<'_, '_, S>,
|
||||
args: &ArgParser<'_>,
|
||||
args: &ArgParser,
|
||||
) -> Option<(Symbol, StabilityLevel)> {
|
||||
let mut feature = None;
|
||||
let mut reason = None;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ impl<S: Stage> SingleAttributeParser<S> for IgnoreParser {
|
|||
"https://doc.rust-lang.org/reference/attributes/testing.html#the-ignore-attribute"
|
||||
);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
Some(AttributeKind::Ignore {
|
||||
span: cx.attr_span,
|
||||
reason: match args {
|
||||
|
|
@ -49,7 +49,7 @@ impl<S: Stage> SingleAttributeParser<S> for ShouldPanicParser {
|
|||
"https://doc.rust-lang.org/reference/attributes/testing.html#the-should_panic-attribute"
|
||||
);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
Some(AttributeKind::ShouldPanic {
|
||||
span: cx.attr_span,
|
||||
reason: match args {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ impl<S: Stage> SingleAttributeParser<S> for SkipDuringMethodDispatchParser {
|
|||
|
||||
const TEMPLATE: AttributeTemplate = template!(List: &["array, boxed_slice"]);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
let mut array = false;
|
||||
let mut boxed_slice = false;
|
||||
let Some(args) = args.list() else {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ impl<S: Stage> SingleAttributeParser<S> for TransparencyParser {
|
|||
const TEMPLATE: AttributeTemplate =
|
||||
template!(NameValueStr: ["transparent", "semitransparent", "opaque"]);
|
||||
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
|
||||
let Some(nv) = args.name_value() else {
|
||||
cx.expected_name_value(cx.attr_span, None);
|
||||
return None;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use rustc_ast::attr::AttributeExt;
|
|||
use rustc_feature::is_builtin_attr_name;
|
||||
use rustc_hir::RustcVersion;
|
||||
use rustc_hir::limit::Limit;
|
||||
use rustc_span::{Symbol, sym};
|
||||
use rustc_span::Symbol;
|
||||
|
||||
use crate::context::{AcceptContext, Stage};
|
||||
use crate::parser::{ArgParser, NameValueParser};
|
||||
|
|
@ -32,36 +32,6 @@ pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool {
|
|||
|| attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name))
|
||||
}
|
||||
|
||||
pub fn is_doc_alias_attrs_contain_symbol<'tcx, T: AttributeExt + 'tcx>(
|
||||
attrs: impl Iterator<Item = &'tcx T>,
|
||||
symbol: Symbol,
|
||||
) -> bool {
|
||||
let doc_attrs = attrs.filter(|attr| attr.has_name(sym::doc));
|
||||
for attr in doc_attrs {
|
||||
let Some(values) = attr.meta_item_list() else {
|
||||
continue;
|
||||
};
|
||||
let alias_values = values.iter().filter(|v| v.has_name(sym::alias));
|
||||
for v in alias_values {
|
||||
if let Some(nested) = v.meta_item_list() {
|
||||
// #[doc(alias("foo", "bar"))]
|
||||
let mut iter = nested.iter().filter_map(|item| item.lit()).map(|item| item.symbol);
|
||||
if iter.any(|s| s == symbol) {
|
||||
return true;
|
||||
}
|
||||
} else if let Some(meta) = v.meta_item()
|
||||
&& let Some(lit) = meta.name_value_literal()
|
||||
{
|
||||
// #[doc(alias = "foo")]
|
||||
if lit.symbol == symbol {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Parse a single integer.
|
||||
///
|
||||
/// Used by attributes that take a single integer as argument, such as
|
||||
|
|
@ -70,7 +40,7 @@ pub fn is_doc_alias_attrs_contain_symbol<'tcx, T: AttributeExt + 'tcx>(
|
|||
/// `args` is the parser for the attribute arguments.
|
||||
pub(crate) fn parse_single_integer<S: Stage>(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
args: &ArgParser<'_>,
|
||||
args: &ArgParser,
|
||||
) -> Option<u128> {
|
||||
let Some(list) = args.list() else {
|
||||
cx.expected_list(cx.attr_span);
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ use crate::attributes::crate_level::{
|
|||
};
|
||||
use crate::attributes::debugger::DebuggerViualizerParser;
|
||||
use crate::attributes::deprecation::DeprecationParser;
|
||||
use crate::attributes::doc::DocParser;
|
||||
use crate::attributes::dummy::DummyParser;
|
||||
use crate::attributes::inline::{InlineParser, RustcForceInlineParser};
|
||||
use crate::attributes::link_attrs::{
|
||||
|
|
@ -74,7 +75,7 @@ use crate::attributes::traits::{
|
|||
};
|
||||
use crate::attributes::transparency::TransparencyParser;
|
||||
use crate::attributes::{AttributeParser as _, Combine, Single, WithoutArgs};
|
||||
use crate::parser::{ArgParser, PathParser};
|
||||
use crate::parser::{ArgParser, RefPathParser};
|
||||
use crate::session_diagnostics::{
|
||||
AttributeParseError, AttributeParseErrorReason, ParsedDescription, UnknownMetaItem,
|
||||
};
|
||||
|
|
@ -94,7 +95,7 @@ pub(super) struct GroupTypeInnerAccept<S: Stage> {
|
|||
}
|
||||
|
||||
type AcceptFn<S> =
|
||||
Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, S>, &ArgParser<'a>) + Send + Sync>;
|
||||
Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, S>, &ArgParser) + Send + Sync>;
|
||||
type FinalizeFn<S> =
|
||||
Box<dyn Send + Sync + Fn(&mut FinalizeContext<'_, '_, S>) -> Option<AttributeKind>>;
|
||||
|
||||
|
|
@ -162,6 +163,7 @@ attribute_parsers!(
|
|||
BodyStabilityParser,
|
||||
ConfusablesParser,
|
||||
ConstStabilityParser,
|
||||
DocParser,
|
||||
MacroUseParser,
|
||||
NakedParser,
|
||||
StabilityParser,
|
||||
|
|
@ -427,7 +429,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
|
|||
&self,
|
||||
span: Span,
|
||||
found: String,
|
||||
options: &'static [&'static str],
|
||||
options: &[&'static str],
|
||||
) -> ErrorGuaranteed {
|
||||
self.emit_err(UnknownMetaItem { span, item: found, expected: options })
|
||||
}
|
||||
|
|
@ -711,7 +713,7 @@ pub(crate) struct FinalizeContext<'p, 'sess, S: Stage> {
|
|||
///
|
||||
/// Usually, you should use normal attribute parsing logic instead,
|
||||
/// especially when making a *denylist* of other attributes.
|
||||
pub(crate) all_attrs: &'p [PathParser<'p>],
|
||||
pub(crate) all_attrs: &'p [RefPathParser<'p>],
|
||||
}
|
||||
|
||||
impl<'p, 'sess: 'p, S: Stage> Deref for FinalizeContext<'p, 'sess, S> {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use std::borrow::Cow;
|
||||
use std::convert::identity;
|
||||
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast::token::DocFragmentKind;
|
||||
use rustc_ast::{AttrStyle, NodeId, Safety};
|
||||
use rustc_errors::DiagCtxtHandle;
|
||||
use rustc_feature::{AttributeTemplate, Features};
|
||||
|
|
@ -12,7 +13,7 @@ use rustc_session::lint::BuiltinLintDiag;
|
|||
use rustc_span::{DUMMY_SP, Span, Symbol, sym};
|
||||
|
||||
use crate::context::{AcceptContext, FinalizeContext, SharedContext, Stage};
|
||||
use crate::parser::{ArgParser, MetaItemParser, PathParser};
|
||||
use crate::parser::{ArgParser, PathParser, RefPathParser};
|
||||
use crate::session_diagnostics::ParsedDescription;
|
||||
use crate::{Early, Late, OmitDoc, ShouldEmit};
|
||||
|
||||
|
|
@ -135,7 +136,7 @@ impl<'sess> AttributeParser<'sess, Early> {
|
|||
target_node_id: NodeId,
|
||||
features: Option<&'sess Features>,
|
||||
emit_errors: ShouldEmit,
|
||||
parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser<'_>) -> Option<T>,
|
||||
parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser) -> Option<T>,
|
||||
template: &AttributeTemplate,
|
||||
) -> Option<T> {
|
||||
let ast::AttrKind::Normal(normal_attr) = &attr.kind else {
|
||||
|
|
@ -143,22 +144,23 @@ impl<'sess> AttributeParser<'sess, Early> {
|
|||
};
|
||||
let parts =
|
||||
normal_attr.item.path.segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>();
|
||||
let meta_parser = MetaItemParser::from_attr(normal_attr, &parts, &sess.psess, emit_errors)?;
|
||||
let path = meta_parser.path();
|
||||
let args = meta_parser.args();
|
||||
|
||||
let path = AttrPath::from_ast(&normal_attr.item.path, identity);
|
||||
let args =
|
||||
ArgParser::from_attr_args(&normal_attr.item.args, &parts, &sess.psess, emit_errors)?;
|
||||
Self::parse_single_args(
|
||||
sess,
|
||||
attr.span,
|
||||
normal_attr.item.span(),
|
||||
attr.style,
|
||||
path.get_attribute_path(),
|
||||
path,
|
||||
Some(normal_attr.item.unsafety),
|
||||
ParsedDescription::Attribute,
|
||||
target_span,
|
||||
target_node_id,
|
||||
features,
|
||||
emit_errors,
|
||||
args,
|
||||
&args,
|
||||
parse_fn,
|
||||
template,
|
||||
)
|
||||
|
|
@ -266,7 +268,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
|
|||
mut emit_lint: impl FnMut(AttributeLint<S::Id>),
|
||||
) -> Vec<Attribute> {
|
||||
let mut attributes = Vec::new();
|
||||
let mut attr_paths = Vec::new();
|
||||
let mut attr_paths: Vec<RefPathParser<'_>> = Vec::new();
|
||||
|
||||
for attr in attrs {
|
||||
// If we're only looking for a single attribute, skip all the ones we don't care about.
|
||||
|
|
@ -281,7 +283,8 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
|
|||
// that's expanded right? But no, sometimes, when parsing attributes on macros,
|
||||
// we already use the lowering logic and these are still there. So, when `omit_doc`
|
||||
// is set we *also* want to ignore these.
|
||||
if omit_doc == OmitDoc::Skip && attr.has_name(sym::doc) {
|
||||
let is_doc_attribute = attr.has_name(sym::doc);
|
||||
if omit_doc == OmitDoc::Skip && is_doc_attribute {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -293,24 +296,13 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
|
|||
|
||||
attributes.push(Attribute::Parsed(AttributeKind::DocComment {
|
||||
style: attr.style,
|
||||
kind: *comment_kind,
|
||||
kind: DocFragmentKind::Sugared(*comment_kind),
|
||||
span: lower_span(attr.span),
|
||||
comment: *symbol,
|
||||
}))
|
||||
}
|
||||
// // FIXME: make doc attributes go through a proper attribute parser
|
||||
// ast::AttrKind::Normal(n) if n.has_name(sym::doc) => {
|
||||
// let p = GenericMetaItemParser::from_attr(&n, self.dcx());
|
||||
//
|
||||
// attributes.push(Attribute::Parsed(AttributeKind::DocComment {
|
||||
// style: attr.style,
|
||||
// kind: CommentKind::Line,
|
||||
// span: attr.span,
|
||||
// comment: p.args().name_value(),
|
||||
// }))
|
||||
// }
|
||||
ast::AttrKind::Normal(n) => {
|
||||
attr_paths.push(PathParser(Cow::Borrowed(&n.item.path)));
|
||||
attr_paths.push(PathParser(&n.item.path));
|
||||
let attr_path = AttrPath::from_ast(&n.item.path, lower_span);
|
||||
|
||||
self.check_attribute_safety(
|
||||
|
|
@ -325,15 +317,46 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
|
|||
n.item.path.segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>();
|
||||
|
||||
if let Some(accepts) = S::parsers().accepters.get(parts.as_slice()) {
|
||||
let Some(parser) = MetaItemParser::from_attr(
|
||||
n,
|
||||
let Some(args) = ArgParser::from_attr_args(
|
||||
&n.item.args,
|
||||
&parts,
|
||||
&self.sess.psess,
|
||||
self.stage.should_emit(),
|
||||
) else {
|
||||
continue;
|
||||
};
|
||||
let args = parser.args();
|
||||
|
||||
// Special-case handling for `#[doc = "..."]`: if we go through with
|
||||
// `DocParser`, the order of doc comments will be messed up because `///`
|
||||
// doc comments are added into `attributes` whereas attributes parsed with
|
||||
// `DocParser` are added into `parsed_attributes` which are then appended
|
||||
// to `attributes`. So if you have:
|
||||
//
|
||||
// /// bla
|
||||
// #[doc = "a"]
|
||||
// /// blob
|
||||
//
|
||||
// You would get:
|
||||
//
|
||||
// bla
|
||||
// blob
|
||||
// a
|
||||
if is_doc_attribute
|
||||
&& let ArgParser::NameValue(nv) = &args
|
||||
// If not a string key/value, it should emit an error, but to make
|
||||
// things simpler, it's handled in `DocParser` because it's simpler to
|
||||
// emit an error with `AcceptContext`.
|
||||
&& let Some(comment) = nv.value_as_str()
|
||||
{
|
||||
attributes.push(Attribute::Parsed(AttributeKind::DocComment {
|
||||
style: attr.style,
|
||||
kind: DocFragmentKind::Raw(nv.value_span),
|
||||
span: attr.span,
|
||||
comment,
|
||||
}));
|
||||
continue;
|
||||
}
|
||||
|
||||
for accept in accepts {
|
||||
let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
|
||||
shared: SharedContext {
|
||||
|
|
@ -350,7 +373,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
|
|||
attr_path: attr_path.clone(),
|
||||
};
|
||||
|
||||
(accept.accept_fn)(&mut cx, args);
|
||||
(accept.accept_fn)(&mut cx, &args);
|
||||
if !matches!(cx.stage.should_emit(), ShouldEmit::Nothing) {
|
||||
Self::check_target(&accept.allowed_targets, target, &mut cx);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,6 +78,8 @@
|
|||
|
||||
// tidy-alphabetical-start
|
||||
#![feature(decl_macro)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(iter_intersperse)]
|
||||
#![recursion_limit = "256"]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
|
|
@ -107,7 +109,7 @@ pub use attributes::cfg::{
|
|||
};
|
||||
pub use attributes::cfg_old::*;
|
||||
pub use attributes::cfg_select::*;
|
||||
pub use attributes::util::{is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version};
|
||||
pub use attributes::util::{is_builtin_attr, parse_version};
|
||||
pub use context::{Early, Late, OmitDoc, ShouldEmit};
|
||||
pub use interface::AttributeParser;
|
||||
pub use session_diagnostics::ParsedDescription;
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@
|
|||
//!
|
||||
//! FIXME(jdonszelmann): delete `rustc_ast/attr/mod.rs`
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::borrow::Borrow;
|
||||
use std::fmt::{Debug, Display};
|
||||
|
||||
use rustc_ast::token::{self, Delimiter, MetaVarKind};
|
||||
use rustc_ast::tokenstream::TokenStream;
|
||||
use rustc_ast::{AttrArgs, Expr, ExprKind, LitKind, MetaItemLit, NormalAttr, Path, StmtKind, UnOp};
|
||||
use rustc_ast::{AttrArgs, Expr, ExprKind, LitKind, MetaItemLit, Path, StmtKind, UnOp};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_errors::{Diag, PResult};
|
||||
use rustc_hir::{self as hir, AttrPath};
|
||||
|
|
@ -26,9 +26,12 @@ use crate::session_diagnostics::{
|
|||
};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PathParser<'a>(pub Cow<'a, Path>);
|
||||
pub struct PathParser<P: Borrow<Path>>(pub P);
|
||||
|
||||
impl<'a> PathParser<'a> {
|
||||
pub type OwnedPathParser = PathParser<Path>;
|
||||
pub type RefPathParser<'p> = PathParser<&'p Path>;
|
||||
|
||||
impl<P: Borrow<Path>> PathParser<P> {
|
||||
pub fn get_attribute_path(&self) -> hir::AttrPath {
|
||||
AttrPath {
|
||||
segments: self.segments().copied().collect::<Vec<_>>().into_boxed_slice(),
|
||||
|
|
@ -36,16 +39,16 @@ impl<'a> PathParser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn segments(&'a self) -> impl Iterator<Item = &'a Ident> {
|
||||
self.0.segments.iter().map(|seg| &seg.ident)
|
||||
pub fn segments(&self) -> impl Iterator<Item = &Ident> {
|
||||
self.0.borrow().segments.iter().map(|seg| &seg.ident)
|
||||
}
|
||||
|
||||
pub fn span(&self) -> Span {
|
||||
self.0.span
|
||||
self.0.borrow().span
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.segments.len()
|
||||
self.0.borrow().segments.len()
|
||||
}
|
||||
|
||||
pub fn segments_is(&self, segments: &[Symbol]) -> bool {
|
||||
|
|
@ -76,21 +79,21 @@ impl<'a> PathParser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl Display for PathParser<'_> {
|
||||
impl<P: Borrow<Path>> Display for PathParser<P> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", pprust::path_to_string(&self.0))
|
||||
write!(f, "{}", pprust::path_to_string(self.0.borrow()))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[must_use]
|
||||
pub enum ArgParser<'a> {
|
||||
pub enum ArgParser {
|
||||
NoArgs,
|
||||
List(MetaItemListParser<'a>),
|
||||
List(MetaItemListParser),
|
||||
NameValue(NameValueParser),
|
||||
}
|
||||
|
||||
impl<'a> ArgParser<'a> {
|
||||
impl ArgParser {
|
||||
pub fn span(&self) -> Option<Span> {
|
||||
match self {
|
||||
Self::NoArgs => None,
|
||||
|
|
@ -100,7 +103,7 @@ impl<'a> ArgParser<'a> {
|
|||
}
|
||||
|
||||
pub fn from_attr_args<'sess>(
|
||||
value: &'a AttrArgs,
|
||||
value: &AttrArgs,
|
||||
parts: &[Symbol],
|
||||
psess: &'sess ParseSess,
|
||||
should_emit: ShouldEmit,
|
||||
|
|
@ -144,7 +147,7 @@ impl<'a> ArgParser<'a> {
|
|||
///
|
||||
/// - `#[allow(clippy::complexity)]`: `(clippy::complexity)` is a list
|
||||
/// - `#[rustfmt::skip::macros(target_macro_name)]`: `(target_macro_name)` is a list
|
||||
pub fn list(&self) -> Option<&MetaItemListParser<'a>> {
|
||||
pub fn list(&self) -> Option<&MetaItemListParser> {
|
||||
match self {
|
||||
Self::List(l) => Some(l),
|
||||
Self::NameValue(_) | Self::NoArgs => None,
|
||||
|
|
@ -184,17 +187,17 @@ impl<'a> ArgParser<'a> {
|
|||
///
|
||||
/// Choose which one you want using the provided methods.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum MetaItemOrLitParser<'a> {
|
||||
MetaItemParser(MetaItemParser<'a>),
|
||||
pub enum MetaItemOrLitParser {
|
||||
MetaItemParser(MetaItemParser),
|
||||
Lit(MetaItemLit),
|
||||
Err(Span, ErrorGuaranteed),
|
||||
}
|
||||
|
||||
impl<'sess> MetaItemOrLitParser<'sess> {
|
||||
pub fn parse_single(
|
||||
impl MetaItemOrLitParser {
|
||||
pub fn parse_single<'sess>(
|
||||
parser: &mut Parser<'sess>,
|
||||
should_emit: ShouldEmit,
|
||||
) -> PResult<'sess, MetaItemOrLitParser<'static>> {
|
||||
) -> PResult<'sess, MetaItemOrLitParser> {
|
||||
let mut this = MetaItemListParserContext { parser, should_emit };
|
||||
this.parse_meta_item_inner()
|
||||
}
|
||||
|
|
@ -216,7 +219,7 @@ impl<'sess> MetaItemOrLitParser<'sess> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn meta_item(&self) -> Option<&MetaItemParser<'sess>> {
|
||||
pub fn meta_item(&self) -> Option<&MetaItemParser> {
|
||||
match self {
|
||||
MetaItemOrLitParser::MetaItemParser(parser) => Some(parser),
|
||||
_ => None,
|
||||
|
|
@ -238,12 +241,12 @@ impl<'sess> MetaItemOrLitParser<'sess> {
|
|||
///
|
||||
/// The syntax of MetaItems can be found at <https://doc.rust-lang.org/reference/attributes.html>
|
||||
#[derive(Clone)]
|
||||
pub struct MetaItemParser<'a> {
|
||||
path: PathParser<'a>,
|
||||
args: ArgParser<'a>,
|
||||
pub struct MetaItemParser {
|
||||
path: OwnedPathParser,
|
||||
args: ArgParser,
|
||||
}
|
||||
|
||||
impl<'a> Debug for MetaItemParser<'a> {
|
||||
impl Debug for MetaItemParser {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("MetaItemParser")
|
||||
.field("path", &self.path)
|
||||
|
|
@ -252,28 +255,12 @@ impl<'a> Debug for MetaItemParser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> MetaItemParser<'a> {
|
||||
/// Create a new parser from a [`NormalAttr`], which is stored inside of any
|
||||
/// [`ast::Attribute`](rustc_ast::Attribute)
|
||||
pub fn from_attr<'sess>(
|
||||
attr: &'a NormalAttr,
|
||||
parts: &[Symbol],
|
||||
psess: &'sess ParseSess,
|
||||
should_emit: ShouldEmit,
|
||||
) -> Option<Self> {
|
||||
Some(Self {
|
||||
path: PathParser(Cow::Borrowed(&attr.item.path)),
|
||||
args: ArgParser::from_attr_args(&attr.item.args, parts, psess, should_emit)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MetaItemParser<'a> {
|
||||
impl MetaItemParser {
|
||||
pub fn span(&self) -> Span {
|
||||
if let Some(other) = self.args.span() {
|
||||
self.path.span().with_hi(other.hi())
|
||||
self.path.borrow().span().with_hi(other.hi())
|
||||
} else {
|
||||
self.path.span()
|
||||
self.path.borrow().span()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -282,12 +269,12 @@ impl<'a> MetaItemParser<'a> {
|
|||
/// - `#[rustfmt::skip]`: `rustfmt::skip` is a path
|
||||
/// - `#[allow(clippy::complexity)]`: `clippy::complexity` is a path
|
||||
/// - `#[inline]`: `inline` is a single segment path
|
||||
pub fn path(&self) -> &PathParser<'a> {
|
||||
pub fn path(&self) -> &OwnedPathParser {
|
||||
&self.path
|
||||
}
|
||||
|
||||
/// Gets just the args parser, without caring about the path.
|
||||
pub fn args(&self) -> &ArgParser<'a> {
|
||||
pub fn args(&self) -> &ArgParser {
|
||||
&self.args
|
||||
}
|
||||
|
||||
|
|
@ -297,7 +284,7 @@ impl<'a> MetaItemParser<'a> {
|
|||
/// - `#[inline]`: `inline` is a word
|
||||
/// - `#[rustfmt::skip]`: `rustfmt::skip` is a path,
|
||||
/// and not a word and should instead be parsed using [`path`](Self::path)
|
||||
pub fn word_is(&self, sym: Symbol) -> Option<&ArgParser<'a>> {
|
||||
pub fn word_is(&self, sym: Symbol) -> Option<&ArgParser> {
|
||||
self.path().word_is(sym).then(|| self.args())
|
||||
}
|
||||
}
|
||||
|
|
@ -421,7 +408,7 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> {
|
|||
Ok(lit)
|
||||
}
|
||||
|
||||
fn parse_attr_item(&mut self) -> PResult<'sess, MetaItemParser<'static>> {
|
||||
fn parse_attr_item(&mut self) -> PResult<'sess, MetaItemParser> {
|
||||
if let Some(MetaVarKind::Meta { has_meta_form }) = self.parser.token.is_metavar_seq() {
|
||||
return if has_meta_form {
|
||||
let attr_item = self
|
||||
|
|
@ -457,10 +444,10 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> {
|
|||
ArgParser::NoArgs
|
||||
};
|
||||
|
||||
Ok(MetaItemParser { path: PathParser(Cow::Owned(path)), args })
|
||||
Ok(MetaItemParser { path: PathParser(path), args })
|
||||
}
|
||||
|
||||
fn parse_meta_item_inner(&mut self) -> PResult<'sess, MetaItemOrLitParser<'static>> {
|
||||
fn parse_meta_item_inner(&mut self) -> PResult<'sess, MetaItemOrLitParser> {
|
||||
if let Some(token_lit) = self.parser.eat_token_lit() {
|
||||
// If a literal token is parsed, we commit to parsing a MetaItemLit for better errors
|
||||
Ok(MetaItemOrLitParser::Lit(self.unsuffixed_meta_item_from_lit(token_lit)?))
|
||||
|
|
@ -547,7 +534,7 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> {
|
|||
psess: &'sess ParseSess,
|
||||
span: Span,
|
||||
should_emit: ShouldEmit,
|
||||
) -> PResult<'sess, MetaItemListParser<'static>> {
|
||||
) -> PResult<'sess, MetaItemListParser> {
|
||||
let mut parser = Parser::new(psess, tokens, None);
|
||||
let mut this = MetaItemListParserContext { parser: &mut parser, should_emit };
|
||||
|
||||
|
|
@ -570,14 +557,14 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MetaItemListParser<'a> {
|
||||
sub_parsers: ThinVec<MetaItemOrLitParser<'a>>,
|
||||
pub struct MetaItemListParser {
|
||||
sub_parsers: ThinVec<MetaItemOrLitParser>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl<'a> MetaItemListParser<'a> {
|
||||
impl MetaItemListParser {
|
||||
pub(crate) fn new<'sess>(
|
||||
tokens: &'a TokenStream,
|
||||
tokens: &TokenStream,
|
||||
span: Span,
|
||||
psess: &'sess ParseSess,
|
||||
should_emit: ShouldEmit,
|
||||
|
|
@ -586,7 +573,7 @@ impl<'a> MetaItemListParser<'a> {
|
|||
}
|
||||
|
||||
/// Lets you pick and choose as what you want to parse each element in the list
|
||||
pub fn mixed(&self) -> impl Iterator<Item = &MetaItemOrLitParser<'a>> {
|
||||
pub fn mixed(&self) -> impl Iterator<Item = &MetaItemOrLitParser> {
|
||||
self.sub_parsers.iter()
|
||||
}
|
||||
|
||||
|
|
@ -601,7 +588,7 @@ impl<'a> MetaItemListParser<'a> {
|
|||
/// Returns Some if the list contains only a single element.
|
||||
///
|
||||
/// Inside the Some is the parser to parse this single element.
|
||||
pub fn single(&self) -> Option<&MetaItemOrLitParser<'a>> {
|
||||
pub fn single(&self) -> Option<&MetaItemOrLitParser> {
|
||||
let mut iter = self.mixed();
|
||||
iter.next().filter(|_| iter.next().is_none())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,49 @@ pub(crate) struct InvalidPredicate {
|
|||
pub predicate: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_doc_alias_empty)]
|
||||
pub(crate) struct DocAliasEmpty<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub attr_str: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_doc_alias_bad_char)]
|
||||
pub(crate) struct DocAliasBadChar<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub attr_str: &'a str,
|
||||
pub char_: char,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_doc_alias_start_end)]
|
||||
pub(crate) struct DocAliasStartEnd<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub attr_str: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_doc_keyword_not_keyword)]
|
||||
#[help]
|
||||
pub(crate) struct DocKeywordNotKeyword {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub keyword: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_doc_attribute_not_attribute)]
|
||||
#[help]
|
||||
pub(crate) struct DocAttributeNotAttribute {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub attribute: Symbol,
|
||||
}
|
||||
|
||||
/// Error code: E0541
|
||||
pub(crate) struct UnknownMetaItem<'a> {
|
||||
pub span: Span,
|
||||
|
|
@ -726,16 +769,20 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> {
|
|||
diag.note(format!("for more information, visit <{link}>"));
|
||||
}
|
||||
|
||||
diag.span_suggestions(
|
||||
self.attr_span,
|
||||
if self.suggestions.len() == 1 {
|
||||
"must be of the form".to_string()
|
||||
} else {
|
||||
format!("try changing it to one of the following valid forms of the {description}")
|
||||
},
|
||||
self.suggestions,
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
if self.suggestions.len() < 4 {
|
||||
diag.span_suggestions(
|
||||
self.attr_span,
|
||||
if self.suggestions.len() == 1 {
|
||||
"must be of the form".to_string()
|
||||
} else {
|
||||
format!(
|
||||
"try changing it to one of the following valid forms of the {description}"
|
||||
)
|
||||
},
|
||||
self.suggestions,
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
}
|
||||
|
||||
diag
|
||||
}
|
||||
|
|
@ -944,3 +991,10 @@ pub(crate) struct CfgAttrBadDelim {
|
|||
#[subdiagnostic]
|
||||
pub sugg: MetaBadDelimSugg,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_doc_alias_malformed)]
|
||||
pub(crate) struct DocAliasMalformed {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -198,6 +198,11 @@ fn emit_malformed_attribute(
|
|||
suggestions.push(format!("#{inner}[{name} = \"{descr}\"]"));
|
||||
}
|
||||
}
|
||||
// If there are too many suggestions, better remove all of them as it's just noise at this
|
||||
// point.
|
||||
if suggestions.len() > 3 {
|
||||
suggestions.clear();
|
||||
}
|
||||
if should_warn(name) {
|
||||
psess.buffer_lint(
|
||||
ILL_FORMED_ATTRIBUTE_INPUT,
|
||||
|
|
|
|||
|
|
@ -4521,7 +4521,9 @@ struct BreakFinder {
|
|||
impl<'hir> Visitor<'hir> for BreakFinder {
|
||||
fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) {
|
||||
match ex.kind {
|
||||
hir::ExprKind::Break(destination, _) => {
|
||||
hir::ExprKind::Break(destination, _)
|
||||
if !ex.span.is_desugaring(DesugaringKind::ForLoop) =>
|
||||
{
|
||||
self.found_breaks.push((destination, ex.span));
|
||||
}
|
||||
hir::ExprKind::Continue(destination) => {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ mod llvm_enzyme {
|
|||
use rustc_ast::{
|
||||
self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AssocItemKind, BindingMode,
|
||||
FnRetTy, FnSig, GenericArg, GenericArgs, GenericParamKind, Generics, ItemKind,
|
||||
MetaItemInner, PatKind, Path, PathSegment, TyKind, Visibility,
|
||||
MetaItemInner, MgcaDisambiguation, PatKind, Path, PathSegment, TyKind, Visibility,
|
||||
};
|
||||
use rustc_expand::base::{Annotatable, ExtCtxt};
|
||||
use rustc_span::{Ident, Span, Symbol, sym};
|
||||
|
|
@ -558,7 +558,11 @@ mod llvm_enzyme {
|
|||
}
|
||||
GenericParamKind::Const { .. } => {
|
||||
let expr = ecx.expr_path(ast::Path::from_ident(p.ident));
|
||||
let anon_const = AnonConst { id: ast::DUMMY_NODE_ID, value: expr };
|
||||
let anon_const = AnonConst {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
value: expr,
|
||||
mgca_disambiguation: MgcaDisambiguation::Direct,
|
||||
};
|
||||
Some(AngleBracketedArg::Arg(GenericArg::Const(anon_const)))
|
||||
}
|
||||
GenericParamKind::Lifetime { .. } => None,
|
||||
|
|
@ -813,6 +817,7 @@ mod llvm_enzyme {
|
|||
let anon_const = rustc_ast::AnonConst {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
value: ecx.expr_usize(span, 1 + x.width as usize),
|
||||
mgca_disambiguation: MgcaDisambiguation::Direct,
|
||||
};
|
||||
TyKind::Array(ty.clone(), anon_const)
|
||||
};
|
||||
|
|
@ -827,6 +832,7 @@ mod llvm_enzyme {
|
|||
let anon_const = rustc_ast::AnonConst {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
value: ecx.expr_usize(span, x.width as usize),
|
||||
mgca_disambiguation: MgcaDisambiguation::Direct,
|
||||
};
|
||||
let kind = TyKind::Array(ty.clone(), anon_const);
|
||||
let ty =
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use rustc_ast::tokenstream::TokenStream;
|
||||
use rustc_ast::{AnonConst, DUMMY_NODE_ID, Ty, TyPat, TyPatKind, ast, token};
|
||||
use rustc_ast::{AnonConst, DUMMY_NODE_ID, MgcaDisambiguation, Ty, TyPat, TyPatKind, ast, token};
|
||||
use rustc_errors::PResult;
|
||||
use rustc_expand::base::{self, DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult};
|
||||
use rustc_parse::exp;
|
||||
|
|
@ -60,8 +60,20 @@ fn ty_pat(kind: TyPatKind, span: Span) -> TyPat {
|
|||
fn pat_to_ty_pat(cx: &mut ExtCtxt<'_>, pat: ast::Pat) -> TyPat {
|
||||
let kind = match pat.kind {
|
||||
ast::PatKind::Range(start, end, include_end) => TyPatKind::Range(
|
||||
start.map(|value| Box::new(AnonConst { id: DUMMY_NODE_ID, value })),
|
||||
end.map(|value| Box::new(AnonConst { id: DUMMY_NODE_ID, value })),
|
||||
start.map(|value| {
|
||||
Box::new(AnonConst {
|
||||
id: DUMMY_NODE_ID,
|
||||
value,
|
||||
mgca_disambiguation: MgcaDisambiguation::Direct,
|
||||
})
|
||||
}),
|
||||
end.map(|value| {
|
||||
Box::new(AnonConst {
|
||||
id: DUMMY_NODE_ID,
|
||||
value,
|
||||
mgca_disambiguation: MgcaDisambiguation::Direct,
|
||||
})
|
||||
}),
|
||||
include_end,
|
||||
),
|
||||
ast::PatKind::Or(variants) => {
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@ pub(crate) fn compile_fn(
|
|||
context.clear();
|
||||
context.func = codegened_func.func;
|
||||
|
||||
#[cfg(any())] // This is never true
|
||||
#[cfg(false)]
|
||||
let _clif_guard = {
|
||||
use std::fmt::Write;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#### this error code is no longer emitted by the compiler.
|
||||
#### Note: this error code is no longer emitted by the compiler.
|
||||
|
||||
This was triggered when multiple macro definitions used the same
|
||||
`#[rustc_builtin_macro(..)]`. This is no longer an error.
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -23,15 +23,15 @@ impl fmt::Display for ErrCode {
|
|||
rustc_error_messages::into_diag_arg_using_display!(ErrCode);
|
||||
|
||||
macro_rules! define_error_code_constants_and_diagnostics_table {
|
||||
($($name:ident: $num:literal,)*) => (
|
||||
($($num:literal,)*) => (
|
||||
$(
|
||||
pub const $name: $crate::ErrCode = $crate::ErrCode::from_u32($num);
|
||||
pub const ${concat(E, $num)}: $crate::ErrCode = $crate::ErrCode::from_u32($num);
|
||||
)*
|
||||
pub static DIAGNOSTICS: &[($crate::ErrCode, &str)] = &[
|
||||
$( (
|
||||
$name,
|
||||
${concat(E, $num)},
|
||||
include_str!(
|
||||
concat!("../../rustc_error_codes/src/error_codes/", stringify!($name), ".md")
|
||||
concat!("../../rustc_error_codes/src/error_codes/E", stringify!($num), ".md")
|
||||
)
|
||||
), )*
|
||||
];
|
||||
|
|
|
|||
|
|
@ -474,9 +474,12 @@ pub trait Emitter {
|
|||
.chain(span.span_labels().iter().map(|sp_label| sp_label.span))
|
||||
.filter_map(|sp| {
|
||||
if !sp.is_dummy() && source_map.is_imported(sp) {
|
||||
let maybe_callsite = sp.source_callsite();
|
||||
if sp != maybe_callsite {
|
||||
return Some((sp, maybe_callsite));
|
||||
let mut span = sp;
|
||||
while let Some(callsite) = span.parent_callsite() {
|
||||
span = callsite;
|
||||
if !source_map.is_imported(span) {
|
||||
return Some((sp, span));
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#![feature(box_patterns)]
|
||||
#![feature(default_field_values)]
|
||||
#![feature(error_reporter)]
|
||||
#![feature(macro_metavar_expr_concat)]
|
||||
#![feature(negative_impls)]
|
||||
#![feature(never_type)]
|
||||
#![feature(rustc_attrs)]
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ use rustc_ast::token::Delimiter;
|
|||
use rustc_ast::tokenstream::TokenStream;
|
||||
use rustc_ast::util::literal;
|
||||
use rustc_ast::{
|
||||
self as ast, AnonConst, AttrItem, AttrVec, BlockCheckMode, Expr, LocalKind, MatchKind, PatKind,
|
||||
UnOp, attr, token, tokenstream,
|
||||
self as ast, AnonConst, AttrItem, AttrVec, BlockCheckMode, Expr, LocalKind, MatchKind,
|
||||
MgcaDisambiguation, PatKind, UnOp, attr, token, tokenstream,
|
||||
};
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
|
||||
|
|
@ -101,6 +101,7 @@ impl<'a> ExtCtxt<'a> {
|
|||
attrs: AttrVec::new(),
|
||||
tokens: None,
|
||||
}),
|
||||
mgca_disambiguation: MgcaDisambiguation::Direct,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2177,7 +2177,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
|
|||
continue;
|
||||
}
|
||||
|
||||
if attr.is_doc_comment() {
|
||||
if attr.doc_str_and_fragment_kind().is_some() {
|
||||
self.cx.sess.psess.buffer_lint(
|
||||
UNUSED_DOC_COMMENTS,
|
||||
current_span,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub use ReprAttr::*;
|
||||
use rustc_abi::Align;
|
||||
use rustc_ast::token::CommentKind;
|
||||
use rustc_ast::token::DocFragmentKind;
|
||||
use rustc_ast::{AttrStyle, ast};
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_error_messages::{DiagArgValue, IntoDiagArg};
|
||||
use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute};
|
||||
use rustc_span::def_id::DefId;
|
||||
|
|
@ -196,14 +198,68 @@ pub enum CfgEntry {
|
|||
|
||||
impl CfgEntry {
|
||||
pub fn span(&self) -> Span {
|
||||
let (CfgEntry::All(_, span)
|
||||
| CfgEntry::Any(_, span)
|
||||
| CfgEntry::Not(_, span)
|
||||
| CfgEntry::Bool(_, span)
|
||||
| CfgEntry::NameValue { span, .. }
|
||||
| CfgEntry::Version(_, span)) = self;
|
||||
let (Self::All(_, span)
|
||||
| Self::Any(_, span)
|
||||
| Self::Not(_, span)
|
||||
| Self::Bool(_, span)
|
||||
| Self::NameValue { span, .. }
|
||||
| Self::Version(_, span)) = self;
|
||||
*span
|
||||
}
|
||||
|
||||
/// Same as `PartialEq` but doesn't check spans and ignore order of cfgs.
|
||||
pub fn is_equivalent_to(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(Self::All(a, _), Self::All(b, _)) | (Self::Any(a, _), Self::Any(b, _)) => {
|
||||
a.len() == b.len() && a.iter().all(|a| b.iter().any(|b| a.is_equivalent_to(b)))
|
||||
}
|
||||
(Self::Not(a, _), Self::Not(b, _)) => a.is_equivalent_to(b),
|
||||
(Self::Bool(a, _), Self::Bool(b, _)) => a == b,
|
||||
(
|
||||
Self::NameValue { name: name1, value: value1, .. },
|
||||
Self::NameValue { name: name2, value: value2, .. },
|
||||
) => name1 == name2 && value1 == value2,
|
||||
(Self::Version(a, _), Self::Version(b, _)) => a == b,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for CfgEntry {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fn write_entries(
|
||||
name: &str,
|
||||
entries: &[CfgEntry],
|
||||
f: &mut fmt::Formatter<'_>,
|
||||
) -> fmt::Result {
|
||||
write!(f, "{name}(")?;
|
||||
for (nb, entry) in entries.iter().enumerate() {
|
||||
if nb != 0 {
|
||||
f.write_str(", ")?;
|
||||
}
|
||||
entry.fmt(f)?;
|
||||
}
|
||||
f.write_str(")")
|
||||
}
|
||||
match self {
|
||||
Self::All(entries, _) => write_entries("all", entries, f),
|
||||
Self::Any(entries, _) => write_entries("any", entries, f),
|
||||
Self::Not(entry, _) => write!(f, "not({entry})"),
|
||||
Self::Bool(value, _) => write!(f, "{value}"),
|
||||
Self::NameValue { name, value, .. } => {
|
||||
match value {
|
||||
// We use `as_str` and debug display to have characters escaped and `"`
|
||||
// characters surrounding the string.
|
||||
Some(value) => write!(f, "{name} = {:?}", value.as_str()),
|
||||
None => write!(f, "{name}"),
|
||||
}
|
||||
}
|
||||
Self::Version(version, _) => match version {
|
||||
Some(version) => write!(f, "{version}"),
|
||||
None => Ok(()),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Possible values for the `#[linkage]` attribute, allowing to specify the
|
||||
|
|
@ -420,6 +476,79 @@ impl WindowsSubsystemKind {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
#[derive(HashStable_Generic, Encodable, Decodable, PrintAttribute)]
|
||||
pub enum DocInline {
|
||||
Inline,
|
||||
NoInline,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
#[derive(HashStable_Generic, Encodable, Decodable, PrintAttribute)]
|
||||
pub enum HideOrShow {
|
||||
Hide,
|
||||
Show,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
|
||||
pub struct CfgInfo {
|
||||
pub name: Symbol,
|
||||
pub name_span: Span,
|
||||
pub value: Option<(Symbol, Span)>,
|
||||
}
|
||||
|
||||
impl CfgInfo {
|
||||
pub fn span_for_name_and_value(&self) -> Span {
|
||||
if let Some((_, value_span)) = self.value {
|
||||
self.name_span.with_hi(value_span.hi())
|
||||
} else {
|
||||
self.name_span
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
|
||||
pub struct CfgHideShow {
|
||||
pub kind: HideOrShow,
|
||||
pub values: ThinVec<CfgInfo>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
|
||||
pub struct DocAttribute {
|
||||
pub aliases: FxIndexMap<Symbol, Span>,
|
||||
pub hidden: Option<Span>,
|
||||
// Because we need to emit the error if there is more than one `inline` attribute on an item
|
||||
// at the same time as the other doc attributes, we store a list instead of using `Option`.
|
||||
pub inline: ThinVec<(DocInline, Span)>,
|
||||
|
||||
// unstable
|
||||
pub cfg: ThinVec<CfgEntry>,
|
||||
pub auto_cfg: ThinVec<(CfgHideShow, Span)>,
|
||||
/// This is for `#[doc(auto_cfg = false|true)]`/`#[doc(auto_cfg)]`.
|
||||
pub auto_cfg_change: ThinVec<(bool, Span)>,
|
||||
|
||||
// builtin
|
||||
pub fake_variadic: Option<Span>,
|
||||
pub keyword: Option<(Symbol, Span)>,
|
||||
pub attribute: Option<(Symbol, Span)>,
|
||||
pub masked: Option<Span>,
|
||||
pub notable_trait: Option<Span>,
|
||||
pub search_unbox: Option<Span>,
|
||||
|
||||
// valid on crate
|
||||
pub html_favicon_url: Option<(Symbol, Span)>,
|
||||
pub html_logo_url: Option<(Symbol, Span)>,
|
||||
pub html_playground_url: Option<(Symbol, Span)>,
|
||||
pub html_root_url: Option<(Symbol, Span)>,
|
||||
pub html_no_source: Option<Span>,
|
||||
pub issue_tracker_base_url: Option<(Symbol, Span)>,
|
||||
pub rust_logo: Option<Span>,
|
||||
|
||||
// #[doc(test(...))]
|
||||
pub test_attrs: ThinVec<Span>,
|
||||
pub no_crate_inject: Option<Span>,
|
||||
}
|
||||
|
||||
/// Represents parsed *built-in* inert attributes.
|
||||
///
|
||||
/// ## Overview
|
||||
|
|
@ -551,8 +680,14 @@ pub enum AttributeKind {
|
|||
/// Represents `#[rustc_do_not_implement_via_object]`.
|
||||
DoNotImplementViaObject(Span),
|
||||
|
||||
/// Represents [`#[doc = "..."]`](https://doc.rust-lang.org/stable/rustdoc/write-documentation/the-doc-attribute.html).
|
||||
DocComment { style: AttrStyle, kind: CommentKind, span: Span, comment: Symbol },
|
||||
/// Represents [`#[doc]`](https://doc.rust-lang.org/stable/rustdoc/write-documentation/the-doc-attribute.html).
|
||||
/// Represents all other uses of the [`#[doc]`](https://doc.rust-lang.org/stable/rustdoc/write-documentation/the-doc-attribute.html)
|
||||
/// attribute.
|
||||
Doc(Box<DocAttribute>),
|
||||
|
||||
/// Represents specifically [`#[doc = "..."]`](https://doc.rust-lang.org/stable/rustdoc/write-documentation/the-doc-attribute.html).
|
||||
/// i.e. doc comments.
|
||||
DocComment { style: AttrStyle, kind: DocFragmentKind, span: Span, comment: Symbol },
|
||||
|
||||
/// Represents `#[rustc_dummy]`.
|
||||
Dummy,
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ impl AttributeKind {
|
|||
DenyExplicitImpl(..) => No,
|
||||
Deprecation { .. } => Yes,
|
||||
DoNotImplementViaObject(..) => No,
|
||||
Doc(_) => Yes,
|
||||
DocComment { .. } => Yes,
|
||||
Dummy => No,
|
||||
ExportName { .. } => Yes,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
use std::num::NonZero;
|
||||
|
||||
use rustc_abi::Align;
|
||||
use rustc_ast::token::CommentKind;
|
||||
use rustc_ast::token::{CommentKind, DocFragmentKind};
|
||||
use rustc_ast::{AttrStyle, IntTy, UintTy};
|
||||
use rustc_ast_pretty::pp::Printer;
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_span::hygiene::Transparency;
|
||||
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol};
|
||||
use rustc_target::spec::SanitizerSet;
|
||||
|
|
@ -64,6 +65,25 @@ impl<T: PrintAttribute> PrintAttribute for ThinVec<T> {
|
|||
p.word("]");
|
||||
}
|
||||
}
|
||||
impl<T: PrintAttribute> PrintAttribute for FxIndexMap<T, Span> {
|
||||
fn should_render(&self) -> bool {
|
||||
self.is_empty() || self[0].should_render()
|
||||
}
|
||||
|
||||
fn print_attribute(&self, p: &mut Printer) {
|
||||
let mut last_printed = false;
|
||||
p.word("[");
|
||||
for (i, _) in self {
|
||||
if last_printed {
|
||||
p.word_space(",");
|
||||
}
|
||||
i.print_attribute(p);
|
||||
last_printed = i.should_render();
|
||||
}
|
||||
p.word("]");
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! print_skip {
|
||||
($($t: ty),* $(,)?) => {$(
|
||||
impl PrintAttribute for $t {
|
||||
|
|
@ -147,6 +167,7 @@ print_debug!(
|
|||
Align,
|
||||
AttrStyle,
|
||||
CommentKind,
|
||||
DocFragmentKind,
|
||||
Transparency,
|
||||
SanitizerSet,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use std::fmt;
|
|||
|
||||
use rustc_abi::ExternAbi;
|
||||
use rustc_ast::attr::AttributeExt;
|
||||
use rustc_ast::token::CommentKind;
|
||||
use rustc_ast::token::DocFragmentKind;
|
||||
use rustc_ast::util::parser::ExprPrecedence;
|
||||
use rustc_ast::{
|
||||
self as ast, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label, LitIntType,
|
||||
|
|
@ -1376,7 +1376,6 @@ impl AttributeExt for Attribute {
|
|||
fn doc_str(&self) -> Option<Symbol> {
|
||||
match &self {
|
||||
Attribute::Parsed(AttributeKind::DocComment { comment, .. }) => Some(*comment),
|
||||
Attribute::Unparsed(_) if self.has_name(sym::doc) => self.value_str(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -1386,14 +1385,11 @@ impl AttributeExt for Attribute {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
|
||||
fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind)> {
|
||||
match &self {
|
||||
Attribute::Parsed(AttributeKind::DocComment { kind, comment, .. }) => {
|
||||
Some((*comment, *kind))
|
||||
}
|
||||
Attribute::Unparsed(_) if self.has_name(sym::doc) => {
|
||||
self.value_str().map(|s| (s, CommentKind::Line))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -1418,6 +1414,14 @@ impl AttributeExt for Attribute {
|
|||
)
|
||||
)
|
||||
}
|
||||
|
||||
fn is_doc_hidden(&self) -> bool {
|
||||
matches!(self, Attribute::Parsed(AttributeKind::Doc(d)) if d.hidden.is_some())
|
||||
}
|
||||
|
||||
fn is_doc_keyword_or_attribute(&self) -> bool {
|
||||
matches!(self, Attribute::Parsed(AttributeKind::Doc(d)) if d.attribute.is_some() || d.keyword.is_some())
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(fn_delegation): use function delegation instead of manually forwarding
|
||||
|
|
@ -1503,8 +1507,8 @@ impl Attribute {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
|
||||
AttributeExt::doc_str_and_comment_kind(self)
|
||||
pub fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind)> {
|
||||
AttributeExt::doc_str_and_fragment_kind(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1147,17 +1147,16 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) -> Result<(), ErrorGuarant
|
|||
///
|
||||
/// Assuming the defaults are used, check that all predicates (bounds on the
|
||||
/// assoc type and where clauses on the trait) hold.
|
||||
fn check_associated_type_bounds(wfcx: &WfCheckingCtxt<'_, '_>, item: ty::AssocItem, span: Span) {
|
||||
fn check_associated_type_bounds(wfcx: &WfCheckingCtxt<'_, '_>, item: ty::AssocItem, _span: Span) {
|
||||
let bounds = wfcx.tcx().explicit_item_bounds(item.def_id);
|
||||
|
||||
debug!("check_associated_type_bounds: bounds={:?}", bounds);
|
||||
let wf_obligations = bounds.iter_identity_copied().flat_map(|(bound, bound_span)| {
|
||||
let normalized_bound = wfcx.normalize(span, None, bound);
|
||||
traits::wf::clause_obligations(
|
||||
wfcx.infcx,
|
||||
wfcx.param_env,
|
||||
wfcx.body_def_id,
|
||||
normalized_bound,
|
||||
bound,
|
||||
bound_span,
|
||||
)
|
||||
});
|
||||
|
|
@ -1525,7 +1524,6 @@ pub(super) fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, def_id:
|
|||
|
||||
assert_eq!(predicates.predicates.len(), predicates.spans.len());
|
||||
let wf_obligations = predicates.into_iter().flat_map(|(p, sp)| {
|
||||
let p = wfcx.normalize(sp, None, p);
|
||||
traits::wf::clause_obligations(infcx, wfcx.param_env, wfcx.body_def_id, p, sp)
|
||||
});
|
||||
let obligations: Vec<_> = wf_obligations.chain(default_obligations).collect();
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ edition = "2024"
|
|||
itertools = "0.12"
|
||||
rustc_abi = { path = "../rustc_abi" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_attr_parsing = { path = "../rustc_attr_parsing" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_errors = { path = "../rustc_errors" }
|
||||
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
// tidy-alphabetical-start
|
||||
#![allow(rustc::diagnostic_outside_of_impl)]
|
||||
#![allow(rustc::untranslatable_diagnostic)]
|
||||
#![feature(assert_matches)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(if_let_guard)]
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@ use std::cell::{Cell, RefCell};
|
|||
use std::cmp::max;
|
||||
use std::ops::Deref;
|
||||
|
||||
use rustc_attr_parsing::is_doc_alias_attrs_contain_symbol;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_data_structures::sso::SsoHashSet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::{self as hir, ExprKind, HirId, Node};
|
||||
use rustc_hir::{self as hir, ExprKind, HirId, Node, find_attr};
|
||||
use rustc_hir_analysis::autoderef::{self, Autoderef};
|
||||
use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse};
|
||||
use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk, TyCtxtInferExt};
|
||||
|
|
@ -2535,7 +2535,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
|||
let hir_id = self.fcx.tcx.local_def_id_to_hir_id(local_def_id);
|
||||
let attrs = self.fcx.tcx.hir_attrs(hir_id);
|
||||
|
||||
if is_doc_alias_attrs_contain_symbol(attrs.into_iter(), method.name) {
|
||||
if let Some(d) = find_attr!(attrs, AttributeKind::Doc(d) => d)
|
||||
&& d.aliases.contains_key(&method.name)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -233,6 +233,58 @@ lint_deprecated_where_clause_location = where clause not allowed here
|
|||
lint_diag_out_of_impl =
|
||||
diagnostics should only be created in `Diagnostic`/`Subdiagnostic`/`LintDiagnostic` impls
|
||||
|
||||
lint_doc_alias_duplicated = doc alias is duplicated
|
||||
.label = first defined here
|
||||
|
||||
lint_doc_auto_cfg_expects_hide_or_show =
|
||||
only `hide` or `show` are allowed in `#[doc(auto_cfg(...))]`
|
||||
|
||||
lint_doc_auto_cfg_hide_show_expects_list =
|
||||
`#![doc(auto_cfg({$attr_name}(...)))]` expects a list of items
|
||||
|
||||
lint_doc_auto_cfg_hide_show_unexpected_item =
|
||||
`#![doc(auto_cfg({$attr_name}(...)))]` only accepts identifiers or key/value items
|
||||
|
||||
lint_doc_auto_cfg_wrong_literal =
|
||||
expected boolean for `#[doc(auto_cfg = ...)]`
|
||||
|
||||
lint_doc_invalid =
|
||||
invalid `doc` attribute
|
||||
|
||||
lint_doc_test_literal = `#![doc(test(...)]` does not take a literal
|
||||
|
||||
lint_doc_test_takes_list =
|
||||
`#[doc(test(...)]` takes a list of attributes
|
||||
|
||||
lint_doc_test_unknown =
|
||||
unknown `doc(test)` attribute `{$name}`
|
||||
|
||||
lint_doc_unknown_any =
|
||||
unknown `doc` attribute `{$name}`
|
||||
|
||||
lint_doc_unknown_include =
|
||||
unknown `doc` attribute `include`
|
||||
.suggestion = use `doc = include_str!` instead
|
||||
|
||||
lint_doc_unknown_passes =
|
||||
unknown `doc` attribute `{$name}`
|
||||
.note = `doc` attribute `{$name}` no longer functions; see issue #44136 <https://github.com/rust-lang/rust/issues/44136>
|
||||
.label = no longer functions
|
||||
.no_op_note = `doc({$name})` is now a no-op
|
||||
|
||||
lint_doc_unknown_plugins =
|
||||
unknown `doc` attribute `plugins`
|
||||
.note = `doc` attribute `plugins` no longer functions; see issue #44136 <https://github.com/rust-lang/rust/issues/44136> and CVE-2018-1000622 <https://nvd.nist.gov/vuln/detail/CVE-2018-1000622>
|
||||
.label = no longer functions
|
||||
.no_op_note = `doc(plugins)` is now a no-op
|
||||
|
||||
lint_doc_unknown_spotlight =
|
||||
unknown `doc` attribute `spotlight`
|
||||
.note = `doc(spotlight)` was renamed to `doc(notable_trait)`
|
||||
.suggestion = use `notable_trait` instead
|
||||
.no_op_note = `doc(spotlight)` is now a no-op
|
||||
|
||||
|
||||
lint_drop_glue =
|
||||
types that do not implement `Drop` can still have drop glue, consider instead using `{$needs_drop}` to detect whether a type is trivially dropped
|
||||
|
||||
|
|
@ -831,6 +883,7 @@ lint_unexpected_cfg_add_build_rs_println = or consider adding `{$build_rs_printl
|
|||
lint_unexpected_cfg_add_cargo_feature = consider using a Cargo feature instead
|
||||
lint_unexpected_cfg_add_cargo_toml_lint_cfg = or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint:{$cargo_toml_lint_cfg}
|
||||
lint_unexpected_cfg_add_cmdline_arg = to expect this configuration use `{$cmdline_arg}`
|
||||
lint_unexpected_cfg_boolean = you may have meant to use `{$literal}` (notice the capitalization). Doing so makes this predicate evaluate to `{$literal}` unconditionally
|
||||
lint_unexpected_cfg_cargo_update = the {$macro_kind} `{$macro_name}` may come from an old version of the `{$crate_name}` crate, try updating your dependency with `cargo update -p {$crate_name}`
|
||||
|
||||
lint_unexpected_cfg_define_features = consider defining some features in `Cargo.toml`
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ use rustc_attr_parsing::AttributeParser;
|
|||
use rustc_errors::{Applicability, LintDiagnostic};
|
||||
use rustc_feature::GateIssue;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_hir::attrs::{AttributeKind, DocAttribute};
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
|
||||
use rustc_hir::intravisit::FnKind as HirFnKind;
|
||||
|
|
@ -396,26 +396,16 @@ pub struct MissingDoc;
|
|||
impl_lint_pass!(MissingDoc => [MISSING_DOCS]);
|
||||
|
||||
fn has_doc(attr: &hir::Attribute) -> bool {
|
||||
if attr.is_doc_comment().is_some() {
|
||||
if matches!(attr, hir::Attribute::Parsed(AttributeKind::DocComment { .. })) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if !attr.has_name(sym::doc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if attr.value_str().is_some() {
|
||||
if let hir::Attribute::Parsed(AttributeKind::Doc(d)) = attr
|
||||
&& matches!(d.as_ref(), DocAttribute { hidden: Some(..), .. })
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if let Some(list) = attr.meta_item_list() {
|
||||
for meta in list {
|
||||
if meta.has_name(sym::hidden) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
|
|
@ -822,19 +812,23 @@ fn warn_if_doc(cx: &EarlyContext<'_>, node_span: Span, node_kind: &str, attrs: &
|
|||
let mut sugared_span: Option<Span> = None;
|
||||
|
||||
while let Some(attr) = attrs.next() {
|
||||
let is_doc_comment = attr.is_doc_comment();
|
||||
let (is_doc_comment, is_doc_attribute) = match &attr.kind {
|
||||
AttrKind::DocComment(..) => (true, false),
|
||||
AttrKind::Normal(normal) if normal.item.path == sym::doc => (true, true),
|
||||
_ => (false, false),
|
||||
};
|
||||
if is_doc_comment {
|
||||
sugared_span =
|
||||
Some(sugared_span.map_or(attr.span, |span| span.with_hi(attr.span.hi())));
|
||||
}
|
||||
|
||||
if attrs.peek().is_some_and(|next_attr| next_attr.is_doc_comment()) {
|
||||
if !is_doc_attribute && attrs.peek().is_some_and(|next_attr| next_attr.is_doc_comment()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let span = sugared_span.take().unwrap_or(attr.span);
|
||||
|
||||
if is_doc_comment || attr.has_name(sym::doc) {
|
||||
if is_doc_comment || is_doc_attribute {
|
||||
let sub = match attr.kind {
|
||||
AttrKind::DocComment(CommentKind::Line, _) | AttrKind::Normal(..) => {
|
||||
BuiltinUnusedDocCommentSub::PlainHelp
|
||||
|
|
|
|||
|
|
@ -367,5 +367,55 @@ pub fn decorate_attribute_lint(
|
|||
&AttributeLintKind::UnexpectedCfgValue(name, value) => {
|
||||
check_cfg::unexpected_cfg_value(sess, tcx, name, value).decorate_lint(diag)
|
||||
}
|
||||
&AttributeLintKind::DuplicateDocAlias { first_definition } => {
|
||||
lints::DocAliasDuplicated { first_defn: first_definition }.decorate_lint(diag)
|
||||
}
|
||||
|
||||
&AttributeLintKind::DocAutoCfgExpectsHideOrShow => {
|
||||
lints::DocAutoCfgExpectsHideOrShow.decorate_lint(diag)
|
||||
}
|
||||
|
||||
&AttributeLintKind::DocAutoCfgHideShowUnexpectedItem { attr_name } => {
|
||||
lints::DocAutoCfgHideShowUnexpectedItem { attr_name }.decorate_lint(diag)
|
||||
}
|
||||
|
||||
&AttributeLintKind::DocAutoCfgHideShowExpectsList { attr_name } => {
|
||||
lints::DocAutoCfgHideShowExpectsList { attr_name }.decorate_lint(diag)
|
||||
}
|
||||
|
||||
&AttributeLintKind::DocInvalid => { lints::DocInvalid }.decorate_lint(diag),
|
||||
|
||||
&AttributeLintKind::DocUnknownInclude { span, inner, value } => {
|
||||
lints::DocUnknownInclude { inner, value, sugg: (span, Applicability::MaybeIncorrect) }
|
||||
}
|
||||
.decorate_lint(diag),
|
||||
|
||||
&AttributeLintKind::DocUnknownSpotlight { span } => {
|
||||
lints::DocUnknownSpotlight { sugg_span: span }.decorate_lint(diag)
|
||||
}
|
||||
|
||||
&AttributeLintKind::DocUnknownPasses { name, span } => {
|
||||
lints::DocUnknownPasses { name, note_span: span }.decorate_lint(diag)
|
||||
}
|
||||
|
||||
&AttributeLintKind::DocUnknownPlugins { span } => {
|
||||
lints::DocUnknownPlugins { label_span: span }.decorate_lint(diag)
|
||||
}
|
||||
|
||||
&AttributeLintKind::DocUnknownAny { name } => {
|
||||
lints::DocUnknownAny { name }.decorate_lint(diag)
|
||||
}
|
||||
|
||||
&AttributeLintKind::DocAutoCfgWrongLiteral => {
|
||||
lints::DocAutoCfgWrongLiteral.decorate_lint(diag)
|
||||
}
|
||||
|
||||
&AttributeLintKind::DocTestTakesList => lints::DocTestTakesList.decorate_lint(diag),
|
||||
|
||||
&AttributeLintKind::DocTestUnknown { name } => {
|
||||
lints::DocTestUnknown { name }.decorate_lint(diag)
|
||||
}
|
||||
|
||||
&AttributeLintKind::DocTestLiteral => lints::DocTestLiteral.decorate_lint(diag),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,6 +138,16 @@ pub(super) fn unexpected_cfg_name(
|
|||
let is_from_external_macro = name_span.in_external_macro(sess.source_map());
|
||||
let mut is_feature_cfg = name == sym::feature;
|
||||
|
||||
fn miscapitalized_boolean(name: Symbol) -> Option<bool> {
|
||||
if name.as_str().eq_ignore_ascii_case("false") {
|
||||
Some(false)
|
||||
} else if name.as_str().eq_ignore_ascii_case("true") {
|
||||
Some(true)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
let code_sugg = if is_feature_cfg && is_from_cargo {
|
||||
lints::unexpected_cfg_name::CodeSuggestion::DefineFeatures
|
||||
// Suggest correct `version("..")` predicate syntax
|
||||
|
|
@ -148,6 +158,21 @@ pub(super) fn unexpected_cfg_name(
|
|||
between_name_and_value: name_span.between(value_span),
|
||||
after_value: value_span.shrink_to_hi(),
|
||||
}
|
||||
// Suggest a literal `false` instead
|
||||
// Detect miscapitalized `False`/`FALSE` etc, ensuring that this isn't `r#false`
|
||||
} else if value.is_none()
|
||||
// If this is a miscapitalized False/FALSE, suggest the boolean literal instead
|
||||
&& let Some(boolean) = miscapitalized_boolean(name)
|
||||
// Check this isn't a raw identifier
|
||||
&& sess
|
||||
.source_map()
|
||||
.span_to_snippet(name_span)
|
||||
.map_or(true, |snippet| !snippet.contains("r#"))
|
||||
{
|
||||
lints::unexpected_cfg_name::CodeSuggestion::BooleanLiteral {
|
||||
span: name_span,
|
||||
literal: boolean,
|
||||
}
|
||||
// Suggest the most probable if we found one
|
||||
} else if let Some(best_match) = find_best_match_for_name(&possibilities, name, None) {
|
||||
is_feature_cfg |= best_match == sym::feature;
|
||||
|
|
|
|||
|
|
@ -657,11 +657,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
|
|||
}
|
||||
|
||||
// `#[doc(hidden)]` disables missing_docs check.
|
||||
if attr.has_name(sym::doc)
|
||||
&& attr
|
||||
.meta_item_list()
|
||||
.is_some_and(|l| ast::attr::list_contains_name(&l, sym::hidden))
|
||||
{
|
||||
if attr.is_doc_hidden() {
|
||||
self.insert(
|
||||
LintId::of(MISSING_DOCS),
|
||||
LevelAndSource {
|
||||
|
|
|
|||
|
|
@ -2401,6 +2401,17 @@ pub(crate) mod unexpected_cfg_name {
|
|||
#[subdiagnostic]
|
||||
expected_names: Option<ExpectedNames>,
|
||||
},
|
||||
#[suggestion(
|
||||
lint_unexpected_cfg_boolean,
|
||||
applicability = "machine-applicable",
|
||||
style = "verbose",
|
||||
code = "{literal}"
|
||||
)]
|
||||
BooleanLiteral {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
literal: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
|
|
@ -3199,3 +3210,91 @@ pub(crate) struct UnusedVisibility {
|
|||
#[suggestion(style = "short", code = "", applicability = "machine-applicable")]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_doc_alias_duplicated)]
|
||||
pub(crate) struct DocAliasDuplicated {
|
||||
#[label]
|
||||
pub first_defn: Span,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_doc_auto_cfg_expects_hide_or_show)]
|
||||
pub(crate) struct DocAutoCfgExpectsHideOrShow;
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_doc_auto_cfg_hide_show_unexpected_item)]
|
||||
pub(crate) struct DocAutoCfgHideShowUnexpectedItem {
|
||||
pub attr_name: Symbol,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_doc_auto_cfg_hide_show_expects_list)]
|
||||
pub(crate) struct DocAutoCfgHideShowExpectsList {
|
||||
pub attr_name: Symbol,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_doc_invalid)]
|
||||
pub(crate) struct DocInvalid;
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_doc_unknown_include)]
|
||||
pub(crate) struct DocUnknownInclude {
|
||||
pub inner: &'static str,
|
||||
pub value: Symbol,
|
||||
#[suggestion(code = "#{inner}[doc = include_str!(\"{value}\")]")]
|
||||
pub sugg: (Span, Applicability),
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_doc_unknown_spotlight)]
|
||||
#[note]
|
||||
#[note(lint_no_op_note)]
|
||||
pub(crate) struct DocUnknownSpotlight {
|
||||
#[suggestion(style = "short", applicability = "machine-applicable", code = "notable_trait")]
|
||||
pub sugg_span: Span,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_doc_unknown_passes)]
|
||||
#[note]
|
||||
#[note(lint_no_op_note)]
|
||||
pub(crate) struct DocUnknownPasses {
|
||||
pub name: Symbol,
|
||||
#[label]
|
||||
pub note_span: Span,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_doc_unknown_plugins)]
|
||||
#[note]
|
||||
#[note(lint_no_op_note)]
|
||||
pub(crate) struct DocUnknownPlugins {
|
||||
#[label]
|
||||
pub label_span: Span,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_doc_unknown_any)]
|
||||
pub(crate) struct DocUnknownAny {
|
||||
pub name: Symbol,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_doc_auto_cfg_wrong_literal)]
|
||||
pub(crate) struct DocAutoCfgWrongLiteral;
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_doc_test_takes_list)]
|
||||
pub(crate) struct DocTestTakesList;
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_doc_test_unknown)]
|
||||
pub(crate) struct DocTestUnknown {
|
||||
pub name: Symbol,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_doc_test_literal)]
|
||||
pub(crate) struct DocTestLiteral;
|
||||
|
|
|
|||
|
|
@ -785,6 +785,41 @@ pub enum AttributeLintKind {
|
|||
},
|
||||
UnexpectedCfgName((Symbol, Span), Option<(Symbol, Span)>),
|
||||
UnexpectedCfgValue((Symbol, Span), Option<(Symbol, Span)>),
|
||||
DuplicateDocAlias {
|
||||
first_definition: Span,
|
||||
},
|
||||
DocAutoCfgExpectsHideOrShow,
|
||||
DocAutoCfgHideShowUnexpectedItem {
|
||||
attr_name: Symbol,
|
||||
},
|
||||
DocAutoCfgHideShowExpectsList {
|
||||
attr_name: Symbol,
|
||||
},
|
||||
DocInvalid,
|
||||
DocUnknownInclude {
|
||||
span: Span,
|
||||
inner: &'static str,
|
||||
value: Symbol,
|
||||
},
|
||||
DocUnknownSpotlight {
|
||||
span: Span,
|
||||
},
|
||||
DocUnknownPasses {
|
||||
name: Symbol,
|
||||
span: Span,
|
||||
},
|
||||
DocUnknownPlugins {
|
||||
span: Span,
|
||||
},
|
||||
DocUnknownAny {
|
||||
name: Symbol,
|
||||
},
|
||||
DocAutoCfgWrongLiteral,
|
||||
DocTestTakesList,
|
||||
DocTestUnknown {
|
||||
name: Symbol,
|
||||
},
|
||||
DocTestLiteral,
|
||||
}
|
||||
|
||||
pub type RegisteredTools = FxIndexSet<Ident>;
|
||||
|
|
|
|||
|
|
@ -870,25 +870,20 @@ fn analyze_attr(attr: &hir::Attribute, state: &mut AnalyzeAttrState<'_>) -> bool
|
|||
&& !rustc_feature::encode_cross_crate(name)
|
||||
{
|
||||
// Attributes not marked encode-cross-crate don't need to be encoded for downstream crates.
|
||||
} else if attr.doc_str().is_some() {
|
||||
} else if let hir::Attribute::Parsed(AttributeKind::DocComment { .. }) = attr {
|
||||
// We keep all doc comments reachable to rustdoc because they might be "imported" into
|
||||
// downstream crates if they use `#[doc(inline)]` to copy an item's documentation into
|
||||
// their own.
|
||||
if state.is_exported {
|
||||
should_encode = true;
|
||||
}
|
||||
} else if attr.has_name(sym::doc) {
|
||||
} else if let hir::Attribute::Parsed(AttributeKind::Doc(d)) = attr {
|
||||
// If this is a `doc` attribute that doesn't have anything except maybe `inline` (as in
|
||||
// `#[doc(inline)]`), then we can remove it. It won't be inlinable in downstream crates.
|
||||
if let Some(item_list) = attr.meta_item_list() {
|
||||
for item in item_list {
|
||||
if !item.has_name(sym::inline) {
|
||||
should_encode = true;
|
||||
if item.has_name(sym::hidden) {
|
||||
state.is_doc_hidden = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if d.inline.is_empty() {
|
||||
should_encode = true;
|
||||
if d.hidden.is_some() {
|
||||
state.is_doc_hidden = true;
|
||||
}
|
||||
}
|
||||
} else if let &[sym::diagnostic, seg] = &*attr.path() {
|
||||
|
|
|
|||
|
|
@ -4,12 +4,14 @@ use std::{fmt, iter};
|
|||
|
||||
use rustc_abi::{Float, Integer, IntegerType, Size};
|
||||
use rustc_apfloat::Float as _;
|
||||
use rustc_ast::attr::AttributeExt;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hashes::Hash128;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_hir::def::{CtorOf, DefKind, Res};
|
||||
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId};
|
||||
use rustc_hir::limit::Limit;
|
||||
|
|
@ -1664,16 +1666,14 @@ pub fn reveal_opaque_types_in_bounds<'tcx>(
|
|||
|
||||
/// Determines whether an item is directly annotated with `doc(hidden)`.
|
||||
fn is_doc_hidden(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
|
||||
tcx.get_attrs(def_id, sym::doc)
|
||||
.filter_map(|attr| attr.meta_item_list())
|
||||
.any(|items| items.iter().any(|item| item.has_name(sym::hidden)))
|
||||
let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id));
|
||||
attrs.iter().any(|attr| attr.is_doc_hidden())
|
||||
}
|
||||
|
||||
/// Determines whether an item is annotated with `doc(notable_trait)`.
|
||||
pub fn is_doc_notable_trait(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
|
||||
tcx.get_attrs(def_id, sym::doc)
|
||||
.filter_map(|attr| attr.meta_item_list())
|
||||
.any(|items| items.iter().any(|item| item.has_name(sym::notable_trait)))
|
||||
let attrs = tcx.get_all_attrs(def_id);
|
||||
attrs.iter().any(|attr| matches!(attr, hir::Attribute::Parsed(AttributeKind::Doc(doc)) if doc.notable_trait.is_some()))
|
||||
}
|
||||
|
||||
/// Determines whether an item is an intrinsic (which may be via Abi or via the `rustc_intrinsic` attribute).
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use rustc_ast::{self as ast, AsmMacro};
|
||||
use rustc_ast::{self as ast, AsmMacro, MgcaDisambiguation};
|
||||
use rustc_span::{Span, Symbol, kw};
|
||||
|
||||
use super::{ExpKeywordPair, ForceCollect, IdentIsRaw, Trailing, UsePreAttrPos};
|
||||
|
|
@ -149,7 +149,7 @@ fn parse_asm_operand<'a>(
|
|||
let block = p.parse_block()?;
|
||||
ast::InlineAsmOperand::Label { block }
|
||||
} else if p.eat_keyword(exp!(Const)) {
|
||||
let anon_const = p.parse_expr_anon_const()?;
|
||||
let anon_const = p.parse_expr_anon_const(|_, _| MgcaDisambiguation::AnonConst)?;
|
||||
ast::InlineAsmOperand::Const { anon_const }
|
||||
} else if p.eat_keyword(exp!(Sym)) {
|
||||
let expr = p.parse_expr()?;
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ use rustc_ast::token::{self, Lit, LitKind, Token, TokenKind};
|
|||
use rustc_ast::util::parser::AssocOp;
|
||||
use rustc_ast::{
|
||||
self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode,
|
||||
Block, BlockCheckMode, Expr, ExprKind, GenericArg, Generics, Item, ItemKind, Param, Pat,
|
||||
PatKind, Path, PathSegment, QSelf, Recovered, Ty, TyKind,
|
||||
Block, BlockCheckMode, Expr, ExprKind, GenericArg, Generics, Item, ItemKind,
|
||||
MgcaDisambiguation, Param, Pat, PatKind, Path, PathSegment, QSelf, Recovered, Ty, TyKind,
|
||||
};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
|
|
@ -31,16 +31,15 @@ use crate::errors::{
|
|||
AddParen, AmbiguousPlus, AsyncMoveBlockIn2015, AsyncUseBlockIn2015, AttributeOnParamType,
|
||||
AwaitSuggestion, BadQPathStage2, BadTypePlus, BadTypePlusSub, ColonAsSemi,
|
||||
ComparisonOperatorsCannotBeChained, ComparisonOperatorsCannotBeChainedSugg,
|
||||
ConstGenericWithoutBraces, ConstGenericWithoutBracesSugg, DocCommentDoesNotDocumentAnything,
|
||||
DocCommentOnParamType, DoubleColonInBound, ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg,
|
||||
GenericParamsWithoutAngleBrackets, GenericParamsWithoutAngleBracketsSugg,
|
||||
HelpIdentifierStartsWithNumber, HelpUseLatestEdition, InInTypo, IncorrectAwait,
|
||||
IncorrectSemicolon, IncorrectUseOfAwait, IncorrectUseOfUse, PatternMethodParamWithoutBody,
|
||||
QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst, StructLiteralBodyWithoutPath,
|
||||
StructLiteralBodyWithoutPathSugg, SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma,
|
||||
TernaryOperator, TernaryOperatorSuggestion, UnexpectedConstInGenericParam,
|
||||
UnexpectedConstParamDeclaration, UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets,
|
||||
UseEqInstead, WrapType,
|
||||
DocCommentDoesNotDocumentAnything, DocCommentOnParamType, DoubleColonInBound,
|
||||
ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg, GenericParamsWithoutAngleBrackets,
|
||||
GenericParamsWithoutAngleBracketsSugg, HelpIdentifierStartsWithNumber, HelpUseLatestEdition,
|
||||
InInTypo, IncorrectAwait, IncorrectSemicolon, IncorrectUseOfAwait, IncorrectUseOfUse,
|
||||
PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst,
|
||||
StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg, SuggAddMissingLetStmt,
|
||||
SuggEscapeIdentifier, SuggRemoveComma, TernaryOperator, TernaryOperatorSuggestion,
|
||||
UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration,
|
||||
UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead, WrapType,
|
||||
};
|
||||
use crate::parser::FnContext;
|
||||
use crate::parser::attr::InnerAttrPolicy;
|
||||
|
|
@ -2558,36 +2557,6 @@ impl<'a> Parser<'a> {
|
|||
Ok(false) // Don't continue.
|
||||
}
|
||||
|
||||
/// Attempt to parse a generic const argument that has not been enclosed in braces.
|
||||
/// There are a limited number of expressions that are permitted without being encoded
|
||||
/// in braces:
|
||||
/// - Literals.
|
||||
/// - Single-segment paths (i.e. standalone generic const parameters).
|
||||
/// All other expressions that can be parsed will emit an error suggesting the expression be
|
||||
/// wrapped in braces.
|
||||
pub(super) fn handle_unambiguous_unbraced_const_arg(&mut self) -> PResult<'a, Box<Expr>> {
|
||||
let start = self.token.span;
|
||||
let attrs = self.parse_outer_attributes()?;
|
||||
let (expr, _) =
|
||||
self.parse_expr_res(Restrictions::CONST_EXPR, attrs).map_err(|mut err| {
|
||||
err.span_label(
|
||||
start.shrink_to_lo(),
|
||||
"while parsing a const generic argument starting here",
|
||||
);
|
||||
err
|
||||
})?;
|
||||
if !self.expr_is_valid_const_arg(&expr) {
|
||||
self.dcx().emit_err(ConstGenericWithoutBraces {
|
||||
span: expr.span,
|
||||
sugg: ConstGenericWithoutBracesSugg {
|
||||
left: expr.span.shrink_to_lo(),
|
||||
right: expr.span.shrink_to_hi(),
|
||||
},
|
||||
});
|
||||
}
|
||||
Ok(expr)
|
||||
}
|
||||
|
||||
fn recover_const_param_decl(&mut self, ty_generics: Option<&Generics>) -> Option<GenericArg> {
|
||||
let snapshot = self.create_snapshot_for_diagnostic();
|
||||
let param = match self.parse_const_param(AttrVec::new()) {
|
||||
|
|
@ -2623,7 +2592,11 @@ impl<'a> Parser<'a> {
|
|||
self.dcx().emit_err(UnexpectedConstParamDeclaration { span: param.span(), sugg });
|
||||
|
||||
let value = self.mk_expr_err(param.span(), guar);
|
||||
Some(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value }))
|
||||
Some(GenericArg::Const(AnonConst {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
value,
|
||||
mgca_disambiguation: MgcaDisambiguation::Direct,
|
||||
}))
|
||||
}
|
||||
|
||||
pub(super) fn recover_const_param_declaration(
|
||||
|
|
@ -2707,7 +2680,11 @@ impl<'a> Parser<'a> {
|
|||
);
|
||||
let guar = err.emit();
|
||||
let value = self.mk_expr_err(start.to(expr.span), guar);
|
||||
return Ok(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value }));
|
||||
return Ok(GenericArg::Const(AnonConst {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
value,
|
||||
mgca_disambiguation: MgcaDisambiguation::Direct,
|
||||
}));
|
||||
} else if snapshot.token == token::Colon
|
||||
&& expr.span.lo() == snapshot.token.span.hi()
|
||||
&& matches!(expr.kind, ExprKind::Path(..))
|
||||
|
|
@ -2776,7 +2753,11 @@ impl<'a> Parser<'a> {
|
|||
);
|
||||
let guar = err.emit();
|
||||
let value = self.mk_expr_err(span, guar);
|
||||
GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value })
|
||||
GenericArg::Const(AnonConst {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
value,
|
||||
mgca_disambiguation: MgcaDisambiguation::Direct,
|
||||
})
|
||||
}
|
||||
|
||||
/// Some special error handling for the "top-level" patterns in a match arm,
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ use rustc_ast::visit::{Visitor, walk_expr};
|
|||
use rustc_ast::{
|
||||
self as ast, AnonConst, Arm, AssignOp, AssignOpKind, AttrStyle, AttrVec, BinOp, BinOpKind,
|
||||
BlockCheckMode, CaptureBy, ClosureBinder, DUMMY_NODE_ID, Expr, ExprField, ExprKind, FnDecl,
|
||||
FnRetTy, Label, MacCall, MetaItemLit, Movability, Param, RangeLimits, StmtKind, Ty, TyKind,
|
||||
UnOp, UnsafeBinderCastKind, YieldKind,
|
||||
FnRetTy, Label, MacCall, MetaItemLit, MgcaDisambiguation, Movability, Param, RangeLimits,
|
||||
StmtKind, Ty, TyKind, UnOp, UnsafeBinderCastKind, YieldKind,
|
||||
};
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
use rustc_errors::{Applicability, Diag, PResult, StashKey, Subdiagnostic};
|
||||
|
|
@ -85,8 +85,15 @@ impl<'a> Parser<'a> {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn parse_expr_anon_const(&mut self) -> PResult<'a, AnonConst> {
|
||||
self.parse_expr().map(|value| AnonConst { id: DUMMY_NODE_ID, value })
|
||||
pub fn parse_expr_anon_const(
|
||||
&mut self,
|
||||
mgca_disambiguation: impl FnOnce(&Self, &Expr) -> MgcaDisambiguation,
|
||||
) -> PResult<'a, AnonConst> {
|
||||
self.parse_expr().map(|value| AnonConst {
|
||||
id: DUMMY_NODE_ID,
|
||||
mgca_disambiguation: mgca_disambiguation(self, &value),
|
||||
value,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_expr_catch_underscore(
|
||||
|
|
@ -1615,7 +1622,18 @@ impl<'a> Parser<'a> {
|
|||
let first_expr = self.parse_expr()?;
|
||||
if self.eat(exp!(Semi)) {
|
||||
// Repeating array syntax: `[ 0; 512 ]`
|
||||
let count = self.parse_expr_anon_const()?;
|
||||
let count = if self.token.is_keyword(kw::Const)
|
||||
&& self.look_ahead(1, |t| *t == token::OpenBrace)
|
||||
{
|
||||
// While we could just disambiguate `Direct` from `AnonConst` by
|
||||
// treating all const block exprs as `AnonConst`, that would
|
||||
// complicate the DefCollector and likely all other visitors.
|
||||
// So we strip the const blockiness and just store it as a block
|
||||
// in the AST with the extra disambiguator on the AnonConst
|
||||
self.parse_expr_anon_const(|_, _| MgcaDisambiguation::AnonConst)?
|
||||
} else {
|
||||
self.parse_expr_anon_const(|this, expr| this.mgca_direct_lit_hack(expr))?
|
||||
};
|
||||
self.expect(close)?;
|
||||
ExprKind::Repeat(first_expr, count)
|
||||
} else if self.eat(exp!(Comma)) {
|
||||
|
|
|
|||
|
|
@ -1431,7 +1431,7 @@ impl<'a> Parser<'a> {
|
|||
|
||||
let rhs = if self.eat(exp!(Eq)) {
|
||||
if attr::contains_name(attrs, sym::type_const) {
|
||||
Some(ConstItemRhs::TypeConst(self.parse_expr_anon_const()?))
|
||||
Some(ConstItemRhs::TypeConst(self.parse_const_arg()?))
|
||||
} else {
|
||||
Some(ConstItemRhs::Body(self.parse_expr()?))
|
||||
}
|
||||
|
|
@ -1650,8 +1650,11 @@ impl<'a> Parser<'a> {
|
|||
VariantData::Unit(DUMMY_NODE_ID)
|
||||
};
|
||||
|
||||
let disr_expr =
|
||||
if this.eat(exp!(Eq)) { Some(this.parse_expr_anon_const()?) } else { None };
|
||||
let disr_expr = if this.eat(exp!(Eq)) {
|
||||
Some(this.parse_expr_anon_const(|_, _| MgcaDisambiguation::AnonConst)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let vr = ast::Variant {
|
||||
ident,
|
||||
|
|
@ -1864,7 +1867,7 @@ impl<'a> Parser<'a> {
|
|||
if p.token == token::Eq {
|
||||
let mut snapshot = p.create_snapshot_for_diagnostic();
|
||||
snapshot.bump();
|
||||
match snapshot.parse_expr_anon_const() {
|
||||
match snapshot.parse_expr_anon_const(|_, _| MgcaDisambiguation::AnonConst) {
|
||||
Ok(const_expr) => {
|
||||
let sp = ty.span.shrink_to_hi().to(const_expr.value.span);
|
||||
p.psess.gated_spans.gate(sym::default_field_values, sp);
|
||||
|
|
@ -2066,7 +2069,7 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
let default = if self.token == token::Eq {
|
||||
self.bump();
|
||||
let const_expr = self.parse_expr_anon_const()?;
|
||||
let const_expr = self.parse_expr_anon_const(|_, _| MgcaDisambiguation::AnonConst)?;
|
||||
let sp = ty.span.shrink_to_hi().to(const_expr.value.span);
|
||||
self.psess.gated_spans.gate(sym::default_field_values, sp);
|
||||
Some(const_expr)
|
||||
|
|
|
|||
|
|
@ -35,9 +35,9 @@ use rustc_ast::tokenstream::{
|
|||
};
|
||||
use rustc_ast::util::case::Case;
|
||||
use rustc_ast::{
|
||||
self as ast, AnonConst, AttrArgs, AttrId, ByRef, Const, CoroutineKind, DUMMY_NODE_ID,
|
||||
DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, Mutability, Recovered, Safety, StrLit,
|
||||
Visibility, VisibilityKind,
|
||||
self as ast, AnonConst, AttrArgs, AttrId, BlockCheckMode, ByRef, Const, CoroutineKind,
|
||||
DUMMY_NODE_ID, DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, MgcaDisambiguation,
|
||||
Mutability, Recovered, Safety, StrLit, Visibility, VisibilityKind,
|
||||
};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
|
|
@ -727,7 +727,10 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
|
||||
fn check_const_arg(&mut self) -> bool {
|
||||
self.check_or_expected(self.token.can_begin_const_arg(), TokenType::Const)
|
||||
let is_mcg_arg = self.check_or_expected(self.token.can_begin_const_arg(), TokenType::Const);
|
||||
let is_mgca_arg = self.is_keyword_ahead(0, &[kw::Const])
|
||||
&& self.look_ahead(1, |t| *t == token::OpenBrace);
|
||||
is_mcg_arg || is_mgca_arg
|
||||
}
|
||||
|
||||
fn check_const_closure(&self) -> bool {
|
||||
|
|
@ -1299,6 +1302,20 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_mgca_const_block(&mut self, gate_syntax: bool) -> PResult<'a, AnonConst> {
|
||||
self.expect_keyword(exp!(Const))?;
|
||||
let kw_span = self.token.span;
|
||||
let value = self.parse_expr_block(None, self.token.span, BlockCheckMode::Default)?;
|
||||
if gate_syntax {
|
||||
self.psess.gated_spans.gate(sym::min_generic_const_args, kw_span.to(value.span));
|
||||
}
|
||||
Ok(AnonConst {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
value,
|
||||
mgca_disambiguation: MgcaDisambiguation::AnonConst,
|
||||
})
|
||||
}
|
||||
|
||||
/// Parses inline const expressions.
|
||||
fn parse_const_block(&mut self, span: Span, pat: bool) -> PResult<'a, Box<Expr>> {
|
||||
self.expect_keyword(exp!(Const))?;
|
||||
|
|
@ -1306,6 +1323,7 @@ impl<'a> Parser<'a> {
|
|||
let anon_const = AnonConst {
|
||||
id: DUMMY_NODE_ID,
|
||||
value: self.mk_expr(blk.span, ExprKind::Block(blk, None)),
|
||||
mgca_disambiguation: MgcaDisambiguation::AnonConst,
|
||||
};
|
||||
let blk_span = anon_const.value.span;
|
||||
let kind = if pat {
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ use ast::token::IdentIsRaw;
|
|||
use rustc_ast::token::{self, MetaVarKind, Token, TokenKind};
|
||||
use rustc_ast::{
|
||||
self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AssocItemConstraint,
|
||||
AssocItemConstraintKind, BlockCheckMode, GenericArg, GenericArgs, Generics, ParenthesizedArgs,
|
||||
Path, PathSegment, QSelf,
|
||||
AssocItemConstraintKind, BlockCheckMode, GenericArg, GenericArgs, Generics, MgcaDisambiguation,
|
||||
ParenthesizedArgs, Path, PathSegment, QSelf,
|
||||
};
|
||||
use rustc_errors::{Applicability, Diag, PResult};
|
||||
use rustc_span::{BytePos, Ident, Span, kw, sym};
|
||||
|
|
@ -16,12 +16,13 @@ use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
|
|||
use super::{Parser, Restrictions, TokenType};
|
||||
use crate::ast::{PatKind, TyKind};
|
||||
use crate::errors::{
|
||||
self, AttributeOnEmptyType, AttributeOnGenericArg, FnPathFoundNamedParams,
|
||||
PathFoundAttributeInParams, PathFoundCVariadicParams, PathSingleColon, PathTripleColon,
|
||||
self, AttributeOnEmptyType, AttributeOnGenericArg, ConstGenericWithoutBraces,
|
||||
ConstGenericWithoutBracesSugg, FnPathFoundNamedParams, PathFoundAttributeInParams,
|
||||
PathFoundCVariadicParams, PathSingleColon, PathTripleColon,
|
||||
};
|
||||
use crate::exp;
|
||||
use crate::parser::{
|
||||
CommaRecoveryMode, ExprKind, FnContext, FnParseMode, RecoverColon, RecoverComma,
|
||||
CommaRecoveryMode, Expr, ExprKind, FnContext, FnParseMode, RecoverColon, RecoverComma,
|
||||
};
|
||||
|
||||
/// Specifies how to parse a path.
|
||||
|
|
@ -870,12 +871,75 @@ impl<'a> Parser<'a> {
|
|||
/// the caller.
|
||||
pub(super) fn parse_const_arg(&mut self) -> PResult<'a, AnonConst> {
|
||||
// Parse const argument.
|
||||
let value = if self.token.kind == token::OpenBrace {
|
||||
self.parse_expr_block(None, self.token.span, BlockCheckMode::Default)?
|
||||
let (value, mgca_disambiguation) = if self.token.kind == token::OpenBrace {
|
||||
let value = self.parse_expr_block(None, self.token.span, BlockCheckMode::Default)?;
|
||||
(value, MgcaDisambiguation::Direct)
|
||||
} else if self.token.is_keyword(kw::Const) {
|
||||
// While we could just disambiguate `Direct` from `AnonConst` by
|
||||
// treating all const block exprs as `AnonConst`, that would
|
||||
// complicate the DefCollector and likely all other visitors.
|
||||
// So we strip the const blockiness and just store it as a block
|
||||
// in the AST with the extra disambiguator on the AnonConst
|
||||
let value = self.parse_mgca_const_block(true)?;
|
||||
(value.value, MgcaDisambiguation::AnonConst)
|
||||
} else {
|
||||
self.handle_unambiguous_unbraced_const_arg()?
|
||||
self.parse_unambiguous_unbraced_const_arg()?
|
||||
};
|
||||
Ok(AnonConst { id: ast::DUMMY_NODE_ID, value })
|
||||
Ok(AnonConst { id: ast::DUMMY_NODE_ID, value, mgca_disambiguation })
|
||||
}
|
||||
|
||||
/// Attempt to parse a const argument that has not been enclosed in braces.
|
||||
/// There are a limited number of expressions that are permitted without being
|
||||
/// enclosed in braces:
|
||||
/// - Literals.
|
||||
/// - Single-segment paths (i.e. standalone generic const parameters).
|
||||
/// All other expressions that can be parsed will emit an error suggesting the expression be
|
||||
/// wrapped in braces.
|
||||
pub(super) fn parse_unambiguous_unbraced_const_arg(
|
||||
&mut self,
|
||||
) -> PResult<'a, (Box<Expr>, MgcaDisambiguation)> {
|
||||
let start = self.token.span;
|
||||
let attrs = self.parse_outer_attributes()?;
|
||||
let (expr, _) =
|
||||
self.parse_expr_res(Restrictions::CONST_EXPR, attrs).map_err(|mut err| {
|
||||
err.span_label(
|
||||
start.shrink_to_lo(),
|
||||
"while parsing a const generic argument starting here",
|
||||
);
|
||||
err
|
||||
})?;
|
||||
if !self.expr_is_valid_const_arg(&expr) {
|
||||
self.dcx().emit_err(ConstGenericWithoutBraces {
|
||||
span: expr.span,
|
||||
sugg: ConstGenericWithoutBracesSugg {
|
||||
left: expr.span.shrink_to_lo(),
|
||||
right: expr.span.shrink_to_hi(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
let mgca_disambiguation = self.mgca_direct_lit_hack(&expr);
|
||||
Ok((expr, mgca_disambiguation))
|
||||
}
|
||||
|
||||
/// Under `min_generic_const_args` we still allow *some* anon consts to be written without
|
||||
/// a `const` block as it makes things quite a lot nicer. This function is useful for contexts
|
||||
/// where we would like to use `MgcaDisambiguation::Direct` but need to fudge it to be `AnonConst`
|
||||
/// in the presence of literals.
|
||||
//
|
||||
/// FIXME(min_generic_const_args): In the long term it would be nice to have a way to directly
|
||||
/// represent literals in `hir::ConstArgKind` so that we can remove this special case by not
|
||||
/// needing an anon const.
|
||||
pub fn mgca_direct_lit_hack(&self, expr: &Expr) -> MgcaDisambiguation {
|
||||
match &expr.kind {
|
||||
ast::ExprKind::Lit(_) => MgcaDisambiguation::AnonConst,
|
||||
ast::ExprKind::Unary(ast::UnOp::Neg, expr)
|
||||
if matches!(expr.kind, ast::ExprKind::Lit(_)) =>
|
||||
{
|
||||
MgcaDisambiguation::AnonConst
|
||||
}
|
||||
_ => MgcaDisambiguation::Direct,
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a generic argument in a path segment.
|
||||
|
|
@ -976,7 +1040,11 @@ impl<'a> Parser<'a> {
|
|||
GenericArg::Type(_) => GenericArg::Type(self.mk_ty(attr_span, TyKind::Err(guar))),
|
||||
GenericArg::Const(_) => {
|
||||
let error_expr = self.mk_expr(attr_span, ExprKind::Err(guar));
|
||||
GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value: error_expr })
|
||||
GenericArg::Const(AnonConst {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
value: error_expr,
|
||||
mgca_disambiguation: MgcaDisambiguation::Direct,
|
||||
})
|
||||
}
|
||||
GenericArg::Lifetime(lt) => GenericArg::Lifetime(lt),
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ use rustc_ast::token::{self, IdentIsRaw, MetaVarKind, Token, TokenKind};
|
|||
use rustc_ast::util::case::Case;
|
||||
use rustc_ast::{
|
||||
self as ast, BoundAsyncness, BoundConstness, BoundPolarity, DUMMY_NODE_ID, FnPtrTy, FnRetTy,
|
||||
GenericBound, GenericBounds, GenericParam, Generics, Lifetime, MacCall, MutTy, Mutability,
|
||||
Pinnedness, PolyTraitRef, PreciseCapturingArg, TraitBoundModifiers, TraitObjectSyntax, Ty,
|
||||
TyKind, UnsafeBinderTy,
|
||||
GenericBound, GenericBounds, GenericParam, Generics, Lifetime, MacCall, MgcaDisambiguation,
|
||||
MutTy, Mutability, Pinnedness, PolyTraitRef, PreciseCapturingArg, TraitBoundModifiers,
|
||||
TraitObjectSyntax, Ty, TyKind, UnsafeBinderTy,
|
||||
};
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
use rustc_errors::{Applicability, Diag, E0516, PResult};
|
||||
|
|
@ -658,7 +658,19 @@ impl<'a> Parser<'a> {
|
|||
};
|
||||
|
||||
let ty = if self.eat(exp!(Semi)) {
|
||||
let mut length = self.parse_expr_anon_const()?;
|
||||
let mut length = if self.token.is_keyword(kw::Const)
|
||||
&& self.look_ahead(1, |t| *t == token::OpenBrace)
|
||||
{
|
||||
// While we could just disambiguate `Direct` from `AnonConst` by
|
||||
// treating all const block exprs as `AnonConst`, that would
|
||||
// complicate the DefCollector and likely all other visitors.
|
||||
// So we strip the const blockiness and just store it as a block
|
||||
// in the AST with the extra disambiguator on the AnonConst
|
||||
self.parse_mgca_const_block(false)?
|
||||
} else {
|
||||
self.parse_expr_anon_const(|this, expr| this.mgca_direct_lit_hack(expr))?
|
||||
};
|
||||
|
||||
if let Err(e) = self.expect(exp!(CloseBracket)) {
|
||||
// Try to recover from `X<Y, ...>` when `X::<Y, ...>` works
|
||||
self.check_mistyped_turbofish_with_multiple_type_params(e, &mut length.value)?;
|
||||
|
|
@ -699,8 +711,9 @@ impl<'a> Parser<'a> {
|
|||
_ = self.eat(exp!(Comma)) || self.eat(exp!(Colon)) || self.eat(exp!(Star));
|
||||
let suggestion_span = self.prev_token.span.with_lo(hi);
|
||||
|
||||
// FIXME(mgca): recovery is broken for `const {` args
|
||||
// we first try to parse pattern like `[u8 5]`
|
||||
let length = match self.parse_expr_anon_const() {
|
||||
let length = match self.parse_expr_anon_const(|_, _| MgcaDisambiguation::Direct) {
|
||||
Ok(length) => length,
|
||||
Err(e) => {
|
||||
e.cancel();
|
||||
|
|
@ -788,7 +801,7 @@ impl<'a> Parser<'a> {
|
|||
/// an error type.
|
||||
fn parse_typeof_ty(&mut self, lo: Span) -> PResult<'a, TyKind> {
|
||||
self.expect(exp!(OpenParen))?;
|
||||
let _expr = self.parse_expr_anon_const()?;
|
||||
let _expr = self.parse_expr_anon_const(|_, _| MgcaDisambiguation::AnonConst)?;
|
||||
self.expect(exp!(CloseParen))?;
|
||||
let span = lo.to(self.prev_token.span);
|
||||
let guar = self
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ edition = "2024"
|
|||
rustc_abi = { path = "../rustc_abi" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_ast_lowering = { path = "../rustc_ast_lowering" }
|
||||
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
|
||||
rustc_attr_parsing = { path = "../rustc_attr_parsing" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_errors = { path = "../rustc_errors" }
|
||||
|
|
|
|||
|
|
@ -106,60 +106,15 @@ passes_diagnostic_diagnostic_on_unimplemented_only_for_traits =
|
|||
passes_diagnostic_item_first_defined =
|
||||
the diagnostic item is first defined here
|
||||
|
||||
passes_doc_alias_bad_char =
|
||||
{$char_} character isn't allowed in {$attr_str}
|
||||
|
||||
passes_doc_alias_bad_location =
|
||||
{$attr_str} isn't allowed on {$location}
|
||||
|
||||
passes_doc_alias_duplicated = doc alias is duplicated
|
||||
.label = first defined here
|
||||
|
||||
passes_doc_alias_empty =
|
||||
{$attr_str} attribute cannot have empty value
|
||||
|
||||
passes_doc_alias_malformed =
|
||||
doc alias attribute expects a string `#[doc(alias = "a")]` or a list of strings `#[doc(alias("a", "b"))]`
|
||||
`#[doc(alias = "...")]` isn't allowed on {$location}
|
||||
|
||||
passes_doc_alias_not_an_alias =
|
||||
{$attr_str} is the same as the item's name
|
||||
|
||||
passes_doc_alias_not_string_literal =
|
||||
`#[doc(alias("a"))]` expects string literals
|
||||
|
||||
passes_doc_alias_start_end =
|
||||
{$attr_str} cannot start or end with ' '
|
||||
|
||||
passes_doc_attr_expects_no_value =
|
||||
`doc({$attr_name})` does not accept a value
|
||||
.suggestion = use `doc({$attr_name})`
|
||||
|
||||
passes_doc_attr_expects_string =
|
||||
`doc({$attr_name})` expects a string value
|
||||
.suggestion = use `doc({$attr_name} = "...")`
|
||||
`#[doc(alias = "{$attr_str}"]` is the same as the item's name
|
||||
|
||||
passes_doc_attr_not_crate_level =
|
||||
`#![doc({$attr_name} = "...")]` isn't allowed as a crate-level attribute
|
||||
|
||||
passes_doc_attribute_not_attribute =
|
||||
nonexistent builtin attribute `{$attribute}` used in `#[doc(attribute = "...")]`
|
||||
.help = only existing builtin attributes are allowed in core/std
|
||||
|
||||
passes_doc_auto_cfg_expects_hide_or_show =
|
||||
only `hide` or `show` are allowed in `#[doc(auto_cfg(...))]`
|
||||
|
||||
passes_doc_auto_cfg_hide_show_expects_list =
|
||||
`#![doc(auto_cfg({$attr_name}(...)))]` expects a list of items
|
||||
|
||||
passes_doc_auto_cfg_hide_show_unexpected_item =
|
||||
`#![doc(auto_cfg({$attr_name}(...)))]` only accepts identifiers or key/value items
|
||||
|
||||
passes_doc_auto_cfg_wrong_literal =
|
||||
expected boolean for `#[doc(auto_cfg = ...)]`
|
||||
|
||||
passes_doc_expect_str =
|
||||
doc {$attr_name} attribute expects a string: #[doc({$attr_name} = "a")]
|
||||
|
||||
passes_doc_fake_variadic_not_valid =
|
||||
`#[doc(fake_variadic)]` must be used on the first of a set of tuple or fn pointer trait impls with varying arity
|
||||
|
||||
|
|
@ -179,19 +134,12 @@ passes_doc_inline_only_use =
|
|||
.not_a_use_item_label = not a `use` item
|
||||
.note = read <https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#inline-and-no_inline> for more information
|
||||
|
||||
passes_doc_invalid =
|
||||
invalid `doc` attribute
|
||||
|
||||
passes_doc_keyword_attribute_empty_mod =
|
||||
`#[doc({$attr_name} = "...")]` should be used on empty modules
|
||||
|
||||
passes_doc_keyword_attribute_not_mod =
|
||||
`#[doc({$attr_name} = "...")]` should be used on modules
|
||||
|
||||
passes_doc_keyword_not_keyword =
|
||||
nonexistent keyword `{$keyword}` used in `#[doc(keyword = "...")]`
|
||||
.help = only existing keywords are allowed in core/std
|
||||
|
||||
passes_doc_keyword_only_impl =
|
||||
`#[doc(keyword = "...")]` should be used on impl blocks
|
||||
|
||||
|
|
@ -212,39 +160,6 @@ passes_doc_rust_logo =
|
|||
passes_doc_search_unbox_invalid =
|
||||
`#[doc(search_unbox)]` should be used on generic structs and enums
|
||||
|
||||
passes_doc_test_literal = `#![doc(test(...)]` does not take a literal
|
||||
|
||||
passes_doc_test_takes_list =
|
||||
`#[doc(test(...)]` takes a list of attributes
|
||||
|
||||
passes_doc_test_unknown =
|
||||
unknown `doc(test)` attribute `{$path}`
|
||||
|
||||
passes_doc_test_unknown_any =
|
||||
unknown `doc` attribute `{$path}`
|
||||
|
||||
passes_doc_test_unknown_include =
|
||||
unknown `doc` attribute `{$path}`
|
||||
.suggestion = use `doc = include_str!` instead
|
||||
|
||||
passes_doc_test_unknown_passes =
|
||||
unknown `doc` attribute `{$path}`
|
||||
.note = `doc` attribute `{$path}` no longer functions; see issue #44136 <https://github.com/rust-lang/rust/issues/44136>
|
||||
.label = no longer functions
|
||||
.no_op_note = `doc({$path})` is now a no-op
|
||||
|
||||
passes_doc_test_unknown_plugins =
|
||||
unknown `doc` attribute `{$path}`
|
||||
.note = `doc` attribute `{$path}` no longer functions; see issue #44136 <https://github.com/rust-lang/rust/issues/44136> and CVE-2018-1000622 <https://nvd.nist.gov/vuln/detail/CVE-2018-1000622>
|
||||
.label = no longer functions
|
||||
.no_op_note = `doc({$path})` is now a no-op
|
||||
|
||||
passes_doc_test_unknown_spotlight =
|
||||
unknown `doc` attribute `{$path}`
|
||||
.note = `doc(spotlight)` was renamed to `doc(notable_trait)`
|
||||
.suggestion = use `notable_trait` instead
|
||||
.no_op_note = `doc(spotlight)` is now a no-op
|
||||
|
||||
passes_duplicate_diagnostic_item_in_crate =
|
||||
duplicate diagnostic item in crate `{$crate_name}`: `{$name}`
|
||||
.note = the diagnostic item is first defined in crate `{$orig_crate_name}`
|
||||
|
|
|
|||
|
|
@ -10,22 +10,25 @@ use std::collections::hash_map::Entry;
|
|||
use std::slice;
|
||||
|
||||
use rustc_abi::{Align, ExternAbi, Size};
|
||||
use rustc_ast::{AttrStyle, LitKind, MetaItem, MetaItemInner, MetaItemKind, ast};
|
||||
use rustc_ast::{AttrStyle, LitKind, MetaItemKind, ast};
|
||||
use rustc_attr_parsing::{AttributeParser, Late};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::{Applicability, DiagCtxtHandle, IntoDiagArg, MultiSpan, StashKey};
|
||||
use rustc_errors::{DiagCtxtHandle, IntoDiagArg, MultiSpan, StashKey};
|
||||
use rustc_feature::{
|
||||
ACCEPTED_LANG_FEATURES, AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP,
|
||||
BuiltinAttribute,
|
||||
};
|
||||
use rustc_hir::attrs::{AttributeKind, InlineAttr, MirDialect, MirPhase, ReprAttr, SanitizerSet};
|
||||
use rustc_hir::attrs::{
|
||||
AttributeKind, DocAttribute, DocInline, InlineAttr, MirDialect, MirPhase, ReprAttr,
|
||||
SanitizerSet,
|
||||
};
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::LocalModDefId;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::{
|
||||
self as hir, Attribute, CRATE_HIR_ID, CRATE_OWNER_ID, Constness, FnSig, ForeignItem, HirId,
|
||||
Item, ItemKind, MethodKind, PartialConstStability, Safety, Stability, StabilityLevel, Target,
|
||||
TraitItem, find_attr,
|
||||
self as hir, Attribute, CRATE_HIR_ID, Constness, FnSig, ForeignItem, HirId, Item, ItemKind,
|
||||
MethodKind, PartialConstStability, Safety, Stability, StabilityLevel, Target, TraitItem,
|
||||
find_attr,
|
||||
};
|
||||
use rustc_macros::LintDiagnostic;
|
||||
use rustc_middle::hir::nested_filter;
|
||||
|
|
@ -43,7 +46,7 @@ use rustc_session::lint::builtin::{
|
|||
};
|
||||
use rustc_session::parse::feature_err;
|
||||
use rustc_span::edition::Edition;
|
||||
use rustc_span::{BytePos, DUMMY_SP, Span, Symbol, edition, sym};
|
||||
use rustc_span::{BytePos, DUMMY_SP, Span, Symbol, sym};
|
||||
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
|
||||
use rustc_trait_selection::infer::{TyCtxtInferExt, ValuePairs};
|
||||
use rustc_trait_selection::traits::ObligationCtxt;
|
||||
|
|
@ -106,21 +109,6 @@ impl IntoDiagArg for ProcMacroKind {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum DocFakeItemKind {
|
||||
Attribute,
|
||||
Keyword,
|
||||
}
|
||||
|
||||
impl DocFakeItemKind {
|
||||
fn name(self) -> &'static str {
|
||||
match self {
|
||||
Self::Attribute => "attribute",
|
||||
Self::Keyword => "keyword",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct CheckAttrVisitor<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
|
||||
|
|
@ -141,8 +129,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
target: Target,
|
||||
item: Option<ItemLike<'_>>,
|
||||
) {
|
||||
let mut doc_aliases = FxHashMap::default();
|
||||
let mut specified_inline = None;
|
||||
let mut seen = FxHashMap::default();
|
||||
let attrs = self.tcx.hir_attrs(hir_id);
|
||||
for attr in attrs {
|
||||
|
|
@ -225,6 +211,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
Attribute::Parsed(AttributeKind::MacroExport { span, .. }) => {
|
||||
self.check_macro_export(hir_id, *span, target)
|
||||
},
|
||||
Attribute::Parsed(AttributeKind::Doc(attr)) => self.check_doc_attrs(attr, hir_id, target),
|
||||
Attribute::Parsed(
|
||||
AttributeKind::BodyStability { .. }
|
||||
| AttributeKind::ConstStabilityIndirect
|
||||
|
|
@ -306,15 +293,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
self.check_diagnostic_on_const(attr.span(), hir_id, target, item)
|
||||
}
|
||||
[sym::thread_local, ..] => self.check_thread_local(attr, span, target),
|
||||
[sym::doc, ..] => self.check_doc_attrs(
|
||||
attr,
|
||||
attr.span(),
|
||||
attr_item.style,
|
||||
hir_id,
|
||||
target,
|
||||
&mut specified_inline,
|
||||
&mut doc_aliases,
|
||||
),
|
||||
[sym::no_link, ..] => self.check_no_link(hir_id, attr, span, target),
|
||||
[sym::rustc_no_implicit_autorefs, ..] => {
|
||||
self.check_applied_to_fn_or_method(hir_id, attr.span(), span, target)
|
||||
|
|
@ -782,42 +760,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn doc_attr_str_error(&self, meta: &MetaItemInner, attr_name: &str) {
|
||||
self.dcx().emit_err(errors::DocExpectStr { attr_span: meta.span(), attr_name });
|
||||
}
|
||||
|
||||
fn check_doc_alias_value(
|
||||
&self,
|
||||
meta: &MetaItemInner,
|
||||
doc_alias: Symbol,
|
||||
hir_id: HirId,
|
||||
target: Target,
|
||||
is_list: bool,
|
||||
aliases: &mut FxHashMap<String, Span>,
|
||||
) {
|
||||
let tcx = self.tcx;
|
||||
let span = meta.name_value_literal_span().unwrap_or_else(|| meta.span());
|
||||
let attr_str =
|
||||
&format!("`#[doc(alias{})]`", if is_list { "(\"...\")" } else { " = \"...\"" });
|
||||
if doc_alias == sym::empty {
|
||||
tcx.dcx().emit_err(errors::DocAliasEmpty { span, attr_str });
|
||||
return;
|
||||
}
|
||||
|
||||
let doc_alias_str = doc_alias.as_str();
|
||||
if let Some(c) = doc_alias_str
|
||||
.chars()
|
||||
.find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' '))
|
||||
{
|
||||
tcx.dcx().emit_err(errors::DocAliasBadChar { span, attr_str, char_: c });
|
||||
return;
|
||||
}
|
||||
if doc_alias_str.starts_with(' ') || doc_alias_str.ends_with(' ') {
|
||||
tcx.dcx().emit_err(errors::DocAliasStartEnd { span, attr_str });
|
||||
return;
|
||||
}
|
||||
|
||||
let span = meta.span();
|
||||
fn check_doc_alias_value(&self, span: Span, hir_id: HirId, target: Target, alias: Symbol) {
|
||||
if let Some(location) = match target {
|
||||
Target::AssocTy => {
|
||||
if let DefKind::Impl { .. } =
|
||||
|
|
@ -874,123 +817,16 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
| Target::MacroCall
|
||||
| Target::Delegation { .. } => None,
|
||||
} {
|
||||
tcx.dcx().emit_err(errors::DocAliasBadLocation { span, attr_str, location });
|
||||
self.tcx.dcx().emit_err(errors::DocAliasBadLocation { span, location });
|
||||
return;
|
||||
}
|
||||
if self.tcx.hir_opt_name(hir_id) == Some(doc_alias) {
|
||||
tcx.dcx().emit_err(errors::DocAliasNotAnAlias { span, attr_str });
|
||||
if self.tcx.hir_opt_name(hir_id) == Some(alias) {
|
||||
self.tcx.dcx().emit_err(errors::DocAliasNotAnAlias { span, attr_str: alias });
|
||||
return;
|
||||
}
|
||||
if let Err(entry) = aliases.try_insert(doc_alias_str.to_owned(), span) {
|
||||
self.tcx.emit_node_span_lint(
|
||||
UNUSED_ATTRIBUTES,
|
||||
hir_id,
|
||||
span,
|
||||
errors::DocAliasDuplicated { first_defn: *entry.entry.get() },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_doc_alias(
|
||||
&self,
|
||||
meta: &MetaItemInner,
|
||||
hir_id: HirId,
|
||||
target: Target,
|
||||
aliases: &mut FxHashMap<String, Span>,
|
||||
) {
|
||||
if let Some(values) = meta.meta_item_list() {
|
||||
for v in values {
|
||||
match v.lit() {
|
||||
Some(l) => match l.kind {
|
||||
LitKind::Str(s, _) => {
|
||||
self.check_doc_alias_value(v, s, hir_id, target, true, aliases);
|
||||
}
|
||||
_ => {
|
||||
self.tcx
|
||||
.dcx()
|
||||
.emit_err(errors::DocAliasNotStringLiteral { span: v.span() });
|
||||
}
|
||||
},
|
||||
None => {
|
||||
self.tcx
|
||||
.dcx()
|
||||
.emit_err(errors::DocAliasNotStringLiteral { span: v.span() });
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let Some(doc_alias) = meta.value_str() {
|
||||
self.check_doc_alias_value(meta, doc_alias, hir_id, target, false, aliases)
|
||||
} else {
|
||||
self.dcx().emit_err(errors::DocAliasMalformed { span: meta.span() });
|
||||
}
|
||||
}
|
||||
|
||||
fn check_doc_keyword_and_attribute(
|
||||
&self,
|
||||
meta: &MetaItemInner,
|
||||
hir_id: HirId,
|
||||
attr_kind: DocFakeItemKind,
|
||||
) {
|
||||
fn is_doc_keyword(s: Symbol) -> bool {
|
||||
// FIXME: Once rustdoc can handle URL conflicts on case insensitive file systems, we
|
||||
// can remove the `SelfTy` case here, remove `sym::SelfTy`, and update the
|
||||
// `#[doc(keyword = "SelfTy")` attribute in `library/std/src/keyword_docs.rs`.
|
||||
s.is_reserved(|| edition::LATEST_STABLE_EDITION) || s.is_weak() || s == sym::SelfTy
|
||||
}
|
||||
|
||||
// FIXME: This should support attributes with namespace like `diagnostic::do_not_recommend`.
|
||||
fn is_builtin_attr(s: Symbol) -> bool {
|
||||
rustc_feature::BUILTIN_ATTRIBUTE_MAP.contains_key(&s)
|
||||
}
|
||||
|
||||
let value = match meta.value_str() {
|
||||
Some(value) if value != sym::empty => value,
|
||||
_ => return self.doc_attr_str_error(meta, attr_kind.name()),
|
||||
};
|
||||
|
||||
let item_kind = match self.tcx.hir_node(hir_id) {
|
||||
hir::Node::Item(item) => Some(&item.kind),
|
||||
_ => None,
|
||||
};
|
||||
match item_kind {
|
||||
Some(ItemKind::Mod(_, module)) => {
|
||||
if !module.item_ids.is_empty() {
|
||||
self.dcx().emit_err(errors::DocKeywordAttributeEmptyMod {
|
||||
span: meta.span(),
|
||||
attr_name: attr_kind.name(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.dcx().emit_err(errors::DocKeywordAttributeNotMod {
|
||||
span: meta.span(),
|
||||
attr_name: attr_kind.name(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
match attr_kind {
|
||||
DocFakeItemKind::Keyword => {
|
||||
if !is_doc_keyword(value) {
|
||||
self.dcx().emit_err(errors::DocKeywordNotKeyword {
|
||||
span: meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
|
||||
keyword: value,
|
||||
});
|
||||
}
|
||||
}
|
||||
DocFakeItemKind::Attribute => {
|
||||
if !is_builtin_attr(value) {
|
||||
self.dcx().emit_err(errors::DocAttributeNotAttribute {
|
||||
span: meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
|
||||
attribute: value,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_doc_fake_variadic(&self, meta: &MetaItemInner, hir_id: HirId) {
|
||||
fn check_doc_fake_variadic(&self, span: Span, hir_id: HirId) {
|
||||
let item_kind = match self.tcx.hir_node(hir_id) {
|
||||
hir::Node::Item(item) => Some(&item.kind),
|
||||
_ => None,
|
||||
|
|
@ -1008,18 +844,18 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
false
|
||||
};
|
||||
if !is_valid {
|
||||
self.dcx().emit_err(errors::DocFakeVariadicNotValid { span: meta.span() });
|
||||
self.dcx().emit_err(errors::DocFakeVariadicNotValid { span });
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.dcx().emit_err(errors::DocKeywordOnlyImpl { span: meta.span() });
|
||||
self.dcx().emit_err(errors::DocKeywordOnlyImpl { span });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_doc_search_unbox(&self, meta: &MetaItemInner, hir_id: HirId) {
|
||||
fn check_doc_search_unbox(&self, span: Span, hir_id: HirId) {
|
||||
let hir::Node::Item(item) = self.tcx.hir_node(hir_id) else {
|
||||
self.dcx().emit_err(errors::DocSearchUnboxInvalid { span: meta.span() });
|
||||
self.dcx().emit_err(errors::DocSearchUnboxInvalid { span });
|
||||
return;
|
||||
};
|
||||
match item.kind {
|
||||
|
|
@ -1032,7 +868,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
}) => {}
|
||||
ItemKind::TyAlias(_, generics, _) if generics.params.len() != 0 => {}
|
||||
_ => {
|
||||
self.dcx().emit_err(errors::DocSearchUnboxInvalid { span: meta.span() });
|
||||
self.dcx().emit_err(errors::DocSearchUnboxInvalid { span });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1046,60 +882,49 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
/// already seen an inlining attribute for this item.
|
||||
/// If so, `specified_inline` holds the value and the span of
|
||||
/// the first `inline`/`no_inline` attribute.
|
||||
fn check_doc_inline(
|
||||
&self,
|
||||
style: AttrStyle,
|
||||
meta: &MetaItemInner,
|
||||
hir_id: HirId,
|
||||
target: Target,
|
||||
specified_inline: &mut Option<(bool, Span)>,
|
||||
) {
|
||||
match target {
|
||||
Target::Use | Target::ExternCrate => {
|
||||
let do_inline = meta.has_name(sym::inline);
|
||||
if let Some((prev_inline, prev_span)) = *specified_inline {
|
||||
if do_inline != prev_inline {
|
||||
let mut spans = MultiSpan::from_spans(vec![prev_span, meta.span()]);
|
||||
spans.push_span_label(prev_span, fluent::passes_doc_inline_conflict_first);
|
||||
spans.push_span_label(
|
||||
meta.span(),
|
||||
fluent::passes_doc_inline_conflict_second,
|
||||
);
|
||||
self.dcx().emit_err(errors::DocKeywordConflict { spans });
|
||||
fn check_doc_inline(&self, hir_id: HirId, target: Target, inline: &[(DocInline, Span)]) {
|
||||
let span = match inline {
|
||||
[] => return,
|
||||
[(_, span)] => *span,
|
||||
[(inline, span), rest @ ..] => {
|
||||
for (inline2, span2) in rest {
|
||||
if inline2 != inline {
|
||||
let mut spans = MultiSpan::from_spans(vec![*span, *span2]);
|
||||
spans.push_span_label(*span, fluent::passes_doc_inline_conflict_first);
|
||||
spans.push_span_label(*span2, fluent::passes_doc_inline_conflict_second);
|
||||
self.dcx().emit_err(errors::DocInlineConflict { spans });
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
*specified_inline = Some((do_inline, meta.span()));
|
||||
}
|
||||
*span
|
||||
}
|
||||
};
|
||||
|
||||
match target {
|
||||
Target::Use | Target::ExternCrate => {}
|
||||
_ => {
|
||||
self.tcx.emit_node_span_lint(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
hir_id,
|
||||
meta.span(),
|
||||
span,
|
||||
errors::DocInlineOnlyUse {
|
||||
attr_span: meta.span(),
|
||||
item_span: (style == AttrStyle::Outer).then(|| self.tcx.hir_span(hir_id)),
|
||||
attr_span: span,
|
||||
item_span: self.tcx.hir_span(hir_id),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_doc_masked(
|
||||
&self,
|
||||
style: AttrStyle,
|
||||
meta: &MetaItemInner,
|
||||
hir_id: HirId,
|
||||
target: Target,
|
||||
) {
|
||||
fn check_doc_masked(&self, span: Span, hir_id: HirId, target: Target) {
|
||||
if target != Target::ExternCrate {
|
||||
self.tcx.emit_node_span_lint(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
hir_id,
|
||||
meta.span(),
|
||||
span,
|
||||
errors::DocMaskedOnlyExternCrate {
|
||||
attr_span: meta.span(),
|
||||
item_span: (style == AttrStyle::Outer).then(|| self.tcx.hir_span(hir_id)),
|
||||
attr_span: span,
|
||||
item_span: self.tcx.hir_span(hir_id),
|
||||
},
|
||||
);
|
||||
return;
|
||||
|
|
@ -1109,354 +934,160 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
self.tcx.emit_node_span_lint(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
hir_id,
|
||||
meta.span(),
|
||||
span,
|
||||
errors::DocMaskedNotExternCrateSelf {
|
||||
attr_span: meta.span(),
|
||||
item_span: (style == AttrStyle::Outer).then(|| self.tcx.hir_span(hir_id)),
|
||||
attr_span: span,
|
||||
item_span: self.tcx.hir_span(hir_id),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_doc_keyword_and_attribute(&self, span: Span, hir_id: HirId, attr_name: &'static str) {
|
||||
let item_kind = match self.tcx.hir_node(hir_id) {
|
||||
hir::Node::Item(item) => Some(&item.kind),
|
||||
_ => None,
|
||||
};
|
||||
match item_kind {
|
||||
Some(ItemKind::Mod(_, module)) => {
|
||||
if !module.item_ids.is_empty() {
|
||||
self.dcx().emit_err(errors::DocKeywordAttributeEmptyMod { span, attr_name });
|
||||
return;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.dcx().emit_err(errors::DocKeywordAttributeNotMod { span, attr_name });
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks that an attribute is *not* used at the crate level. Returns `true` if valid.
|
||||
fn check_attr_not_crate_level(
|
||||
&self,
|
||||
meta: &MetaItemInner,
|
||||
hir_id: HirId,
|
||||
attr_name: &str,
|
||||
) -> bool {
|
||||
fn check_attr_not_crate_level(&self, span: Span, hir_id: HirId, attr_name: &str) -> bool {
|
||||
if CRATE_HIR_ID == hir_id {
|
||||
self.dcx().emit_err(errors::DocAttrNotCrateLevel { span: meta.span(), attr_name });
|
||||
self.dcx().emit_err(errors::DocAttrNotCrateLevel { span, attr_name });
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Checks that an attribute is used at the crate level. Returns `true` if valid.
|
||||
fn check_attr_crate_level(
|
||||
&self,
|
||||
attr_span: Span,
|
||||
style: AttrStyle,
|
||||
meta: &MetaItemInner,
|
||||
hir_id: HirId,
|
||||
) -> bool {
|
||||
fn check_attr_crate_level(&self, span: Span, hir_id: HirId) -> bool {
|
||||
if hir_id != CRATE_HIR_ID {
|
||||
// insert a bang between `#` and `[...`
|
||||
let bang_span = attr_span.lo() + BytePos(1);
|
||||
let sugg = (style == AttrStyle::Outer
|
||||
&& self.tcx.hir_get_parent_item(hir_id) == CRATE_OWNER_ID)
|
||||
.then_some(errors::AttrCrateLevelOnlySugg {
|
||||
attr: attr_span.with_lo(bang_span).with_hi(bang_span),
|
||||
});
|
||||
self.tcx.emit_node_span_lint(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
hir_id,
|
||||
meta.span(),
|
||||
errors::AttrCrateLevelOnly { sugg },
|
||||
span,
|
||||
errors::AttrCrateLevelOnly {},
|
||||
);
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn check_doc_attr_string_value(&self, meta: &MetaItemInner, hir_id: HirId) {
|
||||
if meta.value_str().is_none() {
|
||||
self.tcx.emit_node_span_lint(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
hir_id,
|
||||
meta.span(),
|
||||
errors::DocAttrExpectsString { attr_name: meta.name().unwrap() },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_doc_attr_no_value(&self, meta: &MetaItemInner, hir_id: HirId) {
|
||||
if !meta.is_word() {
|
||||
self.tcx.emit_node_span_lint(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
hir_id,
|
||||
meta.span(),
|
||||
errors::DocAttrExpectsNoValue { attr_name: meta.name().unwrap() },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks that `doc(test(...))` attribute contains only valid attributes and are at the right place.
|
||||
fn check_test_attr(
|
||||
&self,
|
||||
attr_span: Span,
|
||||
style: AttrStyle,
|
||||
meta: &MetaItemInner,
|
||||
hir_id: HirId,
|
||||
) {
|
||||
if let Some(metas) = meta.meta_item_list() {
|
||||
for i_meta in metas {
|
||||
match (i_meta.name(), i_meta.meta_item()) {
|
||||
(Some(sym::attr), _) => {
|
||||
// Allowed everywhere like `#[doc]`
|
||||
}
|
||||
(Some(sym::no_crate_inject), _) => {
|
||||
self.check_attr_crate_level(attr_span, style, meta, hir_id);
|
||||
}
|
||||
(_, Some(m)) => {
|
||||
self.tcx.emit_node_span_lint(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
hir_id,
|
||||
i_meta.span(),
|
||||
errors::DocTestUnknown {
|
||||
path: rustc_ast_pretty::pprust::path_to_string(&m.path),
|
||||
},
|
||||
);
|
||||
}
|
||||
(_, None) => {
|
||||
self.tcx.emit_node_span_lint(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
hir_id,
|
||||
i_meta.span(),
|
||||
errors::DocTestLiteral,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.tcx.emit_node_span_lint(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
hir_id,
|
||||
meta.span(),
|
||||
errors::DocTestTakesList,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Check that the `#![doc(auto_cfg)]` attribute has the expected input.
|
||||
fn check_doc_auto_cfg(&self, meta: &MetaItem, hir_id: HirId) {
|
||||
match &meta.kind {
|
||||
MetaItemKind::Word => {}
|
||||
MetaItemKind::NameValue(lit) => {
|
||||
if !matches!(lit.kind, LitKind::Bool(_)) {
|
||||
self.tcx.emit_node_span_lint(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
hir_id,
|
||||
meta.span,
|
||||
errors::DocAutoCfgWrongLiteral,
|
||||
);
|
||||
}
|
||||
}
|
||||
MetaItemKind::List(list) => {
|
||||
for item in list {
|
||||
let Some(attr_name @ (sym::hide | sym::show)) = item.name() else {
|
||||
self.tcx.emit_node_span_lint(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
hir_id,
|
||||
meta.span,
|
||||
errors::DocAutoCfgExpectsHideOrShow,
|
||||
);
|
||||
continue;
|
||||
};
|
||||
if let Some(list) = item.meta_item_list() {
|
||||
for item in list {
|
||||
let valid = item.meta_item().is_some_and(|meta| {
|
||||
meta.path.segments.len() == 1
|
||||
&& matches!(
|
||||
&meta.kind,
|
||||
MetaItemKind::Word | MetaItemKind::NameValue(_)
|
||||
)
|
||||
});
|
||||
if !valid {
|
||||
self.tcx.emit_node_span_lint(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
hir_id,
|
||||
item.span(),
|
||||
errors::DocAutoCfgHideShowUnexpectedItem { attr_name },
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.tcx.emit_node_span_lint(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
hir_id,
|
||||
meta.span,
|
||||
errors::DocAutoCfgHideShowExpectsList { attr_name },
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs various checks on `#[doc]` attributes.
|
||||
///
|
||||
/// `specified_inline` should be initialized to `None` and kept for the scope
|
||||
/// of one item. Read the documentation of [`check_doc_inline`] for more information.
|
||||
///
|
||||
/// [`check_doc_inline`]: Self::check_doc_inline
|
||||
fn check_doc_attrs(
|
||||
&self,
|
||||
attr: &Attribute,
|
||||
attr_span: Span,
|
||||
style: AttrStyle,
|
||||
hir_id: HirId,
|
||||
target: Target,
|
||||
specified_inline: &mut Option<(bool, Span)>,
|
||||
aliases: &mut FxHashMap<String, Span>,
|
||||
) {
|
||||
if let Some(list) = attr.meta_item_list() {
|
||||
for meta in &list {
|
||||
if let Some(i_meta) = meta.meta_item() {
|
||||
match i_meta.name() {
|
||||
Some(sym::alias) => {
|
||||
if self.check_attr_not_crate_level(meta, hir_id, "alias") {
|
||||
self.check_doc_alias(meta, hir_id, target, aliases);
|
||||
}
|
||||
}
|
||||
fn check_doc_attrs(&self, attr: &DocAttribute, hir_id: HirId, target: Target) {
|
||||
let DocAttribute {
|
||||
aliases,
|
||||
// valid pretty much anywhere, not checked here?
|
||||
// FIXME: should we?
|
||||
hidden: _,
|
||||
inline,
|
||||
// FIXME: currently unchecked
|
||||
cfg: _,
|
||||
// already check in attr_parsing
|
||||
auto_cfg: _,
|
||||
// already check in attr_parsing
|
||||
auto_cfg_change: _,
|
||||
fake_variadic,
|
||||
keyword,
|
||||
masked,
|
||||
// FIXME: currently unchecked
|
||||
notable_trait: _,
|
||||
search_unbox,
|
||||
html_favicon_url,
|
||||
html_logo_url,
|
||||
html_playground_url,
|
||||
html_root_url,
|
||||
html_no_source,
|
||||
issue_tracker_base_url,
|
||||
rust_logo,
|
||||
// allowed anywhere
|
||||
test_attrs: _,
|
||||
no_crate_inject,
|
||||
attribute,
|
||||
} = attr;
|
||||
|
||||
Some(sym::keyword) => {
|
||||
if self.check_attr_not_crate_level(meta, hir_id, "keyword") {
|
||||
self.check_doc_keyword_and_attribute(
|
||||
meta,
|
||||
hir_id,
|
||||
DocFakeItemKind::Keyword,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Some(sym::attribute) => {
|
||||
if self.check_attr_not_crate_level(meta, hir_id, "attribute") {
|
||||
self.check_doc_keyword_and_attribute(
|
||||
meta,
|
||||
hir_id,
|
||||
DocFakeItemKind::Attribute,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Some(sym::fake_variadic) => {
|
||||
if self.check_attr_not_crate_level(meta, hir_id, "fake_variadic") {
|
||||
self.check_doc_fake_variadic(meta, hir_id);
|
||||
}
|
||||
}
|
||||
|
||||
Some(sym::search_unbox) => {
|
||||
if self.check_attr_not_crate_level(meta, hir_id, "fake_variadic") {
|
||||
self.check_doc_search_unbox(meta, hir_id);
|
||||
}
|
||||
}
|
||||
|
||||
Some(sym::test) => {
|
||||
self.check_test_attr(attr_span, style, meta, hir_id);
|
||||
}
|
||||
|
||||
Some(
|
||||
sym::html_favicon_url
|
||||
| sym::html_logo_url
|
||||
| sym::html_playground_url
|
||||
| sym::issue_tracker_base_url
|
||||
| sym::html_root_url,
|
||||
) => {
|
||||
self.check_attr_crate_level(attr_span, style, meta, hir_id);
|
||||
self.check_doc_attr_string_value(meta, hir_id);
|
||||
}
|
||||
|
||||
Some(sym::html_no_source) => {
|
||||
self.check_attr_crate_level(attr_span, style, meta, hir_id);
|
||||
self.check_doc_attr_no_value(meta, hir_id);
|
||||
}
|
||||
|
||||
Some(sym::auto_cfg) => {
|
||||
self.check_doc_auto_cfg(i_meta, hir_id);
|
||||
}
|
||||
|
||||
Some(sym::inline | sym::no_inline) => {
|
||||
self.check_doc_inline(style, meta, hir_id, target, specified_inline)
|
||||
}
|
||||
|
||||
Some(sym::masked) => self.check_doc_masked(style, meta, hir_id, target),
|
||||
|
||||
Some(sym::cfg | sym::hidden | sym::notable_trait) => {}
|
||||
|
||||
Some(sym::rust_logo) => {
|
||||
if self.check_attr_crate_level(attr_span, style, meta, hir_id)
|
||||
&& !self.tcx.features().rustdoc_internals()
|
||||
{
|
||||
feature_err(
|
||||
&self.tcx.sess,
|
||||
sym::rustdoc_internals,
|
||||
meta.span(),
|
||||
fluent::passes_doc_rust_logo,
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
|
||||
_ => {
|
||||
let path = rustc_ast_pretty::pprust::path_to_string(&i_meta.path);
|
||||
if i_meta.has_name(sym::spotlight) {
|
||||
self.tcx.emit_node_span_lint(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
hir_id,
|
||||
i_meta.span,
|
||||
errors::DocTestUnknownSpotlight { path, span: i_meta.span },
|
||||
);
|
||||
} else if i_meta.has_name(sym::include)
|
||||
&& let Some(value) = i_meta.value_str()
|
||||
{
|
||||
let applicability = if list.len() == 1 {
|
||||
Applicability::MachineApplicable
|
||||
} else {
|
||||
Applicability::MaybeIncorrect
|
||||
};
|
||||
// If there are multiple attributes, the suggestion would suggest
|
||||
// deleting all of them, which is incorrect.
|
||||
self.tcx.emit_node_span_lint(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
hir_id,
|
||||
i_meta.span,
|
||||
errors::DocTestUnknownInclude {
|
||||
path,
|
||||
value: value.to_string(),
|
||||
inner: match style {
|
||||
AttrStyle::Inner => "!",
|
||||
AttrStyle::Outer => "",
|
||||
},
|
||||
sugg: (attr.span(), applicability),
|
||||
},
|
||||
);
|
||||
} else if i_meta.has_name(sym::passes)
|
||||
|| i_meta.has_name(sym::no_default_passes)
|
||||
{
|
||||
self.tcx.emit_node_span_lint(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
hir_id,
|
||||
i_meta.span,
|
||||
errors::DocTestUnknownPasses { path, span: i_meta.span },
|
||||
);
|
||||
} else if i_meta.has_name(sym::plugins) {
|
||||
self.tcx.emit_node_span_lint(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
hir_id,
|
||||
i_meta.span,
|
||||
errors::DocTestUnknownPlugins { path, span: i_meta.span },
|
||||
);
|
||||
} else {
|
||||
self.tcx.emit_node_span_lint(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
hir_id,
|
||||
i_meta.span,
|
||||
errors::DocTestUnknownAny { path },
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.tcx.emit_node_span_lint(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
hir_id,
|
||||
meta.span(),
|
||||
errors::DocInvalid,
|
||||
);
|
||||
}
|
||||
for (alias, span) in aliases {
|
||||
if self.check_attr_not_crate_level(*span, hir_id, "alias") {
|
||||
self.check_doc_alias_value(*span, hir_id, target, *alias);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((_, span)) = keyword
|
||||
&& self.check_attr_not_crate_level(*span, hir_id, "keyword")
|
||||
{
|
||||
self.check_doc_keyword_and_attribute(*span, hir_id, "keyword");
|
||||
}
|
||||
if let Some((_, span)) = attribute
|
||||
&& self.check_attr_not_crate_level(*span, hir_id, "attribute")
|
||||
{
|
||||
self.check_doc_keyword_and_attribute(*span, hir_id, "attribute");
|
||||
}
|
||||
|
||||
if let Some(span) = fake_variadic
|
||||
&& self.check_attr_not_crate_level(*span, hir_id, "fake_variadic")
|
||||
{
|
||||
self.check_doc_fake_variadic(*span, hir_id);
|
||||
}
|
||||
|
||||
if let Some(span) = search_unbox
|
||||
&& self.check_attr_not_crate_level(*span, hir_id, "search_unbox")
|
||||
{
|
||||
self.check_doc_search_unbox(*span, hir_id);
|
||||
}
|
||||
|
||||
for i in [
|
||||
html_favicon_url,
|
||||
html_logo_url,
|
||||
html_playground_url,
|
||||
issue_tracker_base_url,
|
||||
html_root_url,
|
||||
] {
|
||||
if let Some((_, span)) = i {
|
||||
self.check_attr_crate_level(*span, hir_id);
|
||||
}
|
||||
}
|
||||
|
||||
for i in [html_no_source, no_crate_inject] {
|
||||
if let Some(span) = i {
|
||||
self.check_attr_crate_level(*span, hir_id);
|
||||
}
|
||||
}
|
||||
|
||||
self.check_doc_inline(hir_id, target, inline);
|
||||
|
||||
if let Some(span) = rust_logo
|
||||
&& self.check_attr_crate_level(*span, hir_id)
|
||||
&& !self.tcx.features().rustdoc_internals()
|
||||
{
|
||||
feature_err(
|
||||
&self.tcx.sess,
|
||||
sym::rustdoc_internals,
|
||||
*span,
|
||||
fluent::passes_doc_rust_logo,
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
|
||||
if let Some(span) = masked {
|
||||
self.check_doc_masked(*span, hir_id, target);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_has_incoherent_inherent_impls(&self, attr: &Attribute, span: Span, target: Target) {
|
||||
|
|
@ -2354,7 +1985,14 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {
|
|||
.hir_attrs(where_predicate.hir_id)
|
||||
.iter()
|
||||
.filter(|attr| !ATTRS_ALLOWED.iter().any(|&sym| attr.has_name(sym)))
|
||||
.filter(|attr| !attr.is_parsed_attr())
|
||||
// FIXME: We shouldn't need to special-case `doc`!
|
||||
.filter(|attr| {
|
||||
matches!(
|
||||
attr,
|
||||
Attribute::Parsed(AttributeKind::DocComment { .. } | AttributeKind::Doc(_))
|
||||
| Attribute::Unparsed(_)
|
||||
)
|
||||
})
|
||||
.map(|attr| attr.span())
|
||||
.collect::<Vec<_>>();
|
||||
if !spans.is_empty() {
|
||||
|
|
|
|||
|
|
@ -24,18 +24,6 @@ pub(crate) struct IncorrectDoNotRecommendLocation;
|
|||
#[diag(passes_incorrect_do_not_recommend_args)]
|
||||
pub(crate) struct DoNotRecommendDoesNotExpectArgs;
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_doc_attr_expects_string)]
|
||||
pub(crate) struct DocAttrExpectsString {
|
||||
pub(crate) attr_name: Symbol,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_doc_attr_expects_no_value)]
|
||||
pub(crate) struct DocAttrExpectsNoValue {
|
||||
pub(crate) attr_name: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_autodiff_attr)]
|
||||
pub(crate) struct AutoDiffAttr {
|
||||
|
|
@ -135,75 +123,20 @@ pub(crate) struct AttrShouldBeAppliedToStatic {
|
|||
pub defn_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_doc_expect_str)]
|
||||
pub(crate) struct DocExpectStr<'a> {
|
||||
#[primary_span]
|
||||
pub attr_span: Span,
|
||||
pub attr_name: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_doc_alias_empty)]
|
||||
pub(crate) struct DocAliasEmpty<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub attr_str: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_doc_alias_bad_char)]
|
||||
pub(crate) struct DocAliasBadChar<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub attr_str: &'a str,
|
||||
pub char_: char,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_doc_alias_start_end)]
|
||||
pub(crate) struct DocAliasStartEnd<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub attr_str: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_doc_alias_bad_location)]
|
||||
pub(crate) struct DocAliasBadLocation<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub attr_str: &'a str,
|
||||
pub location: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_doc_alias_not_an_alias)]
|
||||
pub(crate) struct DocAliasNotAnAlias<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub attr_str: &'a str,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_doc_alias_duplicated)]
|
||||
pub(crate) struct DocAliasDuplicated {
|
||||
#[label]
|
||||
pub first_defn: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_doc_alias_not_string_literal)]
|
||||
pub(crate) struct DocAliasNotStringLiteral {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_doc_alias_malformed)]
|
||||
pub(crate) struct DocAliasMalformed {
|
||||
pub(crate) struct DocAliasNotAnAlias {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub attr_str: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
|
|
@ -214,24 +147,6 @@ pub(crate) struct DocKeywordAttributeEmptyMod {
|
|||
pub attr_name: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_doc_keyword_not_keyword)]
|
||||
#[help]
|
||||
pub(crate) struct DocKeywordNotKeyword {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub keyword: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_doc_attribute_not_attribute)]
|
||||
#[help]
|
||||
pub(crate) struct DocAttributeNotAttribute {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub attribute: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_doc_keyword_attribute_not_mod)]
|
||||
pub(crate) struct DocKeywordAttributeNotMod {
|
||||
|
|
@ -264,7 +179,7 @@ pub(crate) struct DocSearchUnboxInvalid {
|
|||
#[derive(Diagnostic)]
|
||||
#[diag(passes_doc_inline_conflict)]
|
||||
#[help]
|
||||
pub(crate) struct DocKeywordConflict {
|
||||
pub(crate) struct DocInlineConflict {
|
||||
#[primary_span]
|
||||
pub spans: MultiSpan,
|
||||
}
|
||||
|
|
@ -276,7 +191,7 @@ pub(crate) struct DocInlineOnlyUse {
|
|||
#[label]
|
||||
pub attr_span: Span,
|
||||
#[label(passes_not_a_use_item_label)]
|
||||
pub item_span: Option<Span>,
|
||||
pub item_span: Span,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
|
|
@ -286,7 +201,7 @@ pub(crate) struct DocMaskedOnlyExternCrate {
|
|||
#[label]
|
||||
pub attr_span: Span,
|
||||
#[label(passes_not_an_extern_crate_label)]
|
||||
pub item_span: Option<Span>,
|
||||
pub item_span: Span,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
|
|
@ -295,7 +210,7 @@ pub(crate) struct DocMaskedNotExternCrateSelf {
|
|||
#[label]
|
||||
pub attr_span: Span,
|
||||
#[label(passes_extern_crate_self_label)]
|
||||
pub item_span: Option<Span>,
|
||||
pub item_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
|
|
@ -306,90 +221,6 @@ pub(crate) struct DocAttrNotCrateLevel<'a> {
|
|||
pub attr_name: &'a str,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_doc_test_unknown)]
|
||||
pub(crate) struct DocTestUnknown {
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_doc_test_literal)]
|
||||
pub(crate) struct DocTestLiteral;
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_doc_test_takes_list)]
|
||||
pub(crate) struct DocTestTakesList;
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_doc_auto_cfg_wrong_literal)]
|
||||
pub(crate) struct DocAutoCfgWrongLiteral;
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_doc_auto_cfg_expects_hide_or_show)]
|
||||
pub(crate) struct DocAutoCfgExpectsHideOrShow;
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_doc_auto_cfg_hide_show_expects_list)]
|
||||
pub(crate) struct DocAutoCfgHideShowExpectsList {
|
||||
pub attr_name: Symbol,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_doc_auto_cfg_hide_show_unexpected_item)]
|
||||
pub(crate) struct DocAutoCfgHideShowUnexpectedItem {
|
||||
pub attr_name: Symbol,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_doc_test_unknown_any)]
|
||||
pub(crate) struct DocTestUnknownAny {
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_doc_test_unknown_spotlight)]
|
||||
#[note]
|
||||
#[note(passes_no_op_note)]
|
||||
pub(crate) struct DocTestUnknownSpotlight {
|
||||
pub path: String,
|
||||
#[suggestion(style = "short", applicability = "machine-applicable", code = "notable_trait")]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_doc_test_unknown_passes)]
|
||||
#[note]
|
||||
#[note(passes_no_op_note)]
|
||||
pub(crate) struct DocTestUnknownPasses {
|
||||
pub path: String,
|
||||
#[label]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_doc_test_unknown_plugins)]
|
||||
#[note]
|
||||
#[note(passes_no_op_note)]
|
||||
pub(crate) struct DocTestUnknownPlugins {
|
||||
pub path: String,
|
||||
#[label]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_doc_test_unknown_include)]
|
||||
pub(crate) struct DocTestUnknownInclude {
|
||||
pub path: String,
|
||||
pub value: String,
|
||||
pub inner: &'static str,
|
||||
#[suggestion(code = "#{inner}[doc = include_str!(\"{value}\")]")]
|
||||
pub sugg: (Span, Applicability),
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_doc_invalid)]
|
||||
pub(crate) struct DocInvalid;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_has_incoherent_inherent_impl)]
|
||||
pub(crate) struct HasIncoherentInherentImpl {
|
||||
|
|
@ -1353,17 +1184,7 @@ pub(crate) struct IneffectiveUnstableImpl;
|
|||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_attr_crate_level)]
|
||||
#[note]
|
||||
pub(crate) struct AttrCrateLevelOnly {
|
||||
#[subdiagnostic]
|
||||
pub sugg: Option<AttrCrateLevelOnlySugg>,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[suggestion(passes_suggestion, applicability = "maybe-incorrect", code = "!", style = "verbose")]
|
||||
pub(crate) struct AttrCrateLevelOnlySugg {
|
||||
#[primary_span]
|
||||
pub attr: Span,
|
||||
}
|
||||
pub(crate) struct AttrCrateLevelOnly {}
|
||||
|
||||
/// "sanitize attribute not allowed here"
|
||||
#[derive(Diagnostic)]
|
||||
|
|
|
|||
|
|
@ -1222,7 +1222,7 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc
|
|||
if let TyKind::Path(None, ref path) = ty.kind
|
||||
// We cannot disambiguate multi-segment paths right now as that requires type
|
||||
// checking.
|
||||
&& path.is_potential_trivial_const_arg(false)
|
||||
&& path.is_potential_trivial_const_arg()
|
||||
{
|
||||
let mut check_ns = |ns| {
|
||||
self.maybe_resolve_ident_in_lexical_scope(path.segments[0].ident, ns)
|
||||
|
|
@ -4840,9 +4840,12 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|
|||
constant, anon_const_kind
|
||||
);
|
||||
|
||||
let is_trivial_const_arg = constant
|
||||
.value
|
||||
.is_potential_trivial_const_arg(self.r.tcx.features().min_generic_const_args());
|
||||
let is_trivial_const_arg = if self.r.tcx.features().min_generic_const_args() {
|
||||
matches!(constant.mgca_disambiguation, MgcaDisambiguation::Direct)
|
||||
} else {
|
||||
constant.value.is_potential_trivial_const_arg()
|
||||
};
|
||||
|
||||
self.resolve_anon_const_manual(is_trivial_const_arg, anon_const_kind, |this| {
|
||||
this.resolve_expr(&constant.value, None)
|
||||
})
|
||||
|
|
@ -5023,9 +5026,9 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|
|||
// Constant arguments need to be treated as AnonConst since
|
||||
// that is how they will be later lowered to HIR.
|
||||
if const_args.contains(&idx) {
|
||||
let is_trivial_const_arg = argument.is_potential_trivial_const_arg(
|
||||
self.r.tcx.features().min_generic_const_args(),
|
||||
);
|
||||
// FIXME(mgca): legacy const generics doesn't support mgca but maybe
|
||||
// that's okay.
|
||||
let is_trivial_const_arg = argument.is_potential_trivial_const_arg();
|
||||
self.resolve_anon_const_manual(
|
||||
is_trivial_const_arg,
|
||||
AnonConstKind::ConstArg(IsRepeatExpr::No),
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ use rustc_ast::{
|
|||
Item, ItemKind, MethodCall, NodeId, Path, PathSegment, Ty, TyKind,
|
||||
};
|
||||
use rustc_ast_pretty::pprust::where_bound_predicate_to_string;
|
||||
use rustc_attr_parsing::is_doc_alias_attrs_contain_symbol;
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_errors::{
|
||||
|
|
@ -18,6 +17,7 @@ use rustc_errors::{
|
|||
struct_span_code_err,
|
||||
};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_hir::def::Namespace::{self, *};
|
||||
use rustc_hir::def::{self, CtorKind, CtorOf, DefKind, MacroKinds};
|
||||
use rustc_hir::def_id::{CRATE_DEF_ID, DefId};
|
||||
|
|
@ -903,7 +903,10 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
|
|||
// confused by them.
|
||||
continue;
|
||||
}
|
||||
if is_doc_alias_attrs_contain_symbol(r.tcx.get_attrs(did, sym::doc), item_name) {
|
||||
if let Some(d) =
|
||||
hir::find_attr!(r.tcx.get_all_attrs(did), AttributeKind::Doc(d) => d)
|
||||
&& d.aliases.contains_key(&item_name)
|
||||
{
|
||||
return Some(did);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ use rustc_middle::ty::{
|
|||
ResolverGlobalCtxt, TyCtxt, TyCtxtFeed, Visibility,
|
||||
};
|
||||
use rustc_query_system::ich::StableHashingContext;
|
||||
use rustc_session::config::CrateType;
|
||||
use rustc_session::lint::builtin::PRIVATE_MACRO_USE;
|
||||
use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind, SyntaxContext, Transparency};
|
||||
use rustc_span::{DUMMY_SP, Ident, Macros20NormalizedIdent, Span, Symbol, kw, sym};
|
||||
|
|
@ -2430,6 +2431,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
}
|
||||
|
||||
fn resolve_main(&mut self) {
|
||||
let any_exe = self.tcx.crate_types().contains(&CrateType::Executable);
|
||||
// Don't try to resolve main unless it's an executable
|
||||
if !any_exe {
|
||||
return;
|
||||
}
|
||||
|
||||
let module = self.graph_root;
|
||||
let ident = Ident::with_dummy_span(sym::main);
|
||||
let parent_scope = &ParentScope::module(module, self.arenas);
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use pulldown_cmark::{
|
|||
use rustc_ast as ast;
|
||||
use rustc_ast::attr::AttributeExt;
|
||||
use rustc_ast::join_path_syms;
|
||||
use rustc_ast::token::DocFragmentKind;
|
||||
use rustc_ast::util::comments::beautify_doc_string;
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_data_structures::unord::UnordSet;
|
||||
|
|
@ -23,14 +24,6 @@ use tracing::{debug, trace};
|
|||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum DocFragmentKind {
|
||||
/// A doc fragment created from a `///` or `//!` doc comment.
|
||||
SugaredDoc,
|
||||
/// A doc fragment created from a "raw" `#[doc=""]` attribute.
|
||||
RawDoc,
|
||||
}
|
||||
|
||||
/// A portion of documentation, extracted from a `#[doc]` attribute.
|
||||
///
|
||||
/// Each variant contains the line number within the complete doc-comment where the fragment
|
||||
|
|
@ -125,7 +118,7 @@ pub fn unindent_doc_fragments(docs: &mut [DocFragment]) {
|
|||
//
|
||||
// In this case, you want "hello! another" and not "hello! another".
|
||||
let add = if docs.windows(2).any(|arr| arr[0].kind != arr[1].kind)
|
||||
&& docs.iter().any(|d| d.kind == DocFragmentKind::SugaredDoc)
|
||||
&& docs.iter().any(|d| d.kind.is_sugared())
|
||||
{
|
||||
// In case we have a mix of sugared doc comments and "raw" ones, we want the sugared one to
|
||||
// "decide" how much the minimum indent will be.
|
||||
|
|
@ -155,8 +148,7 @@ pub fn unindent_doc_fragments(docs: &mut [DocFragment]) {
|
|||
// Compare against either space or tab, ignoring whether they are
|
||||
// mixed or not.
|
||||
let whitespace = line.chars().take_while(|c| *c == ' ' || *c == '\t').count();
|
||||
whitespace
|
||||
+ (if fragment.kind == DocFragmentKind::SugaredDoc { 0 } else { add })
|
||||
whitespace + (if fragment.kind.is_sugared() { 0 } else { add })
|
||||
})
|
||||
.min()
|
||||
.unwrap_or(usize::MAX)
|
||||
|
|
@ -171,7 +163,7 @@ pub fn unindent_doc_fragments(docs: &mut [DocFragment]) {
|
|||
continue;
|
||||
}
|
||||
|
||||
let indent = if fragment.kind != DocFragmentKind::SugaredDoc && min_indent > 0 {
|
||||
let indent = if !fragment.kind.is_sugared() && min_indent > 0 {
|
||||
min_indent - add
|
||||
} else {
|
||||
min_indent
|
||||
|
|
@ -214,19 +206,17 @@ pub fn attrs_to_doc_fragments<'a, A: AttributeExt + Clone + 'a>(
|
|||
let mut doc_fragments = Vec::with_capacity(size_hint);
|
||||
let mut other_attrs = ThinVec::<A>::with_capacity(if doc_only { 0 } else { size_hint });
|
||||
for (attr, item_id) in attrs {
|
||||
if let Some((doc_str, comment_kind)) = attr.doc_str_and_comment_kind() {
|
||||
let doc = beautify_doc_string(doc_str, comment_kind);
|
||||
let (span, kind, from_expansion) = if let Some(span) = attr.is_doc_comment() {
|
||||
(span, DocFragmentKind::SugaredDoc, span.from_expansion())
|
||||
} else {
|
||||
let attr_span = attr.span();
|
||||
let (span, from_expansion) = match attr.value_span() {
|
||||
Some(sp) => (sp.with_ctxt(attr_span.ctxt()), sp.from_expansion()),
|
||||
None => (attr_span, attr_span.from_expansion()),
|
||||
};
|
||||
(span, DocFragmentKind::RawDoc, from_expansion)
|
||||
if let Some((doc_str, fragment_kind)) = attr.doc_str_and_fragment_kind() {
|
||||
let doc = beautify_doc_string(doc_str, fragment_kind.comment_kind());
|
||||
let attr_span = attr.span();
|
||||
let (span, from_expansion) = match fragment_kind {
|
||||
DocFragmentKind::Sugared(_) => (attr_span, attr_span.from_expansion()),
|
||||
DocFragmentKind::Raw(value_span) => {
|
||||
(value_span.with_ctxt(attr_span.ctxt()), value_span.from_expansion())
|
||||
}
|
||||
};
|
||||
let fragment = DocFragment { span, doc, kind, item_id, indent: 0, from_expansion };
|
||||
let fragment =
|
||||
DocFragment { span, doc, kind: fragment_kind, item_id, indent: 0, from_expansion };
|
||||
doc_fragments.push(fragment);
|
||||
} else if !doc_only {
|
||||
other_attrs.push(attr.clone());
|
||||
|
|
@ -377,16 +367,8 @@ pub fn inner_docs(attrs: &[impl AttributeExt]) -> bool {
|
|||
/// Has `#[rustc_doc_primitive]` or `#[doc(keyword)]` or `#[doc(attribute)]`.
|
||||
pub fn has_primitive_or_keyword_or_attribute_docs(attrs: &[impl AttributeExt]) -> bool {
|
||||
for attr in attrs {
|
||||
if attr.has_name(sym::rustc_doc_primitive) {
|
||||
if attr.has_name(sym::rustc_doc_primitive) || attr.is_doc_keyword_or_attribute() {
|
||||
return true;
|
||||
} else if attr.has_name(sym::doc)
|
||||
&& let Some(items) = attr.meta_item_list()
|
||||
{
|
||||
for item in items {
|
||||
if item.has_name(sym::keyword) || item.has_name(sym::attribute) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
|
|
@ -571,7 +553,7 @@ pub fn source_span_for_markdown_range_inner(
|
|||
use rustc_span::BytePos;
|
||||
|
||||
if let &[fragment] = &fragments
|
||||
&& fragment.kind == DocFragmentKind::RawDoc
|
||||
&& !fragment.kind.is_sugared()
|
||||
&& let Ok(snippet) = map.span_to_snippet(fragment.span)
|
||||
&& snippet.trim_end() == markdown.trim_end()
|
||||
&& let Ok(md_range_lo) = u32::try_from(md_range.start)
|
||||
|
|
@ -589,7 +571,7 @@ pub fn source_span_for_markdown_range_inner(
|
|||
));
|
||||
}
|
||||
|
||||
let is_all_sugared_doc = fragments.iter().all(|frag| frag.kind == DocFragmentKind::SugaredDoc);
|
||||
let is_all_sugared_doc = fragments.iter().all(|frag| frag.kind.is_sugared());
|
||||
|
||||
if !is_all_sugared_doc {
|
||||
// This case ignores the markdown outside of the range so that it can
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use std::path::PathBuf;
|
|||
|
||||
use rustc_span::source_map::{FilePathMapping, SourceMap};
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::{BytePos, Span};
|
||||
use rustc_span::{BytePos, DUMMY_SP, Span};
|
||||
|
||||
use super::{DocFragment, DocFragmentKind, source_span_for_markdown_range_inner};
|
||||
|
||||
|
|
@ -17,7 +17,7 @@ fn single_backtick() {
|
|||
&[DocFragment {
|
||||
span: Span::with_root_ctxt(BytePos(8), BytePos(11)),
|
||||
item_id: None,
|
||||
kind: DocFragmentKind::RawDoc,
|
||||
kind: DocFragmentKind::Raw(DUMMY_SP),
|
||||
doc: sym::empty, // unused placeholder
|
||||
indent: 0,
|
||||
from_expansion: false,
|
||||
|
|
@ -40,7 +40,7 @@ fn utf8() {
|
|||
&[DocFragment {
|
||||
span: Span::with_root_ctxt(BytePos(8), BytePos(14)),
|
||||
item_id: None,
|
||||
kind: DocFragmentKind::RawDoc,
|
||||
kind: DocFragmentKind::Raw(DUMMY_SP),
|
||||
doc: sym::empty, // unused placeholder
|
||||
indent: 0,
|
||||
from_expansion: false,
|
||||
|
|
|
|||
|
|
@ -591,7 +591,7 @@ impl () {}
|
|||
/// # pub unsafe fn malloc(_size: usize) -> *mut core::ffi::c_void { core::ptr::NonNull::dangling().as_ptr() }
|
||||
/// # pub unsafe fn free(_ptr: *mut core::ffi::c_void) {}
|
||||
/// # }
|
||||
/// # #[cfg(any())]
|
||||
/// # #[cfg(false)]
|
||||
/// #[allow(unused_extern_crates)]
|
||||
/// extern crate libc;
|
||||
///
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ fn main() {
|
|||
println!("cargo:rustc-cfg=netbsd10");
|
||||
}
|
||||
|
||||
// Needed for `#![doc(auto_cfg(hide(no_global_oom_handling)))]` attribute.
|
||||
println!("cargo::rustc-check-cfg=cfg(no_global_oom_handling)");
|
||||
|
||||
println!("cargo:rustc-check-cfg=cfg(restricted_std)");
|
||||
if target_os == "linux"
|
||||
|| target_os == "android"
|
||||
|
|
|
|||
|
|
@ -892,8 +892,8 @@ impl Step for Std {
|
|||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct RustcDev {
|
||||
/// The compiler that will build rustc which will be shipped in this component.
|
||||
build_compiler: Compiler,
|
||||
target: TargetSelection,
|
||||
pub build_compiler: Compiler,
|
||||
pub target: TargetSelection,
|
||||
}
|
||||
|
||||
impl RustcDev {
|
||||
|
|
|
|||
|
|
@ -279,6 +279,17 @@ install!((self, builder, _config),
|
|||
});
|
||||
install_sh(builder, "rustc", self.build_compiler, Some(self.target), &tarball);
|
||||
};
|
||||
RustcDev, alias = "rustc-dev", Self::should_build(_config), IS_HOST: true, {
|
||||
if let Some(tarball) = builder.ensure(dist::RustcDev {
|
||||
build_compiler: self.build_compiler, target: self.target
|
||||
}) {
|
||||
install_sh(builder, "rustc-dev", self.build_compiler, Some(self.target), &tarball);
|
||||
} else {
|
||||
builder.info(
|
||||
&format!("skipping Install RustcDev stage{} ({})", self.build_compiler.stage + 1, self.target),
|
||||
);
|
||||
}
|
||||
};
|
||||
RustcCodegenCranelift, alias = "rustc-codegen-cranelift", Self::should_build(_config), IS_HOST: true, {
|
||||
if let Some(tarball) = builder.ensure(dist::CraneliftCodegenBackend {
|
||||
compilers: RustcPrivateCompilers::from_build_compiler(builder, self.build_compiler, self.target),
|
||||
|
|
|
|||
|
|
@ -998,6 +998,7 @@ impl<'a> Builder<'a> {
|
|||
// binary path, we must install rustc before the tools. Otherwise, the rust-installer will
|
||||
// install the same binaries twice for each tool, leaving backup files (*.old) as a result.
|
||||
install::Rustc,
|
||||
install::RustcDev,
|
||||
install::Cargo,
|
||||
install::RustAnalyzer,
|
||||
install::Rustfmt,
|
||||
|
|
|
|||
|
|
@ -2912,6 +2912,7 @@ mod snapshot {
|
|||
[build] rustc 1 <x86_64-unknown-linux-gnu> -> rust-analyzer-proc-macro-srv 2 <x86_64-unknown-linux-gnu>
|
||||
[build] rustc 0 <x86_64-unknown-linux-gnu> -> GenerateCopyright 1 <x86_64-unknown-linux-gnu>
|
||||
[dist] rustc <x86_64-unknown-linux-gnu>
|
||||
[dist] rustc 1 <x86_64-unknown-linux-gnu> -> rustc-dev 2 <x86_64-unknown-linux-gnu>
|
||||
[build] rustc 1 <x86_64-unknown-linux-gnu> -> cargo 2 <x86_64-unknown-linux-gnu>
|
||||
[dist] rustc 1 <x86_64-unknown-linux-gnu> -> cargo 2 <x86_64-unknown-linux-gnu>
|
||||
[build] rustc 1 <x86_64-unknown-linux-gnu> -> rust-analyzer 2 <x86_64-unknown-linux-gnu>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
FROM ubuntu:22.04
|
||||
FROM ghcr.io/rust-lang/ubuntu:24.04
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
FROM ghcr.io/rust-lang/ubuntu:22.04
|
||||
FROM ghcr.io/rust-lang/ubuntu:24.04
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ base64 = "0.21.7"
|
|||
indexmap = { version = "2", features = ["serde"] }
|
||||
itertools = "0.12"
|
||||
minifier = { version = "0.3.5", default-features = false }
|
||||
proc-macro2 = "1.0.103"
|
||||
pulldown-cmark-escape = { version = "0.11.0", features = ["simd"] }
|
||||
regex = "1"
|
||||
rustdoc-json-types = { path = "../rustdoc-json-types" }
|
||||
|
|
|
|||
|
|
@ -9,12 +9,13 @@ use std::{fmt, mem, ops};
|
|||
use itertools::Either;
|
||||
use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_data_structures::thin_vec::{ThinVec, thin_vec};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::Attribute;
|
||||
use rustc_hir::attrs::{self, AttributeKind, CfgEntry, CfgHideShow, HideOrShow};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::parse::ParseSess;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::symbol::{Symbol, sym};
|
||||
use {rustc_ast as ast, rustc_hir as hir};
|
||||
use rustc_span::{DUMMY_SP, Span};
|
||||
|
||||
use crate::display::{Joined as _, MaybeDisplay, Wrapped};
|
||||
use crate::html::escape::Escape;
|
||||
|
|
@ -22,21 +23,11 @@ use crate::html::escape::Escape;
|
|||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) enum Cfg {
|
||||
/// Accepts all configurations.
|
||||
True,
|
||||
/// Denies all configurations.
|
||||
False,
|
||||
/// A generic configuration option, e.g., `test` or `target_os = "linux"`.
|
||||
Cfg(Symbol, Option<Symbol>),
|
||||
/// Negates a configuration requirement, i.e., `not(x)`.
|
||||
Not(Box<Cfg>),
|
||||
/// Union of a list of configuration requirements, i.e., `any(...)`.
|
||||
Any(Vec<Cfg>),
|
||||
/// Intersection of a list of configuration requirements, i.e., `all(...)`.
|
||||
All(Vec<Cfg>),
|
||||
}
|
||||
#[derive(Clone, Debug, Hash)]
|
||||
// Because `CfgEntry` includes `Span`, we must NEVER use `==`/`!=` operators on `Cfg` and instead
|
||||
// use `is_equivalent_to`.
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub(crate) struct Cfg(CfgEntry);
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub(crate) struct InvalidCfgError {
|
||||
|
|
@ -44,27 +35,95 @@ pub(crate) struct InvalidCfgError {
|
|||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
/// Whether the configuration consists of just `Cfg` or `Not`.
|
||||
fn is_simple_cfg(cfg: &CfgEntry) -> bool {
|
||||
match cfg {
|
||||
CfgEntry::Bool(..)
|
||||
| CfgEntry::NameValue { .. }
|
||||
| CfgEntry::Not(..)
|
||||
| CfgEntry::Version(..) => true,
|
||||
CfgEntry::All(..) | CfgEntry::Any(..) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `false` if is `Any`, otherwise returns `true`.
|
||||
fn is_all_cfg(cfg: &CfgEntry) -> bool {
|
||||
match cfg {
|
||||
CfgEntry::Bool(..)
|
||||
| CfgEntry::NameValue { .. }
|
||||
| CfgEntry::Not(..)
|
||||
| CfgEntry::Version(..)
|
||||
| CfgEntry::All(..) => true,
|
||||
CfgEntry::Any(..) => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn strip_hidden(cfg: &CfgEntry, hidden: &FxHashSet<NameValueCfg>) -> Option<CfgEntry> {
|
||||
match cfg {
|
||||
CfgEntry::Bool(..) => Some(cfg.clone()),
|
||||
CfgEntry::NameValue { .. } => {
|
||||
if !hidden.contains(&NameValueCfg::from(cfg)) {
|
||||
Some(cfg.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
CfgEntry::Not(cfg, _) => {
|
||||
if let Some(cfg) = strip_hidden(cfg, hidden) {
|
||||
Some(CfgEntry::Not(Box::new(cfg), DUMMY_SP))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
CfgEntry::Any(cfgs, _) => {
|
||||
let cfgs =
|
||||
cfgs.iter().filter_map(|cfg| strip_hidden(cfg, hidden)).collect::<ThinVec<_>>();
|
||||
if cfgs.is_empty() { None } else { Some(CfgEntry::Any(cfgs, DUMMY_SP)) }
|
||||
}
|
||||
CfgEntry::All(cfgs, _) => {
|
||||
let cfgs =
|
||||
cfgs.iter().filter_map(|cfg| strip_hidden(cfg, hidden)).collect::<ThinVec<_>>();
|
||||
if cfgs.is_empty() { None } else { Some(CfgEntry::All(cfgs, DUMMY_SP)) }
|
||||
}
|
||||
CfgEntry::Version(..) => {
|
||||
// FIXME: Should be handled.
|
||||
Some(cfg.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn should_capitalize_first_letter(cfg: &CfgEntry) -> bool {
|
||||
match cfg {
|
||||
CfgEntry::Bool(..) | CfgEntry::Not(..) | CfgEntry::Version(..) => true,
|
||||
CfgEntry::Any(sub_cfgs, _) | CfgEntry::All(sub_cfgs, _) => {
|
||||
sub_cfgs.first().map(should_capitalize_first_letter).unwrap_or(false)
|
||||
}
|
||||
CfgEntry::NameValue { name, .. } => {
|
||||
*name == sym::debug_assertions || *name == sym::target_endian
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Cfg {
|
||||
/// Parses a `MetaItemInner` into a `Cfg`.
|
||||
fn parse_nested(
|
||||
nested_cfg: &MetaItemInner,
|
||||
exclude: &FxHashSet<Cfg>,
|
||||
exclude: &FxHashSet<NameValueCfg>,
|
||||
) -> Result<Option<Cfg>, InvalidCfgError> {
|
||||
match nested_cfg {
|
||||
MetaItemInner::MetaItem(cfg) => Cfg::parse_without(cfg, exclude),
|
||||
MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => match *b {
|
||||
true => Ok(Some(Cfg::True)),
|
||||
false => Ok(Some(Cfg::False)),
|
||||
},
|
||||
MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => {
|
||||
Ok(Some(Cfg(CfgEntry::Bool(*b, DUMMY_SP))))
|
||||
}
|
||||
MetaItemInner::Lit(lit) => {
|
||||
Err(InvalidCfgError { msg: "unexpected literal", span: lit.span })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn parse_without(
|
||||
fn parse_without(
|
||||
cfg: &MetaItem,
|
||||
exclude: &FxHashSet<Cfg>,
|
||||
exclude: &FxHashSet<NameValueCfg>,
|
||||
) -> Result<Option<Cfg>, InvalidCfgError> {
|
||||
let name = match cfg.ident() {
|
||||
Some(ident) => ident.name,
|
||||
|
|
@ -77,13 +136,23 @@ impl Cfg {
|
|||
};
|
||||
match cfg.kind {
|
||||
MetaItemKind::Word => {
|
||||
let cfg = Cfg::Cfg(name, None);
|
||||
if exclude.contains(&cfg) { Ok(None) } else { Ok(Some(cfg)) }
|
||||
if exclude.contains(&NameValueCfg::new(name)) {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(Cfg(CfgEntry::NameValue { name, value: None, span: DUMMY_SP })))
|
||||
}
|
||||
}
|
||||
MetaItemKind::NameValue(ref lit) => match lit.kind {
|
||||
LitKind::Str(value, _) => {
|
||||
let cfg = Cfg::Cfg(name, Some(value));
|
||||
if exclude.contains(&cfg) { Ok(None) } else { Ok(Some(cfg)) }
|
||||
if exclude.contains(&NameValueCfg::new_value(name, value)) {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(Cfg(CfgEntry::NameValue {
|
||||
name,
|
||||
value: Some(value),
|
||||
span: DUMMY_SP,
|
||||
})))
|
||||
}
|
||||
}
|
||||
_ => Err(InvalidCfgError {
|
||||
// FIXME: if the main #[cfg] syntax decided to support non-string literals,
|
||||
|
|
@ -97,8 +166,12 @@ impl Cfg {
|
|||
let mut sub_cfgs =
|
||||
items.iter().filter_map(|i| Cfg::parse_nested(i, exclude).transpose());
|
||||
let ret = match name {
|
||||
sym::all => sub_cfgs.try_fold(Cfg::True, |x, y| Ok(x & y?)),
|
||||
sym::any => sub_cfgs.try_fold(Cfg::False, |x, y| Ok(x | y?)),
|
||||
sym::all => {
|
||||
sub_cfgs.try_fold(Cfg(CfgEntry::Bool(true, DUMMY_SP)), |x, y| Ok(x & y?))
|
||||
}
|
||||
sym::any => {
|
||||
sub_cfgs.try_fold(Cfg(CfgEntry::Bool(false, DUMMY_SP)), |x, y| Ok(x | y?))
|
||||
}
|
||||
sym::not => {
|
||||
if orig_len == 1 {
|
||||
let mut sub_cfgs = sub_cfgs.collect::<Vec<_>>();
|
||||
|
|
@ -132,40 +205,10 @@ impl Cfg {
|
|||
Self::parse_nested(cfg, &FxHashSet::default()).map(|ret| ret.unwrap())
|
||||
}
|
||||
|
||||
/// Checks whether the given configuration can be matched in the current session.
|
||||
///
|
||||
/// Equivalent to `attr::cfg_matches`.
|
||||
pub(crate) fn matches(&self, psess: &ParseSess) -> bool {
|
||||
match *self {
|
||||
Cfg::False => false,
|
||||
Cfg::True => true,
|
||||
Cfg::Not(ref child) => !child.matches(psess),
|
||||
Cfg::All(ref sub_cfgs) => sub_cfgs.iter().all(|sub_cfg| sub_cfg.matches(psess)),
|
||||
Cfg::Any(ref sub_cfgs) => sub_cfgs.iter().any(|sub_cfg| sub_cfg.matches(psess)),
|
||||
Cfg::Cfg(name, value) => psess.config.contains(&(name, value)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the configuration consists of just `Cfg` or `Not`.
|
||||
fn is_simple(&self) -> bool {
|
||||
match self {
|
||||
Cfg::False | Cfg::True | Cfg::Cfg(..) | Cfg::Not(..) => true,
|
||||
Cfg::All(..) | Cfg::Any(..) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the configuration consists of just `Cfg`, `Not` or `All`.
|
||||
fn is_all(&self) -> bool {
|
||||
match self {
|
||||
Cfg::False | Cfg::True | Cfg::Cfg(..) | Cfg::Not(..) | Cfg::All(..) => true,
|
||||
Cfg::Any(..) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Renders the configuration for human display, as a short HTML description.
|
||||
pub(crate) fn render_short_html(&self) -> String {
|
||||
let mut msg = Display(self, Format::ShortHtml).to_string();
|
||||
if self.should_capitalize_first_letter()
|
||||
let mut msg = Display(&self.0, Format::ShortHtml).to_string();
|
||||
if should_capitalize_first_letter(&self.0)
|
||||
&& let Some(i) = msg.find(|c: char| c.is_ascii_alphanumeric())
|
||||
{
|
||||
msg[i..i + 1].make_ascii_uppercase();
|
||||
|
|
@ -183,9 +226,9 @@ impl Cfg {
|
|||
};
|
||||
|
||||
let mut msg = if matches!(format, Format::LongHtml) {
|
||||
format!("Available{on}<strong>{}</strong>", Display(self, format))
|
||||
format!("Available{on}<strong>{}</strong>", Display(&self.0, format))
|
||||
} else {
|
||||
format!("Available{on}{}", Display(self, format))
|
||||
format!("Available{on}{}", Display(&self.0, format))
|
||||
};
|
||||
if self.should_append_only_to_description() {
|
||||
msg.push_str(" only");
|
||||
|
|
@ -205,27 +248,19 @@ impl Cfg {
|
|||
self.render_long_inner(Format::LongPlain)
|
||||
}
|
||||
|
||||
fn should_capitalize_first_letter(&self) -> bool {
|
||||
match *self {
|
||||
Cfg::False | Cfg::True | Cfg::Not(..) => true,
|
||||
Cfg::Any(ref sub_cfgs) | Cfg::All(ref sub_cfgs) => {
|
||||
sub_cfgs.first().map(Cfg::should_capitalize_first_letter).unwrap_or(false)
|
||||
}
|
||||
Cfg::Cfg(name, _) => name == sym::debug_assertions || name == sym::target_endian,
|
||||
}
|
||||
}
|
||||
|
||||
fn should_append_only_to_description(&self) -> bool {
|
||||
match self {
|
||||
Cfg::False | Cfg::True => false,
|
||||
Cfg::Any(..) | Cfg::All(..) | Cfg::Cfg(..) => true,
|
||||
Cfg::Not(box Cfg::Cfg(..)) => true,
|
||||
Cfg::Not(..) => false,
|
||||
match self.0 {
|
||||
CfgEntry::Any(..)
|
||||
| CfgEntry::All(..)
|
||||
| CfgEntry::NameValue { .. }
|
||||
| CfgEntry::Version(..)
|
||||
| CfgEntry::Not(box CfgEntry::NameValue { .. }, _) => true,
|
||||
CfgEntry::Not(..) | CfgEntry::Bool(..) => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn should_use_with_in_description(&self) -> bool {
|
||||
matches!(self, Cfg::Cfg(sym::target_feature, _))
|
||||
matches!(self.0, CfgEntry::NameValue { name, .. } if name == sym::target_feature)
|
||||
}
|
||||
|
||||
/// Attempt to simplify this cfg by assuming that `assume` is already known to be true, will
|
||||
|
|
@ -234,22 +269,22 @@ impl Cfg {
|
|||
///
|
||||
/// See `tests::test_simplify_with` for examples.
|
||||
pub(crate) fn simplify_with(&self, assume: &Self) -> Option<Self> {
|
||||
if self == assume {
|
||||
if self.0.is_equivalent_to(&assume.0) {
|
||||
None
|
||||
} else if let Cfg::All(a) = self {
|
||||
let mut sub_cfgs: Vec<Cfg> = if let Cfg::All(b) = assume {
|
||||
a.iter().filter(|a| !b.contains(a)).cloned().collect()
|
||||
} else if let CfgEntry::All(a, _) = &self.0 {
|
||||
let mut sub_cfgs: ThinVec<CfgEntry> = if let CfgEntry::All(b, _) = &assume.0 {
|
||||
a.iter().filter(|a| !b.iter().any(|b| a.is_equivalent_to(b))).cloned().collect()
|
||||
} else {
|
||||
a.iter().filter(|&a| a != assume).cloned().collect()
|
||||
a.iter().filter(|&a| !a.is_equivalent_to(&assume.0)).cloned().collect()
|
||||
};
|
||||
let len = sub_cfgs.len();
|
||||
match len {
|
||||
0 => None,
|
||||
1 => sub_cfgs.pop(),
|
||||
_ => Some(Cfg::All(sub_cfgs)),
|
||||
1 => sub_cfgs.pop().map(Cfg),
|
||||
_ => Some(Cfg(CfgEntry::All(sub_cfgs, DUMMY_SP))),
|
||||
}
|
||||
} else if let Cfg::All(b) = assume
|
||||
&& b.contains(self)
|
||||
} else if let CfgEntry::All(b, _) = &assume.0
|
||||
&& b.iter().any(|b| b.is_equivalent_to(&self.0))
|
||||
{
|
||||
None
|
||||
} else {
|
||||
|
|
@ -258,81 +293,54 @@ impl Cfg {
|
|||
}
|
||||
|
||||
fn omit_preposition(&self) -> bool {
|
||||
matches!(self, Cfg::True | Cfg::False)
|
||||
matches!(self.0, CfgEntry::Bool(..))
|
||||
}
|
||||
|
||||
pub(crate) fn strip_hidden(&self, hidden: &FxHashSet<Cfg>) -> Option<Self> {
|
||||
match self {
|
||||
Self::True | Self::False => Some(self.clone()),
|
||||
Self::Cfg(..) => {
|
||||
if !hidden.contains(self) {
|
||||
Some(self.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Self::Not(cfg) => {
|
||||
if let Some(cfg) = cfg.strip_hidden(hidden) {
|
||||
Some(Self::Not(Box::new(cfg)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Self::Any(cfgs) => {
|
||||
let cfgs =
|
||||
cfgs.iter().filter_map(|cfg| cfg.strip_hidden(hidden)).collect::<Vec<_>>();
|
||||
if cfgs.is_empty() { None } else { Some(Self::Any(cfgs)) }
|
||||
}
|
||||
Self::All(cfgs) => {
|
||||
let cfgs =
|
||||
cfgs.iter().filter_map(|cfg| cfg.strip_hidden(hidden)).collect::<Vec<_>>();
|
||||
if cfgs.is_empty() { None } else { Some(Self::All(cfgs)) }
|
||||
}
|
||||
}
|
||||
pub(crate) fn inner(&self) -> &CfgEntry {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Not for Cfg {
|
||||
type Output = Cfg;
|
||||
fn not(self) -> Cfg {
|
||||
match self {
|
||||
Cfg::False => Cfg::True,
|
||||
Cfg::True => Cfg::False,
|
||||
Cfg::Not(cfg) => *cfg,
|
||||
s => Cfg::Not(Box::new(s)),
|
||||
}
|
||||
Cfg(match self.0 {
|
||||
CfgEntry::Bool(v, s) => CfgEntry::Bool(!v, s),
|
||||
CfgEntry::Not(cfg, _) => *cfg,
|
||||
s => CfgEntry::Not(Box::new(s), DUMMY_SP),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::BitAndAssign for Cfg {
|
||||
fn bitand_assign(&mut self, other: Cfg) {
|
||||
match (self, other) {
|
||||
(Cfg::False, _) | (_, Cfg::True) => {}
|
||||
(s, Cfg::False) => *s = Cfg::False,
|
||||
(s @ Cfg::True, b) => *s = b,
|
||||
(Cfg::All(a), Cfg::All(ref mut b)) => {
|
||||
match (&mut self.0, other.0) {
|
||||
(CfgEntry::Bool(false, _), _) | (_, CfgEntry::Bool(true, _)) => {}
|
||||
(s, CfgEntry::Bool(false, _)) => *s = CfgEntry::Bool(false, DUMMY_SP),
|
||||
(s @ CfgEntry::Bool(true, _), b) => *s = b,
|
||||
(CfgEntry::All(a, _), CfgEntry::All(ref mut b, _)) => {
|
||||
for c in b.drain(..) {
|
||||
if !a.contains(&c) {
|
||||
if !a.iter().any(|a| a.is_equivalent_to(&c)) {
|
||||
a.push(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
(Cfg::All(a), ref mut b) => {
|
||||
if !a.contains(b) {
|
||||
a.push(mem::replace(b, Cfg::True));
|
||||
(CfgEntry::All(a, _), ref mut b) => {
|
||||
if !a.iter().any(|a| a.is_equivalent_to(b)) {
|
||||
a.push(mem::replace(b, CfgEntry::Bool(true, DUMMY_SP)));
|
||||
}
|
||||
}
|
||||
(s, Cfg::All(mut a)) => {
|
||||
let b = mem::replace(s, Cfg::True);
|
||||
if !a.contains(&b) {
|
||||
(s, CfgEntry::All(mut a, _)) => {
|
||||
let b = mem::replace(s, CfgEntry::Bool(true, DUMMY_SP));
|
||||
if !a.iter().any(|a| a.is_equivalent_to(&b)) {
|
||||
a.push(b);
|
||||
}
|
||||
*s = Cfg::All(a);
|
||||
*s = CfgEntry::All(a, DUMMY_SP);
|
||||
}
|
||||
(s, b) => {
|
||||
if *s != b {
|
||||
let a = mem::replace(s, Cfg::True);
|
||||
*s = Cfg::All(vec![a, b]);
|
||||
if !s.is_equivalent_to(&b) {
|
||||
let a = mem::replace(s, CfgEntry::Bool(true, DUMMY_SP));
|
||||
*s = CfgEntry::All(thin_vec![a, b], DUMMY_SP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -349,32 +357,34 @@ impl ops::BitAnd for Cfg {
|
|||
|
||||
impl ops::BitOrAssign for Cfg {
|
||||
fn bitor_assign(&mut self, other: Cfg) {
|
||||
match (self, other) {
|
||||
(Cfg::True, _) | (_, Cfg::False) | (_, Cfg::True) => {}
|
||||
(s @ Cfg::False, b) => *s = b,
|
||||
(Cfg::Any(a), Cfg::Any(ref mut b)) => {
|
||||
match (&mut self.0, other.0) {
|
||||
(CfgEntry::Bool(true, _), _)
|
||||
| (_, CfgEntry::Bool(false, _))
|
||||
| (_, CfgEntry::Bool(true, _)) => {}
|
||||
(s @ CfgEntry::Bool(false, _), b) => *s = b,
|
||||
(CfgEntry::Any(a, _), CfgEntry::Any(ref mut b, _)) => {
|
||||
for c in b.drain(..) {
|
||||
if !a.contains(&c) {
|
||||
if !a.iter().any(|a| a.is_equivalent_to(&c)) {
|
||||
a.push(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
(Cfg::Any(a), ref mut b) => {
|
||||
if !a.contains(b) {
|
||||
a.push(mem::replace(b, Cfg::True));
|
||||
(CfgEntry::Any(a, _), ref mut b) => {
|
||||
if !a.iter().any(|a| a.is_equivalent_to(b)) {
|
||||
a.push(mem::replace(b, CfgEntry::Bool(true, DUMMY_SP)));
|
||||
}
|
||||
}
|
||||
(s, Cfg::Any(mut a)) => {
|
||||
let b = mem::replace(s, Cfg::True);
|
||||
if !a.contains(&b) {
|
||||
(s, CfgEntry::Any(mut a, _)) => {
|
||||
let b = mem::replace(s, CfgEntry::Bool(true, DUMMY_SP));
|
||||
if !a.iter().any(|a| a.is_equivalent_to(&b)) {
|
||||
a.push(b);
|
||||
}
|
||||
*s = Cfg::Any(a);
|
||||
*s = CfgEntry::Any(a, DUMMY_SP);
|
||||
}
|
||||
(s, b) => {
|
||||
if *s != b {
|
||||
let a = mem::replace(s, Cfg::True);
|
||||
*s = Cfg::Any(vec![a, b]);
|
||||
if !s.is_equivalent_to(&b) {
|
||||
let a = mem::replace(s, CfgEntry::Bool(true, DUMMY_SP));
|
||||
*s = CfgEntry::Any(thin_vec![a, b], DUMMY_SP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -417,7 +427,7 @@ impl Format {
|
|||
}
|
||||
|
||||
/// Pretty-print wrapper for a `Cfg`. Also indicates what form of rendering should be used.
|
||||
struct Display<'a>(&'a Cfg, Format);
|
||||
struct Display<'a>(&'a CfgEntry, Format);
|
||||
|
||||
impl Display<'_> {
|
||||
fn code_wrappers(&self) -> Wrapped<&'static str> {
|
||||
|
|
@ -427,17 +437,21 @@ impl Display<'_> {
|
|||
fn display_sub_cfgs(
|
||||
&self,
|
||||
fmt: &mut fmt::Formatter<'_>,
|
||||
sub_cfgs: &[Cfg],
|
||||
sub_cfgs: &[CfgEntry],
|
||||
separator: &str,
|
||||
) -> fmt::Result {
|
||||
use fmt::Display as _;
|
||||
|
||||
let short_longhand = self.1.is_long() && {
|
||||
let all_crate_features =
|
||||
sub_cfgs.iter().all(|sub_cfg| matches!(sub_cfg, Cfg::Cfg(sym::feature, Some(_))));
|
||||
let all_target_features = sub_cfgs
|
||||
.iter()
|
||||
.all(|sub_cfg| matches!(sub_cfg, Cfg::Cfg(sym::target_feature, Some(_))));
|
||||
let all_crate_features = sub_cfgs.iter().all(|sub_cfg| {
|
||||
matches!(sub_cfg, CfgEntry::NameValue { name: sym::feature, value: Some(_), .. })
|
||||
});
|
||||
let all_target_features = sub_cfgs.iter().all(|sub_cfg| {
|
||||
matches!(
|
||||
sub_cfg,
|
||||
CfgEntry::NameValue { name: sym::target_feature, value: Some(_), .. }
|
||||
)
|
||||
});
|
||||
|
||||
if all_crate_features {
|
||||
fmt.write_str("crate features ")?;
|
||||
|
|
@ -454,14 +468,14 @@ impl Display<'_> {
|
|||
sub_cfgs
|
||||
.iter()
|
||||
.map(|sub_cfg| {
|
||||
if let Cfg::Cfg(_, Some(feat)) = sub_cfg
|
||||
if let CfgEntry::NameValue { value: Some(feat), .. } = sub_cfg
|
||||
&& short_longhand
|
||||
{
|
||||
Either::Left(self.code_wrappers().wrap(feat))
|
||||
} else {
|
||||
Either::Right(
|
||||
Wrapped::with_parens()
|
||||
.when(!sub_cfg.is_all())
|
||||
.when(!is_all_cfg(sub_cfg))
|
||||
.wrap(Display(sub_cfg, self.1)),
|
||||
)
|
||||
}
|
||||
|
|
@ -476,35 +490,41 @@ impl Display<'_> {
|
|||
|
||||
impl fmt::Display for Display<'_> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self.0 {
|
||||
Cfg::Not(box Cfg::Any(sub_cfgs)) => {
|
||||
let separator =
|
||||
if sub_cfgs.iter().all(Cfg::is_simple) { " nor " } else { ", nor " };
|
||||
match &self.0 {
|
||||
CfgEntry::Not(box CfgEntry::Any(sub_cfgs, _), _) => {
|
||||
let separator = if sub_cfgs.iter().all(is_simple_cfg) { " nor " } else { ", nor " };
|
||||
fmt.write_str("neither ")?;
|
||||
|
||||
sub_cfgs
|
||||
.iter()
|
||||
.map(|sub_cfg| {
|
||||
Wrapped::with_parens()
|
||||
.when(!sub_cfg.is_all())
|
||||
.when(!is_all_cfg(sub_cfg))
|
||||
.wrap(Display(sub_cfg, self.1))
|
||||
})
|
||||
.joined(separator, fmt)
|
||||
}
|
||||
Cfg::Not(box simple @ Cfg::Cfg(..)) => write!(fmt, "non-{}", Display(simple, self.1)),
|
||||
Cfg::Not(box c) => write!(fmt, "not ({})", Display(c, self.1)),
|
||||
|
||||
Cfg::Any(sub_cfgs) => {
|
||||
let separator = if sub_cfgs.iter().all(Cfg::is_simple) { " or " } else { ", or " };
|
||||
self.display_sub_cfgs(fmt, sub_cfgs, separator)
|
||||
CfgEntry::Not(box simple @ CfgEntry::NameValue { .. }, _) => {
|
||||
write!(fmt, "non-{}", Display(simple, self.1))
|
||||
}
|
||||
Cfg::All(sub_cfgs) => self.display_sub_cfgs(fmt, sub_cfgs, " and "),
|
||||
CfgEntry::Not(box c, _) => write!(fmt, "not ({})", Display(c, self.1)),
|
||||
|
||||
Cfg::True => fmt.write_str("everywhere"),
|
||||
Cfg::False => fmt.write_str("nowhere"),
|
||||
CfgEntry::Any(sub_cfgs, _) => {
|
||||
let separator = if sub_cfgs.iter().all(is_simple_cfg) { " or " } else { ", or " };
|
||||
self.display_sub_cfgs(fmt, sub_cfgs.as_slice(), separator)
|
||||
}
|
||||
CfgEntry::All(sub_cfgs, _) => self.display_sub_cfgs(fmt, sub_cfgs.as_slice(), " and "),
|
||||
|
||||
&Cfg::Cfg(name, value) => {
|
||||
let human_readable = match (name, value) {
|
||||
CfgEntry::Bool(v, _) => {
|
||||
if *v {
|
||||
fmt.write_str("everywhere")
|
||||
} else {
|
||||
fmt.write_str("nowhere")
|
||||
}
|
||||
}
|
||||
|
||||
&CfgEntry::NameValue { name, value, .. } => {
|
||||
let human_readable = match (*name, value) {
|
||||
(sym::unix, None) => "Unix",
|
||||
(sym::windows, None) => "Windows",
|
||||
(sym::debug_assertions, None) => "debug-assertions enabled",
|
||||
|
|
@ -572,8 +592,12 @@ impl fmt::Display for Display<'_> {
|
|||
"sgx" => "SGX",
|
||||
_ => "",
|
||||
},
|
||||
(sym::target_endian, Some(endian)) => return write!(fmt, "{endian}-endian"),
|
||||
(sym::target_pointer_width, Some(bits)) => return write!(fmt, "{bits}-bit"),
|
||||
(sym::target_endian, Some(endian)) => {
|
||||
return write!(fmt, "{endian}-endian");
|
||||
}
|
||||
(sym::target_pointer_width, Some(bits)) => {
|
||||
return write!(fmt, "{bits}-bit");
|
||||
}
|
||||
(sym::target_feature, Some(feat)) => match self.1 {
|
||||
Format::LongHtml => {
|
||||
return write!(fmt, "target feature <code>{feat}</code>");
|
||||
|
|
@ -601,16 +625,52 @@ impl fmt::Display for Display<'_> {
|
|||
.fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
CfgEntry::Version(..) => {
|
||||
// FIXME: Should we handle it?
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
struct NameValueCfg {
|
||||
name: Symbol,
|
||||
value: Option<Symbol>,
|
||||
}
|
||||
|
||||
impl NameValueCfg {
|
||||
fn new(name: Symbol) -> Self {
|
||||
Self { name, value: None }
|
||||
}
|
||||
|
||||
fn new_value(name: Symbol, value: Symbol) -> Self {
|
||||
Self { name, value: Some(value) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a CfgEntry> for NameValueCfg {
|
||||
fn from(cfg: &'a CfgEntry) -> Self {
|
||||
match cfg {
|
||||
CfgEntry::NameValue { name, value, .. } => NameValueCfg { name: *name, value: *value },
|
||||
_ => NameValueCfg { name: sym::empty, value: None },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a attrs::CfgInfo> for NameValueCfg {
|
||||
fn from(cfg: &'a attrs::CfgInfo) -> Self {
|
||||
Self { name: cfg.name, value: cfg.value.map(|(value, _)| value) }
|
||||
}
|
||||
}
|
||||
|
||||
/// This type keeps track of (doc) cfg information as we go down the item tree.
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct CfgInfo {
|
||||
/// List of currently active `doc(auto_cfg(hide(...)))` cfgs, minus currently active
|
||||
/// `doc(auto_cfg(show(...)))` cfgs.
|
||||
hidden_cfg: FxHashSet<Cfg>,
|
||||
hidden_cfg: FxHashSet<NameValueCfg>,
|
||||
/// Current computed `cfg`. Each time we enter a new item, this field is updated as well while
|
||||
/// taking into account the `hidden_cfg` information.
|
||||
current_cfg: Cfg,
|
||||
|
|
@ -626,11 +686,11 @@ impl Default for CfgInfo {
|
|||
fn default() -> Self {
|
||||
Self {
|
||||
hidden_cfg: FxHashSet::from_iter([
|
||||
Cfg::Cfg(sym::test, None),
|
||||
Cfg::Cfg(sym::doc, None),
|
||||
Cfg::Cfg(sym::doctest, None),
|
||||
NameValueCfg::new(sym::test),
|
||||
NameValueCfg::new(sym::doc),
|
||||
NameValueCfg::new(sym::doctest),
|
||||
]),
|
||||
current_cfg: Cfg::True,
|
||||
current_cfg: Cfg(CfgEntry::Bool(true, DUMMY_SP)),
|
||||
auto_cfg_active: true,
|
||||
parent_is_doc_cfg: false,
|
||||
}
|
||||
|
|
@ -662,33 +722,26 @@ fn show_hide_show_conflict_error(
|
|||
fn handle_auto_cfg_hide_show(
|
||||
tcx: TyCtxt<'_>,
|
||||
cfg_info: &mut CfgInfo,
|
||||
sub_attr: &MetaItemInner,
|
||||
is_show: bool,
|
||||
attr: &CfgHideShow,
|
||||
new_show_attrs: &mut FxHashMap<(Symbol, Option<Symbol>), rustc_span::Span>,
|
||||
new_hide_attrs: &mut FxHashMap<(Symbol, Option<Symbol>), rustc_span::Span>,
|
||||
) {
|
||||
if let MetaItemInner::MetaItem(item) = sub_attr
|
||||
&& let MetaItemKind::List(items) = &item.kind
|
||||
{
|
||||
for item in items {
|
||||
// FIXME: Report in case `Cfg::parse` reports an error?
|
||||
if let Ok(Cfg::Cfg(key, value)) = Cfg::parse(item) {
|
||||
if is_show {
|
||||
if let Some(span) = new_hide_attrs.get(&(key, value)) {
|
||||
show_hide_show_conflict_error(tcx, item.span(), *span);
|
||||
} else {
|
||||
new_show_attrs.insert((key, value), item.span());
|
||||
}
|
||||
cfg_info.hidden_cfg.remove(&Cfg::Cfg(key, value));
|
||||
} else {
|
||||
if let Some(span) = new_show_attrs.get(&(key, value)) {
|
||||
show_hide_show_conflict_error(tcx, item.span(), *span);
|
||||
} else {
|
||||
new_hide_attrs.insert((key, value), item.span());
|
||||
}
|
||||
cfg_info.hidden_cfg.insert(Cfg::Cfg(key, value));
|
||||
}
|
||||
for value in &attr.values {
|
||||
let simple = NameValueCfg::from(value);
|
||||
if attr.kind == HideOrShow::Show {
|
||||
if let Some(span) = new_hide_attrs.get(&(simple.name, simple.value)) {
|
||||
show_hide_show_conflict_error(tcx, value.span_for_name_and_value(), *span);
|
||||
} else {
|
||||
new_show_attrs.insert((simple.name, simple.value), value.span_for_name_and_value());
|
||||
}
|
||||
cfg_info.hidden_cfg.remove(&simple);
|
||||
} else {
|
||||
if let Some(span) = new_show_attrs.get(&(simple.name, simple.value)) {
|
||||
show_hide_show_conflict_error(tcx, value.span_for_name_and_value(), *span);
|
||||
} else {
|
||||
new_hide_attrs.insert((simple.name, simple.value), value.span_for_name_and_value());
|
||||
}
|
||||
cfg_info.hidden_cfg.insert(simple);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -709,7 +762,7 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator<Item = &'a hir::Attribute>
|
|||
|
||||
fn check_changed_auto_active_status(
|
||||
changed_auto_active_status: &mut Option<rustc_span::Span>,
|
||||
attr: &ast::MetaItem,
|
||||
attr_span: Span,
|
||||
cfg_info: &mut CfgInfo,
|
||||
tcx: TyCtxt<'_>,
|
||||
new_value: bool,
|
||||
|
|
@ -719,14 +772,14 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator<Item = &'a hir::Attribute>
|
|||
tcx.sess
|
||||
.dcx()
|
||||
.struct_span_err(
|
||||
vec![*first_change, attr.span],
|
||||
vec![*first_change, attr_span],
|
||||
"`auto_cfg` was disabled and enabled more than once on the same item",
|
||||
)
|
||||
.emit();
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
*changed_auto_active_status = Some(attr.span);
|
||||
*changed_auto_active_status = Some(attr_span);
|
||||
}
|
||||
cfg_info.auto_cfg_active = new_value;
|
||||
false
|
||||
|
|
@ -737,28 +790,21 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator<Item = &'a hir::Attribute>
|
|||
|
||||
let mut doc_cfg = attrs
|
||||
.clone()
|
||||
.filter(|attr| attr.has_name(sym::doc))
|
||||
.flat_map(|attr| attr.meta_item_list().unwrap_or_default())
|
||||
.filter(|attr| attr.has_name(sym::cfg))
|
||||
.filter_map(|attr| match attr {
|
||||
Attribute::Parsed(AttributeKind::Doc(d)) if !d.cfg.is_empty() => Some(d),
|
||||
_ => None,
|
||||
})
|
||||
.peekable();
|
||||
// If the item uses `doc(cfg(...))`, then we ignore the other `cfg(...)` attributes.
|
||||
if doc_cfg.peek().is_some() {
|
||||
let sess = tcx.sess;
|
||||
// We overwrite existing `cfg`.
|
||||
if !cfg_info.parent_is_doc_cfg {
|
||||
cfg_info.current_cfg = Cfg::True;
|
||||
cfg_info.current_cfg = Cfg(CfgEntry::Bool(true, DUMMY_SP));
|
||||
cfg_info.parent_is_doc_cfg = true;
|
||||
}
|
||||
for attr in doc_cfg {
|
||||
if let Some(cfg_mi) =
|
||||
attr.meta_item().and_then(|attr| rustc_expand::config::parse_cfg_old(attr, sess))
|
||||
{
|
||||
match Cfg::parse(cfg_mi) {
|
||||
Ok(new_cfg) => cfg_info.current_cfg &= new_cfg,
|
||||
Err(e) => {
|
||||
sess.dcx().span_err(e.span, e.msg);
|
||||
}
|
||||
}
|
||||
for new_cfg in attr.cfg.clone() {
|
||||
cfg_info.current_cfg &= Cfg(new_cfg);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -769,71 +815,47 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator<Item = &'a hir::Attribute>
|
|||
|
||||
// We get all `doc(auto_cfg)`, `cfg` and `target_feature` attributes.
|
||||
for attr in attrs {
|
||||
if let Some(ident) = attr.ident()
|
||||
&& ident.name == sym::doc
|
||||
&& let Some(attrs) = attr.meta_item_list()
|
||||
{
|
||||
for attr in attrs.iter().filter(|attr| attr.has_name(sym::auto_cfg)) {
|
||||
let MetaItemInner::MetaItem(attr) = attr else {
|
||||
continue;
|
||||
};
|
||||
match &attr.kind {
|
||||
MetaItemKind::Word => {
|
||||
if check_changed_auto_active_status(
|
||||
&mut changed_auto_active_status,
|
||||
attr,
|
||||
cfg_info,
|
||||
tcx,
|
||||
true,
|
||||
) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
MetaItemKind::NameValue(lit) => {
|
||||
if let LitKind::Bool(value) = lit.kind {
|
||||
if check_changed_auto_active_status(
|
||||
&mut changed_auto_active_status,
|
||||
attr,
|
||||
cfg_info,
|
||||
tcx,
|
||||
value,
|
||||
) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
MetaItemKind::List(sub_attrs) => {
|
||||
if check_changed_auto_active_status(
|
||||
&mut changed_auto_active_status,
|
||||
attr,
|
||||
cfg_info,
|
||||
tcx,
|
||||
true,
|
||||
) {
|
||||
return None;
|
||||
}
|
||||
for sub_attr in sub_attrs.iter() {
|
||||
if let Some(ident) = sub_attr.ident()
|
||||
&& (ident.name == sym::show || ident.name == sym::hide)
|
||||
{
|
||||
handle_auto_cfg_hide_show(
|
||||
tcx,
|
||||
cfg_info,
|
||||
&sub_attr,
|
||||
ident.name == sym::show,
|
||||
&mut new_show_attrs,
|
||||
&mut new_hide_attrs,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Attribute::Parsed(AttributeKind::Doc(d)) = attr {
|
||||
for (new_value, span) in &d.auto_cfg_change {
|
||||
if check_changed_auto_active_status(
|
||||
&mut changed_auto_active_status,
|
||||
*span,
|
||||
cfg_info,
|
||||
tcx,
|
||||
*new_value,
|
||||
) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
if let Some((_, span)) = d.auto_cfg.first() {
|
||||
if check_changed_auto_active_status(
|
||||
&mut changed_auto_active_status,
|
||||
*span,
|
||||
cfg_info,
|
||||
tcx,
|
||||
true,
|
||||
) {
|
||||
return None;
|
||||
}
|
||||
for (value, _) in &d.auto_cfg {
|
||||
handle_auto_cfg_hide_show(
|
||||
tcx,
|
||||
cfg_info,
|
||||
value,
|
||||
&mut new_show_attrs,
|
||||
&mut new_hide_attrs,
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if let hir::Attribute::Parsed(AttributeKind::TargetFeature { features, .. }) = attr {
|
||||
// Treat `#[target_feature(enable = "feat")]` attributes as if they were
|
||||
// `#[doc(cfg(target_feature = "feat"))]` attributes as well.
|
||||
for (feature, _) in features {
|
||||
cfg_info.current_cfg &= Cfg::Cfg(sym::target_feature, Some(*feature));
|
||||
cfg_info.current_cfg &= Cfg(CfgEntry::NameValue {
|
||||
name: sym::target_feature,
|
||||
value: Some(*feature),
|
||||
span: DUMMY_SP,
|
||||
});
|
||||
}
|
||||
continue;
|
||||
} else if !cfg_info.parent_is_doc_cfg
|
||||
|
|
@ -851,7 +873,7 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator<Item = &'a hir::Attribute>
|
|||
if !cfg_info.auto_cfg_active && !cfg_info.parent_is_doc_cfg {
|
||||
None
|
||||
} else if cfg_info.parent_is_doc_cfg {
|
||||
if cfg_info.current_cfg == Cfg::True {
|
||||
if matches!(cfg_info.current_cfg.0, CfgEntry::Bool(true, _)) {
|
||||
None
|
||||
} else {
|
||||
Some(Arc::new(cfg_info.current_cfg.clone()))
|
||||
|
|
@ -859,9 +881,9 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator<Item = &'a hir::Attribute>
|
|||
} else {
|
||||
// If `doc(auto_cfg)` feature is enabled, we want to collect all `cfg` items, we remove the
|
||||
// hidden ones afterward.
|
||||
match cfg_info.current_cfg.strip_hidden(&cfg_info.hidden_cfg) {
|
||||
None | Some(Cfg::True) => None,
|
||||
Some(cfg) => Some(Arc::new(cfg)),
|
||||
match strip_hidden(&cfg_info.current_cfg.0, &cfg_info.hidden_cfg) {
|
||||
None | Some(CfgEntry::Bool(true, _)) => None,
|
||||
Some(cfg) => Some(Arc::new(Cfg(cfg))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,23 +1,57 @@
|
|||
use rustc_ast::ast::LitIntType;
|
||||
use rustc_ast::{MetaItemInner, MetaItemLit, Path, Safety, StrStyle};
|
||||
use rustc_data_structures::thin_vec::thin_vec;
|
||||
use rustc_hir::attrs::CfgEntry;
|
||||
use rustc_span::symbol::{Ident, kw};
|
||||
use rustc_span::{DUMMY_SP, create_default_session_globals_then};
|
||||
|
||||
use super::*;
|
||||
|
||||
fn word_cfg(s: &str) -> Cfg {
|
||||
Cfg::Cfg(Symbol::intern(s), None)
|
||||
fn word_cfg(name: &str) -> Cfg {
|
||||
Cfg(word_cfg_e(name))
|
||||
}
|
||||
|
||||
fn word_cfg_e(name: &str) -> CfgEntry {
|
||||
CfgEntry::NameValue { name: Symbol::intern(name), value: None, span: DUMMY_SP }
|
||||
}
|
||||
|
||||
fn name_value_cfg(name: &str, value: &str) -> Cfg {
|
||||
Cfg::Cfg(Symbol::intern(name), Some(Symbol::intern(value)))
|
||||
Cfg(name_value_cfg_e(name, value))
|
||||
}
|
||||
|
||||
fn name_value_cfg_e(name: &str, value: &str) -> CfgEntry {
|
||||
CfgEntry::NameValue {
|
||||
name: Symbol::intern(name),
|
||||
|
||||
value: Some(Symbol::intern(value)),
|
||||
span: DUMMY_SP,
|
||||
}
|
||||
}
|
||||
|
||||
fn dummy_lit(symbol: Symbol, kind: LitKind) -> MetaItemInner {
|
||||
MetaItemInner::Lit(MetaItemLit { symbol, suffix: None, kind, span: DUMMY_SP })
|
||||
}
|
||||
|
||||
fn cfg_all(v: ThinVec<CfgEntry>) -> Cfg {
|
||||
Cfg(cfg_all_e(v))
|
||||
}
|
||||
|
||||
fn cfg_all_e(v: ThinVec<CfgEntry>) -> CfgEntry {
|
||||
CfgEntry::All(v, DUMMY_SP)
|
||||
}
|
||||
|
||||
fn cfg_any(v: ThinVec<CfgEntry>) -> Cfg {
|
||||
Cfg(cfg_any_e(v))
|
||||
}
|
||||
|
||||
fn cfg_any_e(v: ThinVec<CfgEntry>) -> CfgEntry {
|
||||
CfgEntry::Any(v, DUMMY_SP)
|
||||
}
|
||||
|
||||
fn cfg_not(v: CfgEntry) -> Cfg {
|
||||
Cfg(CfgEntry::Not(Box::new(v), DUMMY_SP))
|
||||
}
|
||||
|
||||
fn dummy_meta_item_word(name: &str) -> MetaItemInner {
|
||||
MetaItemInner::MetaItem(MetaItem {
|
||||
unsafety: Safety::Default,
|
||||
|
|
@ -63,40 +97,48 @@ macro_rules! dummy_meta_item_list {
|
|||
};
|
||||
}
|
||||
|
||||
fn cfg_true() -> Cfg {
|
||||
Cfg(CfgEntry::Bool(true, DUMMY_SP))
|
||||
}
|
||||
|
||||
fn cfg_false() -> Cfg {
|
||||
Cfg(CfgEntry::Bool(false, DUMMY_SP))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cfg_not() {
|
||||
create_default_session_globals_then(|| {
|
||||
assert_eq!(!Cfg::False, Cfg::True);
|
||||
assert_eq!(!Cfg::True, Cfg::False);
|
||||
assert_eq!(!word_cfg("test"), Cfg::Not(Box::new(word_cfg("test"))));
|
||||
assert_eq!(!cfg_false(), cfg_true());
|
||||
assert_eq!(!cfg_true(), cfg_false());
|
||||
assert_eq!(!word_cfg("test"), cfg_not(word_cfg_e("test")));
|
||||
assert_eq!(
|
||||
!Cfg::All(vec![word_cfg("a"), word_cfg("b")]),
|
||||
Cfg::Not(Box::new(Cfg::All(vec![word_cfg("a"), word_cfg("b")])))
|
||||
!cfg_all(thin_vec![word_cfg_e("a"), word_cfg_e("b")]),
|
||||
cfg_not(cfg_all_e(thin_vec![word_cfg_e("a"), word_cfg_e("b")]))
|
||||
);
|
||||
assert_eq!(
|
||||
!Cfg::Any(vec![word_cfg("a"), word_cfg("b")]),
|
||||
Cfg::Not(Box::new(Cfg::Any(vec![word_cfg("a"), word_cfg("b")])))
|
||||
!cfg_any(thin_vec![word_cfg_e("a"), word_cfg_e("b")]),
|
||||
cfg_not(cfg_any_e(thin_vec![word_cfg_e("a"), word_cfg_e("b")]))
|
||||
);
|
||||
assert_eq!(!Cfg::Not(Box::new(word_cfg("test"))), word_cfg("test"));
|
||||
assert_eq!(!cfg_not(word_cfg_e("test")), word_cfg("test"));
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cfg_and() {
|
||||
create_default_session_globals_then(|| {
|
||||
let mut x = Cfg::False;
|
||||
x &= Cfg::True;
|
||||
assert_eq!(x, Cfg::False);
|
||||
let mut x = cfg_false();
|
||||
x &= cfg_true();
|
||||
assert_eq!(x, cfg_false());
|
||||
|
||||
x = word_cfg("test");
|
||||
x &= Cfg::False;
|
||||
assert_eq!(x, Cfg::False);
|
||||
x &= cfg_false();
|
||||
assert_eq!(x, cfg_false());
|
||||
|
||||
x = word_cfg("test2");
|
||||
x &= Cfg::True;
|
||||
x &= cfg_true();
|
||||
assert_eq!(x, word_cfg("test2"));
|
||||
|
||||
x = Cfg::True;
|
||||
x = cfg_true();
|
||||
x &= word_cfg("test3");
|
||||
assert_eq!(x, word_cfg("test3"));
|
||||
|
||||
|
|
@ -104,63 +146,69 @@ fn test_cfg_and() {
|
|||
assert_eq!(x, word_cfg("test3"));
|
||||
|
||||
x &= word_cfg("test4");
|
||||
assert_eq!(x, Cfg::All(vec![word_cfg("test3"), word_cfg("test4")]));
|
||||
assert_eq!(x, cfg_all(thin_vec![word_cfg_e("test3"), word_cfg_e("test4")]));
|
||||
|
||||
x &= word_cfg("test4");
|
||||
assert_eq!(x, Cfg::All(vec![word_cfg("test3"), word_cfg("test4")]));
|
||||
assert_eq!(x, cfg_all(thin_vec![word_cfg_e("test3"), word_cfg_e("test4")]));
|
||||
|
||||
x &= word_cfg("test5");
|
||||
assert_eq!(x, Cfg::All(vec![word_cfg("test3"), word_cfg("test4"), word_cfg("test5")]));
|
||||
|
||||
x &= Cfg::All(vec![word_cfg("test6"), word_cfg("test7")]);
|
||||
assert_eq!(
|
||||
x,
|
||||
Cfg::All(vec![
|
||||
word_cfg("test3"),
|
||||
word_cfg("test4"),
|
||||
word_cfg("test5"),
|
||||
word_cfg("test6"),
|
||||
word_cfg("test7"),
|
||||
cfg_all(thin_vec![word_cfg_e("test3"), word_cfg_e("test4"), word_cfg_e("test5")])
|
||||
);
|
||||
|
||||
x &= cfg_all(thin_vec![word_cfg_e("test6"), word_cfg_e("test7")]);
|
||||
assert_eq!(
|
||||
x,
|
||||
cfg_all(thin_vec![
|
||||
word_cfg_e("test3"),
|
||||
word_cfg_e("test4"),
|
||||
word_cfg_e("test5"),
|
||||
word_cfg_e("test6"),
|
||||
word_cfg_e("test7"),
|
||||
])
|
||||
);
|
||||
|
||||
x &= Cfg::All(vec![word_cfg("test6"), word_cfg("test7")]);
|
||||
x &= cfg_all(thin_vec![word_cfg_e("test6"), word_cfg_e("test7")]);
|
||||
assert_eq!(
|
||||
x,
|
||||
Cfg::All(vec![
|
||||
word_cfg("test3"),
|
||||
word_cfg("test4"),
|
||||
word_cfg("test5"),
|
||||
word_cfg("test6"),
|
||||
word_cfg("test7"),
|
||||
cfg_all(thin_vec![
|
||||
word_cfg_e("test3"),
|
||||
word_cfg_e("test4"),
|
||||
word_cfg_e("test5"),
|
||||
word_cfg_e("test6"),
|
||||
word_cfg_e("test7"),
|
||||
])
|
||||
);
|
||||
|
||||
let mut y = Cfg::Any(vec![word_cfg("a"), word_cfg("b")]);
|
||||
let mut y = cfg_any(thin_vec![word_cfg_e("a"), word_cfg_e("b")]);
|
||||
y &= x;
|
||||
assert_eq!(
|
||||
y,
|
||||
Cfg::All(vec![
|
||||
word_cfg("test3"),
|
||||
word_cfg("test4"),
|
||||
word_cfg("test5"),
|
||||
word_cfg("test6"),
|
||||
word_cfg("test7"),
|
||||
Cfg::Any(vec![word_cfg("a"), word_cfg("b")]),
|
||||
cfg_all(thin_vec![
|
||||
word_cfg_e("test3"),
|
||||
word_cfg_e("test4"),
|
||||
word_cfg_e("test5"),
|
||||
word_cfg_e("test6"),
|
||||
word_cfg_e("test7"),
|
||||
cfg_any_e(thin_vec![word_cfg_e("a"), word_cfg_e("b")]),
|
||||
])
|
||||
);
|
||||
|
||||
let mut z = word_cfg("test8");
|
||||
z &= Cfg::All(vec![word_cfg("test9"), word_cfg("test10")]);
|
||||
assert_eq!(z, Cfg::All(vec![word_cfg("test9"), word_cfg("test10"), word_cfg("test8")]));
|
||||
z &= cfg_all(thin_vec![word_cfg_e("test9"), word_cfg_e("test10")]);
|
||||
assert_eq!(
|
||||
z,
|
||||
cfg_all(thin_vec![word_cfg_e("test9"), word_cfg_e("test10"), word_cfg_e("test8"),]),
|
||||
);
|
||||
|
||||
let mut z = word_cfg("test11");
|
||||
z &= Cfg::All(vec![word_cfg("test11"), word_cfg("test12")]);
|
||||
assert_eq!(z, Cfg::All(vec![word_cfg("test11"), word_cfg("test12")]));
|
||||
z &= cfg_all(thin_vec![word_cfg_e("test11"), word_cfg_e("test12")]);
|
||||
assert_eq!(z, cfg_all(thin_vec![word_cfg_e("test11"), word_cfg_e("test12")]));
|
||||
|
||||
assert_eq!(
|
||||
word_cfg("a") & word_cfg("b") & word_cfg("c"),
|
||||
Cfg::All(vec![word_cfg("a"), word_cfg("b"), word_cfg("c")])
|
||||
cfg_all(thin_vec![word_cfg_e("a"), word_cfg_e("b"), word_cfg_e("c")])
|
||||
);
|
||||
})
|
||||
}
|
||||
|
|
@ -168,19 +216,19 @@ fn test_cfg_and() {
|
|||
#[test]
|
||||
fn test_cfg_or() {
|
||||
create_default_session_globals_then(|| {
|
||||
let mut x = Cfg::True;
|
||||
x |= Cfg::False;
|
||||
assert_eq!(x, Cfg::True);
|
||||
let mut x = cfg_true();
|
||||
x |= cfg_false();
|
||||
assert_eq!(x, cfg_true());
|
||||
|
||||
x = word_cfg("test");
|
||||
x |= Cfg::True;
|
||||
x |= cfg_true();
|
||||
assert_eq!(x, word_cfg("test"));
|
||||
|
||||
x = word_cfg("test2");
|
||||
x |= Cfg::False;
|
||||
x |= cfg_false();
|
||||
assert_eq!(x, word_cfg("test2"));
|
||||
|
||||
x = Cfg::False;
|
||||
x = cfg_false();
|
||||
x |= word_cfg("test3");
|
||||
assert_eq!(x, word_cfg("test3"));
|
||||
|
||||
|
|
@ -188,63 +236,69 @@ fn test_cfg_or() {
|
|||
assert_eq!(x, word_cfg("test3"));
|
||||
|
||||
x |= word_cfg("test4");
|
||||
assert_eq!(x, Cfg::Any(vec![word_cfg("test3"), word_cfg("test4")]));
|
||||
assert_eq!(x, cfg_any(thin_vec![word_cfg_e("test3"), word_cfg_e("test4")]));
|
||||
|
||||
x |= word_cfg("test4");
|
||||
assert_eq!(x, Cfg::Any(vec![word_cfg("test3"), word_cfg("test4")]));
|
||||
assert_eq!(x, cfg_any(thin_vec![word_cfg_e("test3"), word_cfg_e("test4")]));
|
||||
|
||||
x |= word_cfg("test5");
|
||||
assert_eq!(x, Cfg::Any(vec![word_cfg("test3"), word_cfg("test4"), word_cfg("test5")]));
|
||||
|
||||
x |= Cfg::Any(vec![word_cfg("test6"), word_cfg("test7")]);
|
||||
assert_eq!(
|
||||
x,
|
||||
Cfg::Any(vec![
|
||||
word_cfg("test3"),
|
||||
word_cfg("test4"),
|
||||
word_cfg("test5"),
|
||||
word_cfg("test6"),
|
||||
word_cfg("test7"),
|
||||
cfg_any(thin_vec![word_cfg_e("test3"), word_cfg_e("test4"), word_cfg_e("test5")])
|
||||
);
|
||||
|
||||
x |= cfg_any(thin_vec![word_cfg_e("test6"), word_cfg_e("test7")]);
|
||||
assert_eq!(
|
||||
x,
|
||||
cfg_any(thin_vec![
|
||||
word_cfg_e("test3"),
|
||||
word_cfg_e("test4"),
|
||||
word_cfg_e("test5"),
|
||||
word_cfg_e("test6"),
|
||||
word_cfg_e("test7"),
|
||||
])
|
||||
);
|
||||
|
||||
x |= Cfg::Any(vec![word_cfg("test6"), word_cfg("test7")]);
|
||||
x |= cfg_any(thin_vec![word_cfg_e("test6"), word_cfg_e("test7")]);
|
||||
assert_eq!(
|
||||
x,
|
||||
Cfg::Any(vec![
|
||||
word_cfg("test3"),
|
||||
word_cfg("test4"),
|
||||
word_cfg("test5"),
|
||||
word_cfg("test6"),
|
||||
word_cfg("test7"),
|
||||
cfg_any(thin_vec![
|
||||
word_cfg_e("test3"),
|
||||
word_cfg_e("test4"),
|
||||
word_cfg_e("test5"),
|
||||
word_cfg_e("test6"),
|
||||
word_cfg_e("test7"),
|
||||
])
|
||||
);
|
||||
|
||||
let mut y = Cfg::All(vec![word_cfg("a"), word_cfg("b")]);
|
||||
let mut y = cfg_all(thin_vec![word_cfg_e("a"), word_cfg_e("b")]);
|
||||
y |= x;
|
||||
assert_eq!(
|
||||
y,
|
||||
Cfg::Any(vec![
|
||||
word_cfg("test3"),
|
||||
word_cfg("test4"),
|
||||
word_cfg("test5"),
|
||||
word_cfg("test6"),
|
||||
word_cfg("test7"),
|
||||
Cfg::All(vec![word_cfg("a"), word_cfg("b")]),
|
||||
cfg_any(thin_vec![
|
||||
word_cfg_e("test3"),
|
||||
word_cfg_e("test4"),
|
||||
word_cfg_e("test5"),
|
||||
word_cfg_e("test6"),
|
||||
word_cfg_e("test7"),
|
||||
cfg_all_e(thin_vec![word_cfg_e("a"), word_cfg_e("b")]),
|
||||
])
|
||||
);
|
||||
|
||||
let mut z = word_cfg("test8");
|
||||
z |= Cfg::Any(vec![word_cfg("test9"), word_cfg("test10")]);
|
||||
assert_eq!(z, Cfg::Any(vec![word_cfg("test9"), word_cfg("test10"), word_cfg("test8")]));
|
||||
z |= cfg_any(thin_vec![word_cfg_e("test9"), word_cfg_e("test10")]);
|
||||
assert_eq!(
|
||||
z,
|
||||
cfg_any(thin_vec![word_cfg_e("test9"), word_cfg_e("test10"), word_cfg_e("test8")])
|
||||
);
|
||||
|
||||
let mut z = word_cfg("test11");
|
||||
z |= Cfg::Any(vec![word_cfg("test11"), word_cfg("test12")]);
|
||||
assert_eq!(z, Cfg::Any(vec![word_cfg("test11"), word_cfg("test12")]));
|
||||
z |= cfg_any(thin_vec![word_cfg_e("test11"), word_cfg_e("test12")]);
|
||||
assert_eq!(z, cfg_any(thin_vec![word_cfg_e("test11"), word_cfg_e("test12")]));
|
||||
|
||||
assert_eq!(
|
||||
word_cfg("a") | word_cfg("b") | word_cfg("c"),
|
||||
Cfg::Any(vec![word_cfg("a"), word_cfg("b"), word_cfg("c")])
|
||||
cfg_any(thin_vec![word_cfg_e("a"), word_cfg_e("b"), word_cfg_e("c")])
|
||||
);
|
||||
})
|
||||
}
|
||||
|
|
@ -254,11 +308,11 @@ fn test_parse_ok() {
|
|||
create_default_session_globals_then(|| {
|
||||
let r#true = Symbol::intern("true");
|
||||
let mi = dummy_lit(r#true, LitKind::Bool(true));
|
||||
assert_eq!(Cfg::parse(&mi), Ok(Cfg::True));
|
||||
assert_eq!(Cfg::parse(&mi), Ok(cfg_true()));
|
||||
|
||||
let r#false = Symbol::intern("false");
|
||||
let mi = dummy_lit(r#false, LitKind::Bool(false));
|
||||
assert_eq!(Cfg::parse(&mi), Ok(Cfg::False));
|
||||
assert_eq!(Cfg::parse(&mi), Ok(cfg_false()));
|
||||
|
||||
let mi = dummy_meta_item_word("all");
|
||||
assert_eq!(Cfg::parse(&mi), Ok(word_cfg("all")));
|
||||
|
|
@ -464,33 +518,36 @@ fn test_simplify_with() {
|
|||
// This is a tiny subset of things that could be simplified, but it likely covers 90% of
|
||||
// real world usecases well.
|
||||
create_default_session_globals_then(|| {
|
||||
let foo = word_cfg("foo");
|
||||
let bar = word_cfg("bar");
|
||||
let baz = word_cfg("baz");
|
||||
let quux = word_cfg("quux");
|
||||
let foo = word_cfg_e("foo");
|
||||
let bar = word_cfg_e("bar");
|
||||
let baz = word_cfg_e("baz");
|
||||
let quux = word_cfg_e("quux");
|
||||
|
||||
let foobar = Cfg::All(vec![foo.clone(), bar.clone()]);
|
||||
let barbaz = Cfg::All(vec![bar.clone(), baz.clone()]);
|
||||
let foobarbaz = Cfg::All(vec![foo.clone(), bar.clone(), baz.clone()]);
|
||||
let bazquux = Cfg::All(vec![baz.clone(), quux.clone()]);
|
||||
let foobar = cfg_all(thin_vec![foo.clone(), bar.clone()]);
|
||||
let barbaz = cfg_all(thin_vec![bar.clone(), baz.clone()]);
|
||||
let foobarbaz = cfg_all(thin_vec![foo.clone(), bar.clone(), baz.clone()]);
|
||||
let bazquux = cfg_all(thin_vec![baz.clone(), quux.clone()]);
|
||||
|
||||
// Unrelated cfgs don't affect each other
|
||||
assert_eq!(foo.simplify_with(&bar).as_ref(), Some(&foo));
|
||||
assert_eq!(
|
||||
Cfg(foo.clone()).simplify_with(&Cfg(bar.clone())).as_ref(),
|
||||
Some(&Cfg(foo.clone()))
|
||||
);
|
||||
assert_eq!(foobar.simplify_with(&bazquux).as_ref(), Some(&foobar));
|
||||
|
||||
// Identical cfgs are eliminated
|
||||
assert_eq!(foo.simplify_with(&foo), None);
|
||||
assert_eq!(Cfg(foo.clone()).simplify_with(&Cfg(foo.clone())), None);
|
||||
assert_eq!(foobar.simplify_with(&foobar), None);
|
||||
|
||||
// Multiple cfgs eliminate a single assumed cfg
|
||||
assert_eq!(foobar.simplify_with(&foo).as_ref(), Some(&bar));
|
||||
assert_eq!(foobar.simplify_with(&bar).as_ref(), Some(&foo));
|
||||
assert_eq!(foobar.simplify_with(&Cfg(foo.clone())).as_ref(), Some(&Cfg(bar.clone())));
|
||||
assert_eq!(foobar.simplify_with(&Cfg(bar)).as_ref(), Some(&Cfg(foo.clone())));
|
||||
|
||||
// A single cfg is eliminated by multiple assumed cfg containing it
|
||||
assert_eq!(foo.simplify_with(&foobar), None);
|
||||
assert_eq!(Cfg(foo.clone()).simplify_with(&foobar), None);
|
||||
|
||||
// Multiple cfgs eliminate the matching subset of multiple assumed cfg
|
||||
assert_eq!(foobar.simplify_with(&barbaz).as_ref(), Some(&foo));
|
||||
assert_eq!(foobar.simplify_with(&barbaz).as_ref(), Some(&Cfg(foo)));
|
||||
assert_eq!(foobar.simplify_with(&foobarbaz), None);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -640,7 +640,7 @@ pub(crate) fn build_impl(
|
|||
for_,
|
||||
items: trait_items,
|
||||
polarity,
|
||||
kind: if utils::has_doc_flag(tcx, did, sym::fake_variadic) {
|
||||
kind: if utils::has_doc_flag(tcx, did, |d| d.fake_variadic.is_some()) {
|
||||
ImplKind::FakeVariadic
|
||||
} else {
|
||||
ImplKind::Normal
|
||||
|
|
|
|||
|
|
@ -34,13 +34,11 @@ use std::borrow::Cow;
|
|||
use std::collections::BTreeMap;
|
||||
use std::mem;
|
||||
|
||||
use rustc_ast::token::{Token, TokenKind};
|
||||
use rustc_ast::tokenstream::{TokenStream, TokenTree};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet, IndexEntry};
|
||||
use rustc_data_structures::thin_vec::ThinVec;
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_errors::{FatalError, struct_span_code_err};
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_hir::attrs::{AttributeKind, DocAttribute, DocInline};
|
||||
use rustc_hir::def::{CtorKind, DefKind, MacroKinds, Res};
|
||||
use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LOCAL_CRATE, LocalDefId};
|
||||
use rustc_hir::{LangItem, PredicateOrigin, find_attr};
|
||||
|
|
@ -198,12 +196,12 @@ fn generate_item_with_correct_attrs(
|
|||
// For glob re-exports the item may or may not exist to be re-exported (potentially the
|
||||
// cfgs on the path up until the glob can be removed, and only cfgs on the globbed item
|
||||
// itself matter), for non-inlined re-exports see #85043.
|
||||
let import_is_inline =
|
||||
hir_attr_lists(inline::load_attrs(cx, import_id.to_def_id()), sym::doc)
|
||||
.get_word_attr(sym::inline)
|
||||
.is_some()
|
||||
|| (is_glob_import(cx.tcx, import_id)
|
||||
&& (cx.document_hidden() || !cx.tcx.is_doc_hidden(def_id)));
|
||||
let import_is_inline = find_attr!(
|
||||
inline::load_attrs(cx, import_id.to_def_id()),
|
||||
AttributeKind::Doc(d)
|
||||
if d.inline.first().is_some_and(|(inline, _)| *inline == DocInline::Inline)
|
||||
) || (is_glob_import(cx.tcx, import_id)
|
||||
&& (cx.document_hidden() || !cx.tcx.is_doc_hidden(def_id)));
|
||||
attrs.extend(get_all_import_attributes(cx, import_id, def_id, is_inline));
|
||||
is_inline = is_inline || import_is_inline;
|
||||
}
|
||||
|
|
@ -2635,63 +2633,6 @@ fn get_all_import_attributes<'hir>(
|
|||
attrs
|
||||
}
|
||||
|
||||
fn filter_tokens_from_list(
|
||||
args_tokens: &TokenStream,
|
||||
should_retain: impl Fn(&TokenTree) -> bool,
|
||||
) -> Vec<TokenTree> {
|
||||
let mut tokens = Vec::with_capacity(args_tokens.len());
|
||||
let mut skip_next_comma = false;
|
||||
for token in args_tokens.iter() {
|
||||
match token {
|
||||
TokenTree::Token(Token { kind: TokenKind::Comma, .. }, _) if skip_next_comma => {
|
||||
skip_next_comma = false;
|
||||
}
|
||||
token if should_retain(token) => {
|
||||
skip_next_comma = false;
|
||||
tokens.push(token.clone());
|
||||
}
|
||||
_ => {
|
||||
skip_next_comma = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
tokens
|
||||
}
|
||||
|
||||
fn filter_doc_attr_ident(ident: Symbol, is_inline: bool) -> bool {
|
||||
if is_inline {
|
||||
ident == sym::hidden || ident == sym::inline || ident == sym::no_inline
|
||||
} else {
|
||||
ident == sym::cfg
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove attributes from `normal` that should not be inherited by `use` re-export.
|
||||
/// Before calling this function, make sure `normal` is a `#[doc]` attribute.
|
||||
fn filter_doc_attr(args: &mut hir::AttrArgs, is_inline: bool) {
|
||||
match args {
|
||||
hir::AttrArgs::Delimited(args) => {
|
||||
let tokens = filter_tokens_from_list(&args.tokens, |token| {
|
||||
!matches!(
|
||||
token,
|
||||
TokenTree::Token(
|
||||
Token {
|
||||
kind: TokenKind::Ident(
|
||||
ident,
|
||||
_,
|
||||
),
|
||||
..
|
||||
},
|
||||
_,
|
||||
) if filter_doc_attr_ident(*ident, is_inline),
|
||||
)
|
||||
});
|
||||
args.tokens = TokenStream::new(tokens);
|
||||
}
|
||||
hir::AttrArgs::Empty | hir::AttrArgs::Eq { .. } => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// When inlining items, we merge their attributes (and all the reexports attributes too) with the
|
||||
/// final reexport. For example:
|
||||
///
|
||||
|
|
@ -2719,25 +2660,34 @@ fn add_without_unwanted_attributes<'hir>(
|
|||
import_parent: Option<DefId>,
|
||||
) {
|
||||
for attr in new_attrs {
|
||||
if attr.is_doc_comment().is_some() {
|
||||
attrs.push((Cow::Borrowed(attr), import_parent));
|
||||
continue;
|
||||
}
|
||||
let mut attr = attr.clone();
|
||||
match attr {
|
||||
hir::Attribute::Unparsed(ref mut normal) if let [ident] = &*normal.path.segments => {
|
||||
let ident = ident.name;
|
||||
if ident == sym::doc {
|
||||
filter_doc_attr(&mut normal.args, is_inline);
|
||||
attrs.push((Cow::Owned(attr), import_parent));
|
||||
} else if is_inline || ident != sym::cfg_trace {
|
||||
hir::Attribute::Parsed(AttributeKind::DocComment { .. }) => {
|
||||
attrs.push((Cow::Borrowed(attr), import_parent));
|
||||
}
|
||||
hir::Attribute::Parsed(AttributeKind::Doc(box d)) => {
|
||||
// Remove attributes from `normal` that should not be inherited by `use` re-export.
|
||||
let DocAttribute { hidden, inline, cfg, .. } = d;
|
||||
let mut attr = DocAttribute::default();
|
||||
if is_inline {
|
||||
attr.cfg = cfg.clone();
|
||||
} else {
|
||||
attr.inline = inline.clone();
|
||||
attr.hidden = hidden.clone();
|
||||
}
|
||||
attrs.push((
|
||||
Cow::Owned(hir::Attribute::Parsed(AttributeKind::Doc(Box::new(attr)))),
|
||||
import_parent,
|
||||
));
|
||||
}
|
||||
hir::Attribute::Unparsed(normal) if let [ident] = &*normal.path.segments => {
|
||||
if is_inline || ident.name != sym::cfg_trace {
|
||||
// If it's not a `cfg()` attribute, we keep it.
|
||||
attrs.push((Cow::Owned(attr), import_parent));
|
||||
attrs.push((Cow::Borrowed(attr), import_parent));
|
||||
}
|
||||
}
|
||||
// FIXME: make sure to exclude `#[cfg_trace]` here when it is ported to the new parsers
|
||||
hir::Attribute::Parsed(..) => {
|
||||
attrs.push((Cow::Owned(attr), import_parent));
|
||||
attrs.push((Cow::Borrowed(attr), import_parent));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
@ -2939,7 +2889,7 @@ fn clean_impl<'tcx>(
|
|||
} else {
|
||||
ty::ImplPolarity::Positive
|
||||
},
|
||||
kind: if utils::has_doc_flag(tcx, def_id.to_def_id(), sym::fake_variadic) {
|
||||
kind: if utils::has_doc_flag(tcx, def_id.to_def_id(), |d| d.fake_variadic.is_some()) {
|
||||
ImplKind::FakeVariadic
|
||||
} else {
|
||||
ImplKind::Normal
|
||||
|
|
@ -2968,11 +2918,10 @@ fn clean_extern_crate<'tcx>(
|
|||
let ty_vis = cx.tcx.visibility(krate.owner_id);
|
||||
let please_inline = ty_vis.is_public()
|
||||
&& attrs.iter().any(|a| {
|
||||
a.has_name(sym::doc)
|
||||
&& match a.meta_item_list() {
|
||||
Some(l) => ast::attr::list_contains_name(&l, sym::inline),
|
||||
None => false,
|
||||
}
|
||||
matches!(
|
||||
a,
|
||||
hir::Attribute::Parsed(AttributeKind::Doc(d))
|
||||
if d.inline.first().is_some_and(|(i, _)| *i == DocInline::Inline))
|
||||
})
|
||||
&& !cx.is_json_output();
|
||||
|
||||
|
|
@ -3035,7 +2984,11 @@ fn clean_use_statement_inner<'tcx>(
|
|||
|
||||
let visibility = cx.tcx.visibility(import.owner_id);
|
||||
let attrs = cx.tcx.hir_attrs(import.hir_id());
|
||||
let inline_attr = hir_attr_lists(attrs, sym::doc).get_word_attr(sym::inline);
|
||||
let inline_attr = find_attr!(
|
||||
attrs,
|
||||
AttributeKind::Doc(d) if d.inline.first().is_some_and(|(i, _)| *i == DocInline::Inline) => d
|
||||
)
|
||||
.and_then(|d| d.inline.first());
|
||||
let pub_underscore = visibility.is_public() && name == Some(kw::Underscore);
|
||||
let current_mod = cx.tcx.parent_module_from_def_id(import.owner_id.def_id);
|
||||
let import_def_id = import.owner_id.def_id;
|
||||
|
|
@ -3053,10 +3006,10 @@ fn clean_use_statement_inner<'tcx>(
|
|||
let is_visible_from_parent_mod =
|
||||
visibility.is_accessible_from(parent_mod, cx.tcx) && !current_mod.is_top_level_module();
|
||||
|
||||
if pub_underscore && let Some(ref inline) = inline_attr {
|
||||
if pub_underscore && let Some((_, inline_span)) = inline_attr {
|
||||
struct_span_code_err!(
|
||||
cx.tcx.dcx(),
|
||||
inline.span(),
|
||||
*inline_span,
|
||||
E0780,
|
||||
"anonymous imports cannot be inlined"
|
||||
)
|
||||
|
|
@ -3071,16 +3024,11 @@ fn clean_use_statement_inner<'tcx>(
|
|||
let mut denied = cx.is_json_output()
|
||||
|| !(visibility.is_public() || (cx.document_private() && is_visible_from_parent_mod))
|
||||
|| pub_underscore
|
||||
|| attrs.iter().any(|a| {
|
||||
a.has_name(sym::doc)
|
||||
&& match a.meta_item_list() {
|
||||
Some(l) => {
|
||||
ast::attr::list_contains_name(&l, sym::no_inline)
|
||||
|| ast::attr::list_contains_name(&l, sym::hidden)
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
});
|
||||
|| attrs.iter().any(|a| matches!(
|
||||
a,
|
||||
hir::Attribute::Parsed(AttributeKind::Doc(d))
|
||||
if d.hidden.is_some() || d.inline.first().is_some_and(|(i, _)| *i == DocInline::NoInline)
|
||||
));
|
||||
|
||||
// Also check whether imports were asked to be inlined, in case we're trying to re-export a
|
||||
// crate in Rust 2018+
|
||||
|
|
|
|||
|
|
@ -9,11 +9,11 @@ use itertools::Either;
|
|||
use rustc_abi::{ExternAbi, VariantIdx};
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
|
||||
use rustc_data_structures::thin_vec::ThinVec;
|
||||
use rustc_hir::attrs::{AttributeKind, DeprecatedSince, Deprecation};
|
||||
use rustc_hir::attrs::{AttributeKind, DeprecatedSince, Deprecation, DocAttribute};
|
||||
use rustc_hir::def::{CtorKind, DefKind, Res};
|
||||
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId};
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_hir::{BodyId, ConstStability, Mutability, Stability, StableSince, find_attr};
|
||||
use rustc_hir::{Attribute, BodyId, ConstStability, Mutability, Stability, StableSince, find_attr};
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_metadata::rendered_const;
|
||||
use rustc_middle::span_bug;
|
||||
|
|
@ -190,12 +190,13 @@ impl ExternalCrate {
|
|||
// Failing that, see if there's an attribute specifying where to find this
|
||||
// external crate
|
||||
let did = self.crate_num.as_def_id();
|
||||
tcx.get_attrs(did, sym::doc)
|
||||
.flat_map(|attr| attr.meta_item_list().unwrap_or_default())
|
||||
.filter(|a| a.has_name(sym::html_root_url))
|
||||
.filter_map(|a| a.value_str())
|
||||
tcx.get_all_attrs(did)
|
||||
.iter()
|
||||
.find_map(|a| match a {
|
||||
Attribute::Parsed(AttributeKind::Doc(d)) => d.html_root_url.map(|(url, _)| url),
|
||||
_ => None,
|
||||
})
|
||||
.map(to_remote)
|
||||
.next()
|
||||
.or_else(|| extern_url.map(to_remote)) // NOTE: only matters if `extern_url_takes_precedence` is false
|
||||
.unwrap_or(Unknown) // Well, at least we tried.
|
||||
}
|
||||
|
|
@ -228,25 +229,27 @@ impl ExternalCrate {
|
|||
}
|
||||
|
||||
pub(crate) fn keywords(&self, tcx: TyCtxt<'_>) -> impl Iterator<Item = (DefId, Symbol)> {
|
||||
self.retrieve_keywords_or_documented_attributes(tcx, sym::keyword)
|
||||
self.retrieve_keywords_or_documented_attributes(tcx, |d| d.keyword.map(|(v, _)| v))
|
||||
}
|
||||
pub(crate) fn documented_attributes(
|
||||
&self,
|
||||
tcx: TyCtxt<'_>,
|
||||
) -> impl Iterator<Item = (DefId, Symbol)> {
|
||||
self.retrieve_keywords_or_documented_attributes(tcx, sym::attribute)
|
||||
self.retrieve_keywords_or_documented_attributes(tcx, |d| d.attribute.map(|(v, _)| v))
|
||||
}
|
||||
|
||||
fn retrieve_keywords_or_documented_attributes(
|
||||
fn retrieve_keywords_or_documented_attributes<F: Fn(&DocAttribute) -> Option<Symbol>>(
|
||||
&self,
|
||||
tcx: TyCtxt<'_>,
|
||||
name: Symbol,
|
||||
callback: F,
|
||||
) -> impl Iterator<Item = (DefId, Symbol)> {
|
||||
let as_target = move |did: DefId, tcx: TyCtxt<'_>| -> Option<(DefId, Symbol)> {
|
||||
tcx.get_attrs(did, sym::doc)
|
||||
.flat_map(|attr| attr.meta_item_list().unwrap_or_default())
|
||||
.filter(|meta| meta.has_name(name))
|
||||
.find_map(|meta| meta.value_str())
|
||||
tcx.get_all_attrs(did)
|
||||
.iter()
|
||||
.find_map(|attr| match attr {
|
||||
Attribute::Parsed(AttributeKind::Doc(d)) => callback(d),
|
||||
_ => None,
|
||||
})
|
||||
.map(|value| (did, value))
|
||||
};
|
||||
self.mapped_root_modules(tcx, as_target)
|
||||
|
|
@ -920,37 +923,6 @@ pub(crate) struct Module {
|
|||
pub(crate) span: Span,
|
||||
}
|
||||
|
||||
pub(crate) fn hir_attr_lists<'a, I: IntoIterator<Item = &'a hir::Attribute>>(
|
||||
attrs: I,
|
||||
name: Symbol,
|
||||
) -> impl Iterator<Item = ast::MetaItemInner> + use<'a, I> {
|
||||
attrs
|
||||
.into_iter()
|
||||
.filter(move |attr| attr.has_name(name))
|
||||
.filter_map(ast::attr::AttributeExt::meta_item_list)
|
||||
.flatten()
|
||||
}
|
||||
|
||||
pub(crate) trait NestedAttributesExt {
|
||||
/// Returns `true` if the attribute list contains a specific `word`
|
||||
fn has_word(self, word: Symbol) -> bool
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
<Self as NestedAttributesExt>::get_word_attr(self, word).is_some()
|
||||
}
|
||||
|
||||
/// Returns `Some(attr)` if the attribute list contains 'attr'
|
||||
/// corresponding to a specific `word`
|
||||
fn get_word_attr(self, word: Symbol) -> Option<ast::MetaItemInner>;
|
||||
}
|
||||
|
||||
impl<I: Iterator<Item = ast::MetaItemInner>> NestedAttributesExt for I {
|
||||
fn get_word_attr(mut self, word: Symbol) -> Option<ast::MetaItemInner> {
|
||||
self.find(|attr| attr.is_word() && attr.has_name(word))
|
||||
}
|
||||
}
|
||||
|
||||
/// A link that has not yet been rendered.
|
||||
///
|
||||
/// This link will be turned into a rendered link by [`Item::links`].
|
||||
|
|
@ -993,28 +965,14 @@ pub(crate) struct Attributes {
|
|||
}
|
||||
|
||||
impl Attributes {
|
||||
pub(crate) fn lists(&self, name: Symbol) -> impl Iterator<Item = ast::MetaItemInner> {
|
||||
hir_attr_lists(&self.other_attrs[..], name)
|
||||
}
|
||||
|
||||
pub(crate) fn has_doc_flag(&self, flag: Symbol) -> bool {
|
||||
for attr in &self.other_attrs {
|
||||
if !attr.has_name(sym::doc) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(items) = attr.meta_item_list()
|
||||
&& items.iter().filter_map(|i| i.meta_item()).any(|it| it.has_name(flag))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
pub(crate) fn has_doc_flag<F: Fn(&DocAttribute) -> bool>(&self, callback: F) -> bool {
|
||||
self.other_attrs
|
||||
.iter()
|
||||
.any(|a| matches!(a, Attribute::Parsed(AttributeKind::Doc(d)) if callback(d)))
|
||||
}
|
||||
|
||||
pub(crate) fn is_doc_hidden(&self) -> bool {
|
||||
self.has_doc_flag(sym::hidden)
|
||||
find_attr!(&self.other_attrs, AttributeKind::Doc(d) if d.hidden.is_some())
|
||||
}
|
||||
|
||||
pub(crate) fn from_hir(attrs: &[hir::Attribute]) -> Attributes {
|
||||
|
|
@ -1061,19 +1019,11 @@ impl Attributes {
|
|||
pub(crate) fn get_doc_aliases(&self) -> Box<[Symbol]> {
|
||||
let mut aliases = FxIndexSet::default();
|
||||
|
||||
for attr in
|
||||
hir_attr_lists(&self.other_attrs[..], sym::doc).filter(|a| a.has_name(sym::alias))
|
||||
{
|
||||
if let Some(values) = attr.meta_item_list() {
|
||||
for l in values {
|
||||
if let Some(lit) = l.lit()
|
||||
&& let ast::LitKind::Str(s, _) = lit.kind
|
||||
{
|
||||
aliases.insert(s);
|
||||
}
|
||||
for attr in &self.other_attrs {
|
||||
if let Attribute::Parsed(AttributeKind::Doc(d)) = attr {
|
||||
for (alias, _) in &d.aliases {
|
||||
aliases.insert(*alias);
|
||||
}
|
||||
} else if let Some(value) = attr.value_str() {
|
||||
aliases.insert(value);
|
||||
}
|
||||
}
|
||||
aliases.into_iter().collect::<Vec<_>>().into()
|
||||
|
|
@ -2416,7 +2366,7 @@ mod size_asserts {
|
|||
use super::*;
|
||||
// tidy-alphabetical-start
|
||||
static_assert_size!(Crate, 16); // frequently moved by-value
|
||||
static_assert_size!(DocFragment, 32);
|
||||
static_assert_size!(DocFragment, 48);
|
||||
static_assert_size!(GenericArg, 32);
|
||||
static_assert_size!(GenericArgs, 24);
|
||||
static_assert_size!(GenericParamDef, 40);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use rustc_resolve::rustdoc::{DocFragmentKind, unindent_doc_fragments};
|
||||
use rustc_ast::token::{CommentKind, DocFragmentKind};
|
||||
use rustc_resolve::rustdoc::unindent_doc_fragments;
|
||||
use rustc_span::create_default_session_globals_then;
|
||||
|
||||
use super::*;
|
||||
|
|
@ -8,7 +9,7 @@ fn create_doc_fragment(s: &str) -> Vec<DocFragment> {
|
|||
span: DUMMY_SP,
|
||||
item_id: None,
|
||||
doc: Symbol::intern(s),
|
||||
kind: DocFragmentKind::SugaredDoc,
|
||||
kind: DocFragmentKind::Sugared(CommentKind::Line),
|
||||
indent: 0,
|
||||
from_expansion: false,
|
||||
}]
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue