Auto merge of #150700 - Kobzol:rollup-02y1lh9, r=Kobzol
Rollup of 3 pull requests Successful merges: - rust-lang/rust#149790 (Remove `Span` from segments of `AttrPath`) - rust-lang/rust#150384 (Tidying up tests/ui/issues 16 tests [7/N]) - rust-lang/rust#150697 (relate.rs: tiny cleanup: eliminate temp vars 2) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
4fa80a5e73
56 changed files with 218 additions and 224 deletions
|
|
@ -96,11 +96,11 @@ impl AttributeExt for Attribute {
|
|||
}
|
||||
|
||||
/// For a single-segment attribute, returns its name; otherwise, returns `None`.
|
||||
fn ident(&self) -> Option<Ident> {
|
||||
fn name(&self) -> Option<Symbol> {
|
||||
match &self.kind {
|
||||
AttrKind::Normal(normal) => {
|
||||
if let [ident] = &*normal.item.path.segments {
|
||||
Some(ident.ident)
|
||||
Some(ident.ident.name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
@ -109,9 +109,18 @@ impl AttributeExt for Attribute {
|
|||
}
|
||||
}
|
||||
|
||||
fn ident_path(&self) -> Option<SmallVec<[Ident; 1]>> {
|
||||
fn symbol_path(&self) -> Option<SmallVec<[Symbol; 1]>> {
|
||||
match &self.kind {
|
||||
AttrKind::Normal(p) => Some(p.item.path.segments.iter().map(|i| i.ident).collect()),
|
||||
AttrKind::Normal(p) => {
|
||||
Some(p.item.path.segments.iter().map(|i| i.ident.name).collect())
|
||||
}
|
||||
AttrKind::DocComment(_, _) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn path_span(&self) -> Option<Span> {
|
||||
match &self.kind {
|
||||
AttrKind::Normal(attr) => Some(attr.item.path.span),
|
||||
AttrKind::DocComment(_, _) => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -794,9 +803,7 @@ pub trait AttributeExt: Debug {
|
|||
|
||||
/// For a single-segment attribute (i.e., `#[attr]` and not `#[path::atrr]`),
|
||||
/// return the name of the attribute; otherwise, returns `None`.
|
||||
fn name(&self) -> Option<Symbol> {
|
||||
self.ident().map(|ident| ident.name)
|
||||
}
|
||||
fn name(&self) -> Option<Symbol>;
|
||||
|
||||
/// Get the meta item list, `#[attr(meta item list)]`
|
||||
fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>>;
|
||||
|
|
@ -807,9 +814,6 @@ pub trait AttributeExt: Debug {
|
|||
/// Gets the span of the value literal, as string, when using `#[attr = value]`
|
||||
fn value_span(&self) -> Option<Span>;
|
||||
|
||||
/// For a single-segment attribute, returns its ident; otherwise, returns `None`.
|
||||
fn ident(&self) -> Option<Ident>;
|
||||
|
||||
/// Checks whether the path of this attribute matches the name.
|
||||
///
|
||||
/// Matches one segment of the path to each element in `name`
|
||||
|
|
@ -822,7 +826,7 @@ pub trait AttributeExt: Debug {
|
|||
|
||||
#[inline]
|
||||
fn has_name(&self, name: Symbol) -> bool {
|
||||
self.ident().map(|x| x.name == name).unwrap_or(false)
|
||||
self.name().map(|x| x == name).unwrap_or(false)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -836,13 +840,13 @@ pub trait AttributeExt: Debug {
|
|||
fn is_word(&self) -> bool;
|
||||
|
||||
fn path(&self) -> SmallVec<[Symbol; 1]> {
|
||||
self.ident_path()
|
||||
.map(|i| i.into_iter().map(|i| i.name).collect())
|
||||
.unwrap_or(smallvec![sym::doc])
|
||||
self.symbol_path().unwrap_or(smallvec![sym::doc])
|
||||
}
|
||||
|
||||
fn path_span(&self) -> Option<Span>;
|
||||
|
||||
/// Returns None for doc comments
|
||||
fn ident_path(&self) -> Option<SmallVec<[Ident; 1]>>;
|
||||
fn symbol_path(&self) -> Option<SmallVec<[Symbol; 1]>>;
|
||||
|
||||
/// Returns the documentation if this is a doc comment or a sugared doc comment.
|
||||
/// * `///doc` returns `Some("doc")`.
|
||||
|
|
@ -903,10 +907,6 @@ impl Attribute {
|
|||
AttributeExt::value_span(self)
|
||||
}
|
||||
|
||||
pub fn ident(&self) -> Option<Ident> {
|
||||
AttributeExt::ident(self)
|
||||
}
|
||||
|
||||
pub fn path_matches(&self, name: &[Symbol]) -> bool {
|
||||
AttributeExt::path_matches(self, name)
|
||||
}
|
||||
|
|
@ -938,10 +938,6 @@ impl Attribute {
|
|||
AttributeExt::path(self)
|
||||
}
|
||||
|
||||
pub fn ident_path(&self) -> Option<SmallVec<[Ident; 1]>> {
|
||||
AttributeExt::ident_path(self)
|
||||
}
|
||||
|
||||
pub fn doc_str(&self) -> Option<Symbol> {
|
||||
AttributeExt::doc_str(self)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ impl<'a> PostExpansionVisitor<'a> {
|
|||
|
||||
impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
|
||||
fn visit_attribute(&mut self, attr: &ast::Attribute) {
|
||||
let attr_info = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name));
|
||||
let attr_info = attr.name().and_then(|name| BUILTIN_ATTRIBUTE_MAP.get(&name));
|
||||
// Check feature gates for built-in attributes.
|
||||
if let Some(BuiltinAttribute {
|
||||
gate: AttributeGate::Gated { feature, message, check, notes, .. },
|
||||
|
|
|
|||
|
|
@ -371,13 +371,7 @@ fn parse_cfg_attr_internal<'a>(
|
|||
attribute.span,
|
||||
attribute.get_normal_item().span(),
|
||||
attribute.style,
|
||||
AttrPath {
|
||||
segments: attribute
|
||||
.ident_path()
|
||||
.expect("cfg_attr is not a doc comment")
|
||||
.into_boxed_slice(),
|
||||
span: attribute.span,
|
||||
},
|
||||
AttrPath { segments: attribute.path().into_boxed_slice(), span: attribute.span },
|
||||
Some(attribute.get_normal_item().unsafety),
|
||||
ParsedDescription::Attribute,
|
||||
pred_span,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use rustc_hir::attrs::CfgEntry;
|
|||
use rustc_parse::exp;
|
||||
use rustc_parse::parser::Parser;
|
||||
use rustc_session::Session;
|
||||
use rustc_span::{ErrorGuaranteed, Ident, Span};
|
||||
use rustc_span::{ErrorGuaranteed, Span, sym};
|
||||
|
||||
use crate::parser::MetaItemOrLitParser;
|
||||
use crate::{AttributeParser, ParsedDescription, ShouldEmit, parse_cfg_entry};
|
||||
|
|
@ -86,10 +86,7 @@ pub fn parse_cfg_select(
|
|||
cfg_span,
|
||||
cfg_span,
|
||||
AttrStyle::Inner,
|
||||
AttrPath {
|
||||
segments: vec![Ident::from_str("cfg_select")].into_boxed_slice(),
|
||||
span: cfg_span,
|
||||
},
|
||||
AttrPath { segments: vec![sym::cfg_select].into_boxed_slice(), span: cfg_span },
|
||||
None,
|
||||
ParsedDescription::Macro,
|
||||
cfg_span,
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@ pub fn parse_version(s: Symbol) -> Option<RustcVersion> {
|
|||
}
|
||||
|
||||
pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool {
|
||||
attr.is_doc_comment().is_some()
|
||||
|| attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name))
|
||||
attr.is_doc_comment().is_some() || attr.name().is_some_and(|name| is_builtin_attr_name(name))
|
||||
}
|
||||
|
||||
/// Parse a single integer.
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ 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(),
|
||||
segments: self.segments().map(|s| s.name).collect::<Vec<_>>().into_boxed_slice(),
|
||||
span: self.span(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
|
|||
return;
|
||||
}
|
||||
|
||||
let name = (attr_path.segments.len() == 1).then_some(attr_path.segments[0].name);
|
||||
let name = (attr_path.segments.len() == 1).then_some(attr_path.segments[0]);
|
||||
if let Some(name) = name
|
||||
&& [sym::cfg_trace, sym::cfg_attr_trace].contains(&name)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
|
|||
let (applied, only) = allowed_targets_applied(allowed_targets, target, cx.features);
|
||||
let name = cx.attr_path.clone();
|
||||
|
||||
let lint = if name.segments[0].name == sym::deprecated
|
||||
let lint = if name.segments[0] == sym::deprecated
|
||||
&& ![
|
||||
Target::Closure,
|
||||
Target::Expression,
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ pub fn check_attr(psess: &ParseSess, attr: &Attribute) {
|
|||
return;
|
||||
}
|
||||
|
||||
let builtin_attr_info = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name));
|
||||
let builtin_attr_info = attr.name().and_then(|name| BUILTIN_ATTRIBUTE_MAP.get(&name));
|
||||
|
||||
// Check input tokens for built-in and key-value attributes.
|
||||
match builtin_attr_info {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpa
|
|||
use rustc_hir::AttrPath;
|
||||
use rustc_hir::attrs::CfgEntry;
|
||||
use rustc_parse::exp;
|
||||
use rustc_span::{ErrorGuaranteed, Ident, Span};
|
||||
use rustc_span::{ErrorGuaranteed, Span, sym};
|
||||
|
||||
use crate::errors;
|
||||
|
||||
|
|
@ -47,7 +47,7 @@ fn parse_cfg(cx: &ExtCtxt<'_>, span: Span, tts: TokenStream) -> Result<CfgEntry,
|
|||
span,
|
||||
span,
|
||||
AttrStyle::Inner,
|
||||
AttrPath { segments: vec![Ident::from_str("cfg")].into_boxed_slice(), span },
|
||||
AttrPath { segments: vec![sym::cfg].into_boxed_slice(), span },
|
||||
None,
|
||||
ParsedDescription::Macro,
|
||||
span,
|
||||
|
|
|
|||
|
|
@ -47,10 +47,7 @@ fn has_cfg_or_cfg_attr(annotatable: &Annotatable) -> bool {
|
|||
impl<'ast> visit::Visitor<'ast> for CfgFinder {
|
||||
type Result = ControlFlow<()>;
|
||||
fn visit_attribute(&mut self, attr: &'ast Attribute) -> ControlFlow<()> {
|
||||
if attr
|
||||
.ident()
|
||||
.is_some_and(|ident| ident.name == sym::cfg || ident.name == sym::cfg_attr)
|
||||
{
|
||||
if attr.name().is_some_and(|name| name == sym::cfg || name == sym::cfg_attr) {
|
||||
ControlFlow::Break(())
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use rustc_middle::span_bug;
|
|||
use rustc_middle::ty::{self as ty, Instance, TyCtxt};
|
||||
use rustc_session::lint;
|
||||
use rustc_session::parse::feature_err;
|
||||
use rustc_span::{Ident, Span, Symbol, sym};
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
use rustc_target::spec::Os;
|
||||
|
||||
use crate::errors;
|
||||
|
|
@ -327,7 +327,7 @@ fn process_builtin_attrs(
|
|||
}
|
||||
}
|
||||
|
||||
let Some(Ident { name, .. }) = attr.ident() else {
|
||||
let Some(name) = attr.name() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -2110,7 +2110,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
|
|||
let mut attr_pos = None;
|
||||
for (pos, attr) in item.attrs().iter().enumerate() {
|
||||
if !attr.is_doc_comment() && !self.cx.expanded_inert_attrs.is_marked(attr) {
|
||||
let name = attr.ident().map(|ident| ident.name);
|
||||
let name = attr.name();
|
||||
if name == Some(sym::cfg) || name == Some(sym::cfg_attr) {
|
||||
cfg_pos = Some(pos); // a cfg attr found, no need to search anymore
|
||||
break;
|
||||
|
|
@ -2187,7 +2187,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
|
|||
} else if rustc_attr_parsing::is_builtin_attr(attr)
|
||||
&& !AttributeParser::<Early>::is_parsed_attribute(&attr.path())
|
||||
{
|
||||
let attr_name = attr.ident().unwrap().name;
|
||||
let attr_name = attr.name().unwrap();
|
||||
// `#[cfg]` and `#[cfg_attr]` are special - they are
|
||||
// eagerly evaluated.
|
||||
if attr_name != sym::cfg_trace && attr_name != sym::cfg_attr_trace {
|
||||
|
|
|
|||
|
|
@ -1199,7 +1199,7 @@ pub enum AttrArgs {
|
|||
|
||||
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable)]
|
||||
pub struct AttrPath {
|
||||
pub segments: Box<[Ident]>,
|
||||
pub segments: Box<[Symbol]>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
|
|
@ -1215,7 +1215,7 @@ impl AttrPath {
|
|||
segments: path
|
||||
.segments
|
||||
.iter()
|
||||
.map(|i| Ident { name: i.ident.name, span: lower_span(i.ident.span) })
|
||||
.map(|i| i.ident.name)
|
||||
.collect::<Vec<_>>()
|
||||
.into_boxed_slice(),
|
||||
span: lower_span(path.span),
|
||||
|
|
@ -1225,7 +1225,11 @@ impl AttrPath {
|
|||
|
||||
impl fmt::Display for AttrPath {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", join_path_idents(&self.segments))
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
join_path_idents(self.segments.iter().map(|i| Ident { name: *i, span: DUMMY_SP }))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1330,7 +1334,7 @@ impl AttributeExt for Attribute {
|
|||
|
||||
/// For a single-segment attribute, returns its name; otherwise, returns `None`.
|
||||
#[inline]
|
||||
fn ident(&self) -> Option<Ident> {
|
||||
fn name(&self) -> Option<Symbol> {
|
||||
match &self {
|
||||
Attribute::Unparsed(n) => {
|
||||
if let [ident] = n.path.segments.as_ref() {
|
||||
|
|
@ -1346,7 +1350,7 @@ impl AttributeExt for Attribute {
|
|||
#[inline]
|
||||
fn path_matches(&self, name: &[Symbol]) -> bool {
|
||||
match &self {
|
||||
Attribute::Unparsed(n) => n.path.segments.iter().map(|ident| &ident.name).eq(name),
|
||||
Attribute::Unparsed(n) => n.path.segments.iter().eq(name),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
@ -1382,13 +1386,20 @@ impl AttributeExt for Attribute {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn ident_path(&self) -> Option<SmallVec<[Ident; 1]>> {
|
||||
fn symbol_path(&self) -> Option<SmallVec<[Symbol; 1]>> {
|
||||
match &self {
|
||||
Attribute::Unparsed(n) => Some(n.path.segments.iter().copied().collect()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn path_span(&self) -> Option<Span> {
|
||||
match &self {
|
||||
Attribute::Unparsed(attr) => Some(attr.path.span),
|
||||
Attribute::Parsed(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn doc_str(&self) -> Option<Symbol> {
|
||||
match &self {
|
||||
|
|
@ -1468,11 +1479,6 @@ impl Attribute {
|
|||
AttributeExt::value_span(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn ident(&self) -> Option<Ident> {
|
||||
AttributeExt::ident(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn path_matches(&self, name: &[Symbol]) -> bool {
|
||||
AttributeExt::path_matches(self, name)
|
||||
|
|
@ -1508,11 +1514,6 @@ impl Attribute {
|
|||
AttributeExt::path(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn ident_path(&self) -> Option<SmallVec<[Ident; 1]>> {
|
||||
AttributeExt::ident_path(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn doc_str(&self) -> Option<Symbol> {
|
||||
AttributeExt::doc_str(self)
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ use rustc_hir::{
|
|||
TyPatKind,
|
||||
};
|
||||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::{FileName, Ident, Span, Symbol, kw, sym};
|
||||
use rustc_span::{DUMMY_SP, FileName, Ident, Span, Symbol, kw, sym};
|
||||
use {rustc_ast as ast, rustc_hir as hir};
|
||||
|
||||
pub fn id_to_string(cx: &dyn rustc_hir::intravisit::HirTyCtxt<'_>, hir_id: HirId) -> String {
|
||||
|
|
@ -136,7 +136,11 @@ impl<'a> State<'a> {
|
|||
.path
|
||||
.segments
|
||||
.iter()
|
||||
.map(|i| ast::PathSegment { ident: *i, args: None, id: DUMMY_NODE_ID })
|
||||
.map(|i| ast::PathSegment {
|
||||
ident: Ident { name: *i, span: DUMMY_SP },
|
||||
args: None,
|
||||
id: DUMMY_NODE_ID,
|
||||
})
|
||||
.collect(),
|
||||
tokens: None,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -86,9 +86,9 @@ fn has_cfg_or_cfg_attr(attrs: &[Attribute]) -> bool {
|
|||
// NOTE: Builtin attributes like `cfg` and `cfg_attr` cannot be renamed via imports.
|
||||
// Therefore, the absence of a literal `cfg` or `cfg_attr` guarantees that
|
||||
// we don't need to do any eager expansion.
|
||||
attrs.iter().any(|attr| {
|
||||
attr.ident().is_some_and(|ident| ident.name == sym::cfg || ident.name == sym::cfg_attr)
|
||||
})
|
||||
attrs
|
||||
.iter()
|
||||
.any(|attr| attr.name().is_some_and(|ident| ident == sym::cfg || ident == sym::cfg_attr))
|
||||
}
|
||||
|
||||
impl<'a> Parser<'a> {
|
||||
|
|
@ -398,10 +398,8 @@ impl<'a> Parser<'a> {
|
|||
/// - any single-segment, non-builtin attributes are present, e.g. `derive`,
|
||||
/// `test`, `global_allocator`.
|
||||
fn needs_tokens(attrs: &[ast::Attribute]) -> bool {
|
||||
attrs.iter().any(|attr| match attr.ident() {
|
||||
attrs.iter().any(|attr| match attr.name() {
|
||||
None => !attr.is_doc_comment(),
|
||||
Some(ident) => {
|
||||
ident.name == sym::cfg_attr || !rustc_feature::is_builtin_attr_name(ident.name)
|
||||
}
|
||||
Some(name) => name == sym::cfg_attr || !rustc_feature::is_builtin_attr_name(name),
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -391,7 +391,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
attr.path
|
||||
.segments
|
||||
.first()
|
||||
.and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name))
|
||||
.and_then(|name| BUILTIN_ATTRIBUTE_MAP.get(&name))
|
||||
{
|
||||
match attr.style {
|
||||
ast::AttrStyle::Outer => {
|
||||
|
|
@ -428,7 +428,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
|
||||
if let Attribute::Unparsed(unparsed_attr) = attr
|
||||
&& let Some(BuiltinAttribute { duplicates, .. }) =
|
||||
attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name))
|
||||
attr.name().and_then(|name| BUILTIN_ATTRIBUTE_MAP.get(&name))
|
||||
{
|
||||
check_duplicates(
|
||||
self.tcx,
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ impl<'a> HashStable<StableHashingContext<'a>> for [hir::Attribute] {
|
|||
.filter(|attr| {
|
||||
attr.is_doc_comment().is_none()
|
||||
// FIXME(jdonszelmann) have a better way to handle ignored attrs
|
||||
&& !attr.ident().is_some_and(|ident| hcx.is_ignored_attr(ident.name))
|
||||
&& !attr.name().is_some_and(|ident| hcx.is_ignored_attr(ident))
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
|
|
|||
|
|
@ -216,11 +216,7 @@ impl<I: Interner> Relate<I> for ty::AliasTy<I> {
|
|||
b: ty::AliasTy<I>,
|
||||
) -> RelateResult<I, ty::AliasTy<I>> {
|
||||
if a.def_id != b.def_id {
|
||||
Err(TypeError::ProjectionMismatched({
|
||||
let a = a.def_id;
|
||||
let b = b.def_id;
|
||||
ExpectedFound::new(a, b)
|
||||
}))
|
||||
Err(TypeError::ProjectionMismatched(ExpectedFound::new(a.def_id, b.def_id)))
|
||||
} else {
|
||||
let cx = relation.cx();
|
||||
let args = if let Some(variances) = cx.opt_alias_variances(a.kind(cx), a.def_id) {
|
||||
|
|
@ -240,11 +236,7 @@ impl<I: Interner> Relate<I> for ty::AliasTerm<I> {
|
|||
b: ty::AliasTerm<I>,
|
||||
) -> RelateResult<I, ty::AliasTerm<I>> {
|
||||
if a.def_id != b.def_id {
|
||||
Err(TypeError::ProjectionMismatched({
|
||||
let a = a.def_id;
|
||||
let b = b.def_id;
|
||||
ExpectedFound::new(a, b)
|
||||
}))
|
||||
Err(TypeError::ProjectionMismatched(ExpectedFound::new(a.def_id, b.def_id)))
|
||||
} else {
|
||||
let args = match a.kind(relation.cx()) {
|
||||
ty::AliasTermKind::OpaqueTy => relate_args_with_variances(
|
||||
|
|
@ -275,11 +267,7 @@ impl<I: Interner> Relate<I> for ty::ExistentialProjection<I> {
|
|||
b: ty::ExistentialProjection<I>,
|
||||
) -> RelateResult<I, ty::ExistentialProjection<I>> {
|
||||
if a.def_id != b.def_id {
|
||||
Err(TypeError::ProjectionMismatched({
|
||||
let a = a.def_id;
|
||||
let b = b.def_id;
|
||||
ExpectedFound::new(a, b)
|
||||
}))
|
||||
Err(TypeError::ProjectionMismatched(ExpectedFound::new(a.def_id, b.def_id)))
|
||||
} else {
|
||||
let term = relation.relate_with_variance(
|
||||
ty::Invariant,
|
||||
|
|
|
|||
|
|
@ -859,8 +859,8 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator<Item = &'a hir::Attribute>
|
|||
}
|
||||
continue;
|
||||
} else if !cfg_info.parent_is_doc_cfg
|
||||
&& let Some(ident) = attr.ident()
|
||||
&& matches!(ident.name, sym::cfg | sym::cfg_trace)
|
||||
&& let Some(name) = attr.name()
|
||||
&& matches!(name, sym::cfg | sym::cfg_trace)
|
||||
&& let Some(attr) = single(attr.meta_item_list()?)
|
||||
&& let Ok(new_cfg) = Cfg::parse(&attr)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2682,8 +2682,8 @@ fn add_without_unwanted_attributes<'hir>(
|
|||
import_parent,
|
||||
));
|
||||
}
|
||||
hir::Attribute::Unparsed(normal) if let [ident] = &*normal.path.segments => {
|
||||
if is_inline || ident.name != sym::cfg_trace {
|
||||
hir::Attribute::Unparsed(normal) if let [name] = &*normal.path.segments => {
|
||||
if is_inline || *name != sym::cfg_trace {
|
||||
// If it's not a `cfg()` attribute, we keep it.
|
||||
attrs.push((Cow::Borrowed(attr), import_parent));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ fn add_only_cfg_attributes(attrs: &mut Vec<Attribute>, new_attrs: &[Attribute])
|
|||
new_attr.cfg = d.cfg.clone();
|
||||
attrs.push(Attribute::Parsed(AttributeKind::Doc(Box::new(new_attr))));
|
||||
} else if let Attribute::Unparsed(normal) = attr
|
||||
&& let [ident] = &*normal.path.segments
|
||||
&& ident.name == sym::cfg_trace
|
||||
&& let [name] = &*normal.path.segments
|
||||
&& *name == sym::cfg_trace
|
||||
{
|
||||
// If it's a `cfg()` attribute, we keep it.
|
||||
attrs.push(attr.clone());
|
||||
|
|
|
|||
|
|
@ -4,18 +4,19 @@ use clippy_utils::is_from_proc_macro;
|
|||
use rustc_ast::{AttrStyle, Attribute};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, LintContext};
|
||||
use rustc_ast::attr::AttributeExt;
|
||||
|
||||
// Separate each crate's features.
|
||||
pub fn check<'cx>(cx: &EarlyContext<'cx>, attr: &'cx Attribute) {
|
||||
if !attr.span.in_external_macro(cx.sess().source_map())
|
||||
&& let AttrStyle::Outer = attr.style
|
||||
&& let Some(ident) = attr.ident()
|
||||
&& let Some(path_span) = attr.path_span()
|
||||
&& !is_from_proc_macro(cx, attr)
|
||||
{
|
||||
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
|
||||
span_lint_and_then(cx, ALLOW_ATTRIBUTES, ident.span, "#[allow] attribute found", |diag| {
|
||||
span_lint_and_then(cx, ALLOW_ATTRIBUTES, path_span, "#[allow] attribute found", |diag| {
|
||||
diag.span_suggestion(
|
||||
ident.span,
|
||||
path_span,
|
||||
"replace it with",
|
||||
"expect",
|
||||
Applicability::MachineApplicable,
|
||||
|
|
|
|||
|
|
@ -574,16 +574,16 @@ impl EarlyLintPass for PostExpansionEarlyAttributes {
|
|||
|
||||
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
|
||||
if let Some(items) = &attr.meta_item_list()
|
||||
&& let Some(ident) = attr.ident()
|
||||
&& let Some(name) = attr.name()
|
||||
{
|
||||
if matches!(ident.name, sym::allow) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) {
|
||||
if matches!(name, sym::allow) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) {
|
||||
allow_attributes::check(cx, attr);
|
||||
}
|
||||
if matches!(ident.name, sym::allow | sym::expect) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) {
|
||||
allow_attributes_without_reason::check(cx, ident.name, items, attr);
|
||||
if matches!(name, sym::allow | sym::expect) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) {
|
||||
allow_attributes_without_reason::check(cx, name, items, attr);
|
||||
}
|
||||
if is_lint_level(ident.name, attr.id) {
|
||||
blanket_clippy_restriction_lints::check(cx, ident.name, items);
|
||||
if is_lint_level(name, attr.id) {
|
||||
blanket_clippy_restriction_lints::check(cx, name, items);
|
||||
}
|
||||
if items.is_empty() || !attr.has_name(sym::deprecated) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) {
|
|||
return;
|
||||
}
|
||||
if let Some(lint_list) = &attr.meta_item_list()
|
||||
&& attr.ident().is_some_and(|ident| is_lint_level(ident.name, attr.id))
|
||||
&& attr.name().is_some_and(|name| is_lint_level(name, attr.id))
|
||||
{
|
||||
for lint in lint_list {
|
||||
match item.kind {
|
||||
|
|
|
|||
|
|
@ -270,7 +270,7 @@ fn is_under_cfg_attribute(cx: &LateContext<'_>, hir_id: HirId) -> bool {
|
|||
cx.tcx.hir_parent_id_iter(hir_id).any(|id| {
|
||||
cx.tcx.hir_attrs(id).iter().any(|attr| {
|
||||
matches!(
|
||||
attr.ident().map(|ident| ident.name),
|
||||
attr.name(),
|
||||
Some(sym::cfg_trace | sym::cfg_attr_trace)
|
||||
)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -287,8 +287,8 @@ fn is_doc_attr(attr: &Attribute) -> bool {
|
|||
match attr {
|
||||
Attribute::Parsed(AttributeKind::DocComment { .. }) => true,
|
||||
Attribute::Unparsed(attr)
|
||||
if let [ident] = &*attr.path.segments
|
||||
&& ident.name == sym::doc =>
|
||||
if let [name] = &*attr.path.segments
|
||||
&& *name == sym::doc =>
|
||||
{
|
||||
matches!(attr.args, AttrArgs::Eq { .. })
|
||||
},
|
||||
|
|
|
|||
|
|
@ -20,10 +20,11 @@ pub fn get_builtin_attr<'a, A: AttributeExt + 'a>(
|
|||
name: Symbol,
|
||||
) -> impl Iterator<Item = &'a A> {
|
||||
attrs.iter().filter(move |attr| {
|
||||
if let Some([clippy, segment2]) = attr.ident_path().as_deref()
|
||||
&& clippy.name == sym::clippy
|
||||
if let [clippy, segment2] = &*attr.path()
|
||||
&& *clippy == sym::clippy
|
||||
{
|
||||
let new_name = match segment2.name {
|
||||
let path_span = attr.path_span().expect("Clippy attributes are unparsed and have a span");
|
||||
let new_name = match *segment2 {
|
||||
sym::cyclomatic_complexity => Some("cognitive_complexity"),
|
||||
sym::author
|
||||
| sym::version
|
||||
|
|
@ -35,7 +36,7 @@ pub fn get_builtin_attr<'a, A: AttributeExt + 'a>(
|
|||
| sym::has_significant_drop
|
||||
| sym::format_args => None,
|
||||
_ => {
|
||||
sess.dcx().span_err(segment2.span, "usage of unknown attribute");
|
||||
sess.dcx().span_err(path_span, "usage of unknown attribute");
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
|
@ -43,17 +44,17 @@ pub fn get_builtin_attr<'a, A: AttributeExt + 'a>(
|
|||
match new_name {
|
||||
Some(new_name) => {
|
||||
sess.dcx()
|
||||
.struct_span_err(segment2.span, "usage of deprecated attribute")
|
||||
.struct_span_err(path_span, "usage of deprecated attribute")
|
||||
.with_span_suggestion(
|
||||
segment2.span,
|
||||
path_span,
|
||||
"consider using",
|
||||
new_name,
|
||||
format!("clippy::{}", new_name),
|
||||
Applicability::MachineApplicable,
|
||||
)
|
||||
.emit();
|
||||
false
|
||||
},
|
||||
None => segment2.name == name,
|
||||
None => *segment2 == name,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
|
|
|
|||
|
|
@ -348,9 +348,9 @@ fn fn_kind_pat(tcx: TyCtxt<'_>, kind: &FnKind<'_>, body: &Body<'_>, hir_id: HirI
|
|||
fn attr_search_pat(attr: &Attribute) -> (Pat, Pat) {
|
||||
match attr.kind {
|
||||
AttrKind::Normal(..) => {
|
||||
if let Some(ident) = attr.ident() {
|
||||
if let Some(name) = attr.name() {
|
||||
// NOTE: This will likely have false positives, like `allow = 1`
|
||||
let ident_string = ident.to_string();
|
||||
let ident_string = name.to_string();
|
||||
if attr.style == AttrStyle::Outer {
|
||||
(
|
||||
Pat::OwnedMultiStr(vec!["#[".to_owned() + &ident_string, ident_string]),
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
error: usage of deprecated attribute
|
||||
--> tests/ui/renamed_builtin_attr.rs:3:11
|
||||
--> tests/ui/renamed_builtin_attr.rs:3:3
|
||||
|
|
||||
LL | #[clippy::cyclomatic_complexity = "1"]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `cognitive_complexity`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `clippy::cognitive_complexity`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
error: usage of unknown attribute
|
||||
--> tests/ui/unknown_attribute.rs:3:11
|
||||
--> tests/ui/unknown_attribute.rs:3:3
|
||||
|
|
||||
LL | #[clippy::unknown]
|
||||
| ^^^^^^^
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
|
|||
|
|
@ -336,8 +336,8 @@ impl Rewrite for ast::Attribute {
|
|||
rewrite_doc_comment(snippet, shape.comment(context.config), context.config)
|
||||
} else {
|
||||
let should_skip = self
|
||||
.ident()
|
||||
.map(|s| context.skip_context.attributes.skip(s.name.as_str()))
|
||||
.name()
|
||||
.map(|s| context.skip_context.attributes.skip(s.as_str()))
|
||||
.unwrap_or(false);
|
||||
let prefix = attr_prefix(self);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
//! regression test for <https://github.com/rust-lang/rust/issues/26614>
|
||||
//@ check-pass
|
||||
|
||||
trait Mirror {
|
||||
12
tests/ui/cast/cast-enum-const.rs
Normal file
12
tests/ui/cast/cast-enum-const.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
//! regression test for <https://github.com/rust-lang/rust/issues/2428>
|
||||
//@ run-pass
|
||||
|
||||
fn main() {
|
||||
const QUUX: isize = 5;
|
||||
|
||||
enum Stuff {
|
||||
Bar = QUUX,
|
||||
}
|
||||
|
||||
assert_eq!(Stuff::Bar as isize, QUUX);
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
//! regression test for <https://github.com/rust-lang/rust/issues/22403>
|
||||
//@ run-pass
|
||||
fn main() {
|
||||
let x = Box::new([1, 2, 3]);
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
//! regression test for <https://github.com/rust-lang/rust/issues/41998>
|
||||
//@ check-pass
|
||||
|
||||
|
||||
fn main() {
|
||||
if ('x' as char) < ('y' as char) {
|
||||
print!("x");
|
||||
7
tests/ui/consts/const-static-ref-to-closure.rs
Normal file
7
tests/ui/consts/const-static-ref-to-closure.rs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
//! regression test for <https://github.com/rust-lang/rust/issues/25180>
|
||||
//@ check-pass
|
||||
#![allow(dead_code)]
|
||||
|
||||
const X: &'static dyn Fn() = &|| println!("ICE here");
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
//! regression test for <https://github.com/rust-lang/rust/issues/29030>
|
||||
//@ check-pass
|
||||
#![allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
//! regression test for <https://github.com/rust-lang/rust/issues/25394>
|
||||
//@ check-pass
|
||||
#![allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
//! regression test for <https://github.com/rust-lang/rust/issues/33687>
|
||||
//@ run-pass
|
||||
#![feature(unboxed_closures)]
|
||||
#![feature(fn_traits)]
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
//! regression test for <https://github.com/rust-lang/rust/issues/20847>
|
||||
//@ run-pass
|
||||
#![feature(fn_traits)]
|
||||
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
//@ check-pass
|
||||
|
||||
trait Common { fn dummy(&self) { } }
|
||||
|
||||
impl<'t, T> Common for (T, &'t T) {}
|
||||
|
||||
impl<'t, T> Common for (&'t T, T) {}
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
//@ check-pass
|
||||
|
||||
trait A<T> {}
|
||||
struct B<T> where B<T>: A<B<T>> { t: T }
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
//@ run-pass
|
||||
// Regression test for #20676. Error was that we didn't support
|
||||
// UFCS-style calls to a method in `Trait` where `Self` was bound to a
|
||||
// trait object of type `Trait`. See also `ufcs-trait-object.rs`.
|
||||
|
||||
|
||||
use std::fmt;
|
||||
|
||||
fn main() {
|
||||
let a: &dyn fmt::Debug = &1;
|
||||
let _ = format!("{:?}", a);
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
//@ run-pass
|
||||
|
||||
fn main() {
|
||||
match 42 {
|
||||
x if x < 7 => (),
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
//@ run-pass
|
||||
#![allow(non_upper_case_globals)]
|
||||
|
||||
|
||||
pub fn main() {
|
||||
let _foo = 100;
|
||||
const quux: isize = 5;
|
||||
|
||||
enum Stuff {
|
||||
Bar = quux
|
||||
}
|
||||
|
||||
assert_eq!(Stuff::Bar as isize, quux);
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
//@ check-pass
|
||||
#![allow(dead_code)]
|
||||
|
||||
struct Foo;
|
||||
|
||||
impl Foo {
|
||||
fn new() -> Self { Foo }
|
||||
fn bar() { Self::new(); }
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
//@ check-pass
|
||||
#![allow(dead_code)]
|
||||
#![allow(non_upper_case_globals)]
|
||||
|
||||
const x: &'static dyn Fn() = &|| println!("ICE here");
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
//@ run-pass
|
||||
#![allow(dead_code)]
|
||||
#![allow(non_upper_case_globals)]
|
||||
|
||||
fn main() {
|
||||
const iter: i32 = 0;
|
||||
|
||||
for i in 1..10 {
|
||||
println!("{}", i);
|
||||
}
|
||||
}
|
||||
15
tests/ui/resolve/const-iter-no-conflict-for-loop.rs
Normal file
15
tests/ui/resolve/const-iter-no-conflict-for-loop.rs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
//! regression test for <https://github.com/rust-lang/rust/issues/27639>
|
||||
//! Ensure that a constant named `iter` does not
|
||||
//! interfere with the name resolution of the `iter` methods used internally
|
||||
//! by `for` loops
|
||||
//@ run-pass
|
||||
#![allow(dead_code)]
|
||||
#![allow(non_upper_case_globals)]
|
||||
|
||||
fn main() {
|
||||
const iter: i32 = 0;
|
||||
|
||||
for i in 1..10 {
|
||||
println!("{}", i);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
//! regression test for <https://github.com/rust-lang/rust/issues/20427>
|
||||
//@ run-pass
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused_variables)]
|
||||
|
|
@ -31,17 +32,10 @@ mod char {
|
|||
mod char_ {}
|
||||
|
||||
mod str {
|
||||
use super::i8 as i8;
|
||||
use super::i32_ as i32;
|
||||
use super::i64_ as i64;
|
||||
use super::u8_ as u8;
|
||||
use super::f_ as f64;
|
||||
use super::u16_ as u16;
|
||||
use super::u32_ as u32;
|
||||
use super::u64_ as u64;
|
||||
use super::bool_ as bool;
|
||||
use super::{bool_ as str};
|
||||
use super::char_ as char;
|
||||
use super::{
|
||||
bool_ as bool, bool_ as str, char_ as char, f_ as f64, i8, i32_ as i32, i64_ as i64,
|
||||
u8_ as u8, u16_ as u16, u32_ as u32, u64_ as u64,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -49,7 +43,9 @@ trait isize_ {
|
|||
type isize;
|
||||
}
|
||||
|
||||
fn usize<'usize>(usize: &'usize usize) -> &'usize usize { usize }
|
||||
fn usize<'usize>(usize: &'usize usize) -> &'usize usize {
|
||||
usize
|
||||
}
|
||||
|
||||
mod reuse {
|
||||
use std::mem::size_of;
|
||||
|
|
@ -68,9 +64,10 @@ mod reuse {
|
|||
mod guard {
|
||||
pub fn check() {
|
||||
use std::u8; // bring module u8 in scope
|
||||
fn f() -> u8 { // OK, resolves to primitive u8, not to std::u8
|
||||
fn f() -> u8 {
|
||||
// OK, resolves to primitive u8, not to std::u8
|
||||
u8::max_value() // OK, resolves to associated function <u8>::max_value,
|
||||
// not to non-existent std::u8::max_value
|
||||
// not to non-existent std::u8::max_value
|
||||
}
|
||||
assert_eq!(f(), u8::MAX); // OK, resolves to std::u8::MAX
|
||||
}
|
||||
|
|
@ -79,7 +76,13 @@ mod guard {
|
|||
fn main() {
|
||||
let bool = true;
|
||||
let _ = match bool {
|
||||
str @ true => if str { i32 as i64 } else { i64 },
|
||||
str @ true => {
|
||||
if str {
|
||||
i32 as i64
|
||||
} else {
|
||||
i64
|
||||
}
|
||||
}
|
||||
false => i64,
|
||||
};
|
||||
|
||||
16
tests/ui/self/self-in-method-body-resolves.rs
Normal file
16
tests/ui/self/self-in-method-body-resolves.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
//! regression test for <https://github.com/rust-lang/rust/issues/24389>
|
||||
//@ check-pass
|
||||
#![allow(dead_code)]
|
||||
|
||||
struct Foo;
|
||||
|
||||
impl Foo {
|
||||
fn new() -> Self {
|
||||
Foo
|
||||
}
|
||||
fn bar() {
|
||||
Self::new();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
12
tests/ui/traits/bound/recursive-trait-bound-on-type-param.rs
Normal file
12
tests/ui/traits/bound/recursive-trait-bound-on-type-param.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
//! regression test for <https://github.com/rust-lang/rust/issues/19601>
|
||||
//@ check-pass
|
||||
|
||||
trait A<T> {}
|
||||
struct B<T>
|
||||
where
|
||||
B<T>: A<B<T>>,
|
||||
{
|
||||
t: T,
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
12
tests/ui/typeck/tuple-ref-order-distinct-impls.rs
Normal file
12
tests/ui/typeck/tuple-ref-order-distinct-impls.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
//! regression test for <https://github.com/rust-lang/rust/issues/11384>
|
||||
//@ check-pass
|
||||
|
||||
trait Common {
|
||||
fn dummy(&self) {}
|
||||
}
|
||||
|
||||
impl<'t, T> Common for (T, &'t T) {}
|
||||
|
||||
impl<'t, T> Common for (&'t T, T) {}
|
||||
|
||||
fn main() {}
|
||||
13
tests/ui/ufcs/ufcs-trait-object-format.rs
Normal file
13
tests/ui/ufcs/ufcs-trait-object-format.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
//@ run-pass
|
||||
//! Regression test for <https://github.com/rust-lang/rust/issues/20676>.
|
||||
//! Error was that we didn't support
|
||||
//! UFCS-style calls to a method in `Trait` where `Self` was bound to a
|
||||
//! trait object of type `Trait`.
|
||||
//! See also <https://github.com/rust-lang/rust/blob/ec2cc76/tests/ui/traits/ufcs-object.rs>.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
fn main() {
|
||||
let a: &dyn fmt::Debug = &1;
|
||||
let _ = format!("{:?}", a);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue