diff --git a/Cargo.lock b/Cargo.lock index 39049cd7e2c6f..f8dd8db9529e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3599,6 +3599,7 @@ name = "rustc_ast" version = "0.0.0" dependencies = [ "bitflags", + "either", "memchr", "rustc-literal-escaper", "rustc_ast_ir", @@ -3758,6 +3759,7 @@ dependencies = [ name = "rustc_builtin_macros" version = "0.0.0" dependencies = [ + "itertools", "rustc_ast", "rustc_ast_pretty", "rustc_attr_ir", @@ -4589,6 +4591,7 @@ name = "rustc_parse" version = "0.0.0" dependencies = [ "bitflags", + "itertools", "rustc-literal-escaper", "rustc_ast", "rustc_ast_pretty", diff --git a/compiler/rustc_ast/Cargo.toml b/compiler/rustc_ast/Cargo.toml index fc9118f593ce5..0461d00214dfa 100644 --- a/compiler/rustc_ast/Cargo.toml +++ b/compiler/rustc_ast/Cargo.toml @@ -6,6 +6,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start bitflags = "2.4.1" +either = "1.15.0" memchr = "2.7.6" rustc-literal-escaper = "0.0.8" rustc_ast_ir = { path = "../rustc_ast_ir" } diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 6a86e9af71826..29203328c774b 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -18,11 +18,14 @@ //! - [`Attribute`]: Metadata associated with item. //! - [`UnOp`], [`BinOp`], and [`BinOpKind`]: Unary and binary operators. +// ignore-tidy-file-filelength + use std::borrow::{Borrow, Cow}; -use std::{cmp, fmt}; +use std::{cmp, fmt, iter}; pub use GenericArgs::*; pub use UnsafeSource::*; +use either::Either; pub use rustc_ast_ir::{FloatTy, IntTy, Movability, Mutability, Pinnedness, UintTy}; use rustc_data_structures::packed::Pu128; use rustc_data_structures::stable_hash::{StableHash, StableHashCtxt, StableHasher}; @@ -85,29 +88,33 @@ impl fmt::Display for Lifetime { /// A "Path" is essentially Rust's notion of a name. /// -/// It's represented as a sequence of identifiers, -/// along with a bunch of supporting information. +/// We separate the common case of a single identifier (e.g. `x`) from the general case of a +/// sequence of identifiers that might also have generics attached (e.g. `std::cmp::PartialEq`, +/// `Vec::::new`). /// -/// E.g., `std::cmp::PartialEq`. +/// Canonical form is desirable but not mandatory. Single idents may use General if their span +/// differs from the ident's span, or occasionally simply because they didn't get canonicalized. #[derive(Clone, Encodable, Decodable, Debug, Walkable)] -pub struct Path { - pub span: Span, - /// The segments in the path: the things separated by `::`. - /// Global paths begin with `kw::PathRoot`. - pub segments: ThinVec, +pub enum Path { + /// The common case of a single identifier (e.g. `x`) + Ident { ident: Ident, id: NodeId }, + /// The common case of a general path, which must have at least one segment, and the span can be + /// trivially reconstructed from the spans of the first and last segments. + NoSpan { + /// The segments in the path: the things separated by `::`. Global paths begin with + /// `kw::PathRoot`. + segments: ThinVec, + }, + /// A fully general path where the path's span differs (e.g. from recovery). Boxed to avoid + /// making `Path` larger. + General(Box<(ThinVec, Span)>), } // Succeeds if the path has a single segment that is arg-free and matches the given symbol. impl PartialEq for Path { #[inline] fn eq(&self, name: &Symbol) -> bool { - if let [segment] = self.segments.as_ref() - && segment == name - { - true - } else { - false - } + self.as_single_argless_name() == Some(*name) } } @@ -115,16 +122,22 @@ impl PartialEq for Path { impl PartialEq<&[Symbol]> for Path { #[inline] fn eq(&self, names: &&[Symbol]) -> bool { - self.segments.iter().eq(*names) + match self { + Path::Ident { ident, .. } => { + let [name] = names else { + return false; + }; + ident.name == *name + } + Path::NoSpan { segments } | Path::General((segments, _)) => segments.iter().eq(*names), + } } } impl StableHash for Path { fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { - self.segments.len().stable_hash(hcx, hasher); - for segment in &self.segments { - segment.ident.stable_hash(hcx, hasher); - } + self.num_segments().stable_hash(hcx, hasher); + self.iter_idents().for_each(|ident| ident.stable_hash(hcx, hasher)); } } @@ -132,25 +145,165 @@ impl Path { /// Convert a span and an identifier to the corresponding /// one-segment path. pub fn from_ident(ident: Ident) -> Path { - Path { segments: thin_vec![PathSegment::from_ident(ident)], span: ident.span } + Path::Ident { ident, id: DUMMY_NODE_ID } + } + + /// Convert a set of segments and a span to the corresponding `Path`. + #[inline] + pub fn from_segments(segments: ThinVec, span: Span) -> Path { + if let [segment] = segments.as_slice() + && segment.ident.span == span + && segment.args.is_none() + { + Path::Ident { ident: segment.ident, id: segment.id } + } else if !segments.is_empty() && segments_span(&segments) == span { + Path::NoSpan { segments } + } else { + Path::General(Box::new((segments, span))) + } + } + + #[inline] + pub fn span(&self) -> Span { + match self { + Path::Ident { ident, .. } => ident.span, + Path::NoSpan { segments } => segments_span(segments), + Path::General((_, span)) => *span, + } + } + + pub fn path_root(span: Span) -> Self { + Path::from_ident(Ident::new(kw::PathRoot, span)) } pub fn is_global(&self) -> bool { - self.segments.first().is_some_and(|segment| segment.ident.name == kw::PathRoot) + self.iter_idents().next().is_some_and(|i| i.name == kw::PathRoot) } /// Checks if this path is just a simple one-word `PATH` - i.e. the inverse of /// [`Path::from_ident`] + #[inline] pub fn is_single_argless_ident(&self) -> bool { - self.segments.len() == 1 && self.segments[0].args.is_none() + self.as_single_argless_ident().is_some() } /// The inverse of [`Path::from_ident`] - if this path is just a simple one-word `PATH` + #[inline] pub fn as_single_argless_ident(&self) -> Option { - self.is_single_argless_ident().then(|| self.segments[0].ident) + // This can't *exclusively* handle the `Path::Ident` case, because a single ident can use + // `Path::General` if the path span differs from the ident span, or `Path::NoSpan` if it + // isn't in canonical form. + match self { + Path::Ident { ident, .. } => Some(*ident), + Path::NoSpan { segments } | Path::General((segments, _)) => { + let [segment] = segments.as_ref() else { + return None; + }; + if segment.args.is_some() { + return None; + } + Some(segment.ident) + } + } + } + + #[inline] + pub fn as_single_argless_name(&self) -> Option { + self.as_single_argless_ident().map(|i| i.name) + } + + #[inline] + pub fn is_empty(&self) -> bool { + match self { + Path::General((segments, _)) => segments.is_empty(), + // Path::NoSpan always has non-empty segments. Path::Ident is non-empty by definition. + _ => false, + } + } + + #[inline] + pub fn num_segments(&self) -> usize { + match self { + Path::Ident { .. } => 1, + Path::NoSpan { segments } | Path::General((segments, _)) => segments.len(), + } + } + + #[inline] + pub fn iter_idents(&self) -> impl DoubleEndedIterator + ExactSizeIterator { + match self { + Path::Ident { ident, .. } => Either::Left(iter::once(ident)), + Path::NoSpan { segments } | Path::General((segments, _)) => { + Either::Right(segments.iter().map(|s| &s.ident)) + } + } + } + + #[inline] + pub fn iter_segments( + &self, + ) -> impl DoubleEndedIterator> + ExactSizeIterator { + match self { + &Path::Ident { ref ident, id } => { + Either::Left(iter::once(PathSegmentRef { ident, id, args: None })) + } + Path::NoSpan { segments } | Path::General((segments, _)) => { + Either::Right(segments.iter().map(PathSegment::as_ref)) + } + } + } + + #[inline] + pub fn last_segment(&self) -> Option> { + match self { + &Path::Ident { ref ident, id } => Some(PathSegmentRef { ident, id, args: None }), + Path::NoSpan { segments } | Path::General((segments, _)) => { + segments.last().map(PathSegment::as_ref) + } + } + } + + #[inline] + pub fn last_ident(&self) -> Option { + match self { + &Path::Ident { ident, .. } => Some(ident), + Path::NoSpan { segments } | Path::General((segments, _)) => { + segments.last().map(|s| s.ident) + } + } + } + + pub fn force_general_mut(&mut self) -> (&mut ThinVec, &mut Span) { + match self { + Path::General((segments, span)) => return (segments, span), + Path::NoSpan { segments } => { + let span = segments_span(segments); + *self = Path::General(Box::new((std::mem::take(segments), span))) + } + &mut Path::Ident { ident, id } => { + *self = Path::General(Box::new(( + thin_vec![PathSegment { ident, id, args: None }], + ident.span, + ))); + } + } + match self { + Path::General((segments, span)) => (segments, span), + _ => unreachable!(), + } } } +#[inline] +fn segments_span(segments: &[PathSegment]) -> Span { + let last = segments.last().unwrap(); + let last_span = match last.args.as_deref() { + Some(last_args) => last_args.span(), + None => last.ident.span, + }; + segments[0].ident.span.to(last_span) +} + /// Joins multiple symbols with "::" into a path, e.g. "a::b::c". If the first /// segment is `kw::PathRoot` it will be printed as empty, e.g. "::b::c". /// @@ -240,12 +393,34 @@ impl PathSegment { PathSegment::from_ident(Ident::new(kw::PathRoot, span)) } + #[inline] pub fn span(&self) -> Span { match &self.args { Some(args) => self.ident.span.to(args.span()), None => self.ident.span, } } + + pub fn as_ref(&self) -> PathSegmentRef<'_> { + PathSegmentRef { ident: &self.ident, id: self.id, args: self.args.as_deref() } + } +} + +#[derive(Copy, Clone, Debug)] +pub struct PathSegmentRef<'a> { + pub ident: &'a Ident, + pub id: NodeId, + pub args: Option<&'a GenericArgs>, +} + +impl PathSegmentRef<'_> { + #[inline] + pub fn span(&self) -> Span { + match self.args { + Some(args) => self.ident.span.to(args.span()), + None => self.ident.span, + } + } } /// The generic arguments and associated item constraints of a path segment. @@ -2060,7 +2235,11 @@ pub struct MacCall { impl MacCall { pub fn span(&self) -> Span { - self.path.span.to(self.args.dspan.entire()) + let first_span = match &self.path { + Path::NoSpan { segments } => segments[0].ident.span, + _ => self.path.span(), + }; + first_span.to(self.args.dspan.entire()) } } @@ -2588,14 +2767,7 @@ impl TyKind { } pub fn is_simple_path(&self) -> Option { - if let TyKind::Path(None, Path { segments, .. }) = &self - && let [segment] = &segments[..] - && segment.args.is_none() - { - Some(segment.ident.name) - } else { - None - } + if let TyKind::Path(None, path) = &self { path.as_single_argless_name() } else { None } } /// Returns `true` if this type is considered a scalar primitive (e.g., @@ -3340,7 +3512,7 @@ impl UseTree { match self.kind { UseTreeKind::Simple(Some(rename)) => rename, UseTreeKind::Simple(None) => { - self.prefix.segments.last().expect("empty prefix in a simple import").ident + self.prefix.last_ident().expect("empty prefix in a simple import") } _ => panic!("`UseTree::ident` can only be used on a simple import"), } @@ -3350,7 +3522,10 @@ impl UseTree { /// closing `}` or nested spans, `*` of glob spans or the end of the /// identifier of simple spans. pub fn span(&self) -> Span { - self.prefix.span.to(self.hi_span()) + match self.kind { + UseTreeKind::Simple(None) => self.prefix.span(), + _ => self.prefix.span().to(self.hi_span()), + } } /// Returns the trailing element's span. So for a nested @@ -3360,7 +3535,7 @@ impl UseTree { /// path if no rename is specified. pub fn hi_span(&self) -> Span { match self.kind { - UseTreeKind::Simple(None) => self.prefix.span, + UseTreeKind::Simple(None) => self.prefix.span(), UseTreeKind::Simple(Some(name)) => name.span, UseTreeKind::Nested { span, .. } => span, UseTreeKind::Glob(span) => span, @@ -3994,7 +4169,7 @@ pub struct Delegation { impl Delegation { pub fn last_segment_span(&self) -> Span { - self.path.segments.last().unwrap().ident.span + self.path.last_ident().unwrap().span } } diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 40a1b4bd32218..262b67ade8f93 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -115,7 +115,7 @@ impl AttributeExt for Attribute { use SyntheticAttr::*; match &self.kind { AttrKind::Normal(normal) => { - Some(normal.item.path.segments.iter().map(|i| i.ident.name).collect()) + Some(normal.item.path.iter_idents().map(|i| i.name).collect()) } AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace(_)) => None, AttrKind::DocComment(_, _) => None, @@ -124,7 +124,7 @@ impl AttributeExt for Attribute { fn path_span(&self) -> Option { match &self.kind { - AttrKind::Normal(attr) => Some(attr.item.path.span), + AttrKind::Normal(attr) => Some(attr.item.path.span()), AttrKind::Synthetic(..) => unreachable!(), AttrKind::DocComment(_, _) => None, } @@ -132,16 +132,7 @@ impl AttributeExt for Attribute { fn path_matches(&self, name: &[Symbol]) -> bool { match &self.kind { - AttrKind::Normal(normal) => { - normal.item.path.segments.len() == name.len() - && normal - .item - .path - .segments - .iter() - .zip(name) - .all(|(s, n)| s.args.is_none() && s.ident.name == *n) - } + AttrKind::Normal(normal) => normal.item.path == name, AttrKind::Synthetic(..) | AttrKind::DocComment(..) => false, } } @@ -339,7 +330,7 @@ impl Attribute { impl AttrItem { pub fn name(&self) -> Option { - if let [seg] = &*self.path.segments { Some(seg.ident.name) } else { None } + self.path.as_single_argless_name() } pub fn meta_item_list(&self) -> Option> { @@ -411,11 +402,11 @@ impl AttrItem { impl MetaItem { /// For a single-segment meta item, returns its name; otherwise, returns `None`. pub fn ident(&self) -> Option { - if let [PathSegment { ident, .. }] = self.path.segments[..] { Some(ident) } else { None } + self.path.as_single_argless_ident() } pub fn name(&self) -> Option { - self.ident().map(|ident| ident.name) + self.path.as_single_argless_name() } pub fn has_name(&self, name: Symbol) -> bool { @@ -509,7 +500,7 @@ impl MetaItem { iter.next(); } let span = span.with_hi(segments.last().unwrap().ident.span.hi()); - Path { span, segments } + Path::from_segments(segments, span) } Some(TokenTree::Delimited( _span, @@ -529,12 +520,13 @@ impl MetaItem { }; let list_closing_paren_pos = iter.peek().map(|tt| tt.span().hi()); let kind = MetaItemKind::from_tokens(iter)?; - let hi = match &kind { - MetaItemKind::NameValue(lit) => lit.span.hi(), - MetaItemKind::List(..) => list_closing_paren_pos.unwrap_or(path.span.hi()), - _ => path.span.hi(), + let span = match &kind { + MetaItemKind::NameValue(lit) => path.span().with_hi(lit.span.hi()), + MetaItemKind::List(..) if let Some(list_closing_paren_pos) = list_closing_paren_pos => { + path.span().with_hi(list_closing_paren_pos) + } + _ => path.span(), }; - let span = path.span.with_hi(hi); // FIXME: This parses `unsafe()` not as unsafe attribute syntax in `MetaItem`, // but as a parenthesized list. This (and likely `MetaItem`) should be changed in // such a way that builtin macros don't accept extraneous `unsafe()`. diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs index 86a4073bd100f..e5c778fde2fe4 100644 --- a/compiler/rustc_ast/src/util/classify.rs +++ b/compiler/rustc_ast/src/util/classify.rs @@ -321,9 +321,7 @@ fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> { /// ^^^^^^^^^^^^^^^^^^^^^ /// ``` fn path_return_type(path: &ast::Path) -> Option<&ast::Ty> { - let last_segment = path.segments.last()?; - let args = last_segment.args.as_ref()?; - match &**args { + match path.last_segment()?.args? { ast::GenericArgs::Parenthesized(args) => match &args.output { ast::FnRetTy::Default(_) => None, ast::FnRetTy::Ty(ret) => Some(ret), diff --git a/compiler/rustc_ast_lowering/src/delegation/generics.rs b/compiler/rustc_ast_lowering/src/delegation/generics.rs index 836bf7df2bbd5..76635cbb7d5dc 100644 --- a/compiler/rustc_ast_lowering/src/delegation/generics.rs +++ b/compiler/rustc_ast_lowering/src/delegation/generics.rs @@ -309,7 +309,8 @@ impl<'hir> DelegationResolver<'_, 'hir> { let qself_is_none = delegation.qself.is_none(); - let parent_args = if let [.., parent_segment, _] = &delegation.path.segments[..] { + let parent_args = if let Some(parent_segment) = delegation.path.iter_segments().nth_back(1) + { let res = self.get_resolution_id(parent_segment.id)?; if !matches!(tcx.def_kind(res), DefKind::Mod) { assert_matches!( @@ -339,13 +340,13 @@ impl<'hir> DelegationResolver<'_, 'hir> { trait_impl: matches!(delegation_parent_kind, DefKind::Impl { of_trait: true }), sig_child_params: &tcx.generics_of(sig_id).own_params, child_args: self.get_user_args( - delegation.path.segments.last().expect("must be at least one segment"), + delegation.path.last_segment().expect("must be at least one segment"), ), }) } - fn get_user_args<'a>(&self, segment: &'a PathSegment) -> Option<&'a AngleBracketedArgs> { - let Some(args) = &segment.args else { return None }; + fn get_user_args<'a>(&self, segment: PathSegmentRef<'a>) -> Option<&'a AngleBracketedArgs> { + let Some(args) = segment.args else { return None }; let GenericArgs::AngleBracketed(args) = args else { self.tcx().dcx().span_delayed_bug( segment.span(), diff --git a/compiler/rustc_ast_lowering/src/delegation/resolution.rs b/compiler/rustc_ast_lowering/src/delegation/resolution.rs index 7678418c47e56..61c8c3f10eacb 100644 --- a/compiler/rustc_ast_lowering/src/delegation/resolution.rs +++ b/compiler/rustc_ast_lowering/src/delegation/resolution.rs @@ -68,7 +68,7 @@ pub(crate) fn resolve_type_relative_delegations( let res = r.partial_res_map.get(&delegation.id); let res = res.and_then(|res| res.base_res().opt_def_id()); - let ident = delegation.path.segments.last().map(|s| s.ident); + let ident = delegation.path.last_ident(); let span = delegation.last_segment_span(); @@ -240,23 +240,23 @@ impl<'tcx> DelegationResolver<'_, 'tcx> { let create_invalid_path_error = || tcx.dcx().span_delayed_bug(span, "invalid delegation path"); - match &delegation.path.segments[..] { - [] => return Err(create_invalid_path_error()), - [child] => { - let res = self.get_resolution_id(child.id)?; - if tcx.def_kind(res) != DefKind::Fn { - return Err(create_invalid_path_error()); - } + let mut segment_iter = delegation.path.iter_segments(); + let Some(child) = segment_iter.next_back() else { + return Err(create_invalid_path_error()); + }; + if let Some(parent) = segment_iter.next_back() { + let child_res = self.get_call_path_res(delegation, span)?; + let parent_res = self.get_resolution_id(parent.id)?; + + match (tcx.def_kind(child_res), tcx.def_kind(parent_res)) { + (DefKind::Fn, DefKind::Mod) => {} + (DefKind::AssocFn, DefKind::Trait | DefKind::Struct | DefKind::Enum) => {} + _ => return Err(create_invalid_path_error()), } - [.., parent, _] => { - let child_res = self.get_call_path_res(delegation, span)?; - let parent_res = self.get_resolution_id(parent.id)?; - - match (tcx.def_kind(child_res), tcx.def_kind(parent_res)) { - (DefKind::Fn, DefKind::Mod) => {} - (DefKind::AssocFn, DefKind::Trait | DefKind::Struct | DefKind::Enum) => {} - _ => return Err(create_invalid_path_error()), - } + } else { + let res = self.get_resolution_id(child.id)?; + if tcx.def_kind(res) != DefKind::Fn { + return Err(create_invalid_path_error()); } } diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index f4bfec24e3b1b..6c8e9bb702e14 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -277,7 +277,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ExprKind::MethodCall(MethodCall { seg, receiver, args, span }) => { let hir_seg = self.arena.alloc(self.lower_path_segment( e.span, - seg, + seg.as_ref(), ParamMode::Optional, GenericArgsMode::Err, ImplTraitContext::Disallowed(ImplTraitPosition::Path), @@ -656,7 +656,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } // Add generic args to the last element of the path. - let last_segment = path.segments.last_mut().unwrap(); + let last_segment = path.force_general_mut().0.last_mut().unwrap(); assert!(last_segment.args.is_none()); last_segment.args = Some(Box::new(GenericArgs::AngleBracketed(AngleBracketedArgs { span: DUMMY_SP, diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 6ec5903c5fb3b..d69775391ecbb 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -534,7 +534,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_path_simple_eii(&mut self, id: NodeId, path: &Path) -> Option { let res = self.get_partial_res(id)?; let Some(did) = res.expect_full_res().opt_def_id() else { - self.dcx().span_delayed_bug(path.span, "should have errored in resolve"); + self.dcx().span_delayed_bug(path.span(), "should have errored in resolve"); return None; }; @@ -549,27 +549,21 @@ impl<'hir> LoweringContext<'_, 'hir> { vis_span: Span, attrs: &'hir [rustc_attr_ir::Attribute], ) -> hir::UseTree<'hir> { - let path = &tree.prefix; - let segments = path.segments.iter().cloned().collect(); - match tree.kind { UseTreeKind::Simple(rename) => { let mut ident = tree.ident(); - // First, apply the prefix to the path. - let mut path = Path { segments, span: path.span }; - // Correctly resolve `self` imports. - if path.segments.len() > 1 - && path.segments.last().unwrap().ident.name == kw::SelfLower - { - let _ = path.segments.pop(); + let mut path = tree.prefix.clone(); + if path.num_segments() > 1 && path.last_ident().unwrap().name == kw::SelfLower { + let segments = path.force_general_mut().0; + let _ = segments.pop(); if rename.is_none() { - ident = path.segments.last().unwrap().ident; + ident = segments.last().unwrap().ident; } } - let res = self.lower_import_res(id, path.span); + let res = self.lower_import_res(id, &path); let path = self.lower_use_path(res, &path, ParamMode::Explicit); let ident = self.lower_ident(ident); hir::UseTree { prefix: path, kind: hir::UseKind::Single(ident) } @@ -579,8 +573,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let res = self.lower_res(res); // Put the result in the appropriate namespace. let res = res.in_namespace(); - let path = Path { segments, span: path.span }; - let path = self.lower_use_path(res, &path, ParamMode::Explicit); + let path = self.lower_use_path(res, &tree.prefix, ParamMode::Explicit); hir::UseTree { prefix: path, kind: hir::UseKind::Glob } } UseTreeKind::Nested { items: ref trees, .. } => { @@ -588,7 +581,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let res = self.lower_res(res); // Put the result in the appropriate namespace. let res = res.in_namespace(); - let prefix = self.lower_use_path(res, &path, ParamMode::Explicit); + let prefix = self.lower_use_path(res, &tree.prefix, ParamMode::Explicit); let items = self.arena.alloc_from_iter(trees.iter().map(|use_tree| { let id = use_tree.id; let hir_id = self.lower_node_id(id); @@ -1683,22 +1676,20 @@ impl<'hir> LoweringContext<'_, 'hir> { RestrictionKind::Restricted { path, id, shorthand: _ } => { let res = self.get_partial_res(*id); let parent_module = self.tcx.parent_module(hir_id); + let span = path.span(); if let Some(did) = res.and_then(|res| res.expect_full_res().opt_def_id()) { if !self.tcx.is_descendant_of(parent_module, did) { // If the restriction path is not an ancestor of the item, // emit an error and recover by lowering the restriction to `Unrestricted`. - self.dcx().emit_err(RestrictionAncestorOnly { - span: path.span, - kind: resolving_kind, - }); + self.dcx().emit_err(RestrictionAncestorOnly { span, kind: resolving_kind }); hir::RestrictionKind::Unrestricted } else { hir::RestrictionKind::Restricted(self.arena.alloc(hir::Path { res: did, - segments: self.arena.alloc_from_iter(path.segments.iter().map( + segments: self.arena.alloc_from_iter(path.iter_segments().map( |segment| { self.lower_path_segment( - path.span, + span, segment, ParamMode::Explicit, GenericArgsMode::Err, @@ -1707,11 +1698,11 @@ impl<'hir> LoweringContext<'_, 'hir> { ) }, )), - span: self.lower_span(path.span), + span: self.lower_span(span), })) } } else { - self.dcx().span_delayed_bug(path.span, "should have errored in resolve"); + self.dcx().span_delayed_bug(span, "should have errored in resolve"); hir::RestrictionKind::Unrestricted } } @@ -1821,17 +1812,17 @@ impl<'hir> LoweringContext<'_, 'hir> { let define_opaque = define_opaque.iter().filter_map(|(id, path)| { let res = self.get_partial_res(*id); let Some(did) = res.and_then(|res| res.expect_full_res().opt_def_id()) else { - self.dcx().span_delayed_bug(path.span, "should have errored in resolve"); + self.dcx().span_delayed_bug(path.span(), "should have errored in resolve"); return None; }; let Some(did) = did.as_local() else { self.dcx().span_err( - path.span, + path.span(), "only opaque types defined in the local crate can be defined", ); return None; }; - Some((self.lower_span(path.span), did)) + Some((self.lower_span(path.span()), did)) }); let define_opaque = self.arena.alloc_from_iter(define_opaque); self.curr_owner.define_opaque = Some(define_opaque); diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 4b6e897da4c87..66ab771053019 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -418,7 +418,7 @@ impl<'tcx> ResolverAstLowering<'tcx> { // Don't perform legacy const generics rewriting if the path already // has generic arguments. - if path.segments.last().unwrap().args.is_some() { + if path.last_segment().unwrap().args.is_some() { return None; } @@ -644,7 +644,7 @@ fn index_ast<'tcx>( // Lacking a better choice, we replace the contents with a macro call. // Unexpanded macros should never reach lowering, so this is not confusing. kind: dummy(Box::new(MacCall { - path: Path { span, segments: thin_vec![] }, + path: Path::from_segments(thin_vec![], span), args: Box::new(DelimArgs { dspan: DelimSpan::from_single(span), delim: Delimiter::Parenthesis, @@ -943,7 +943,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.get_partial_res(id).map_or(Res::Err, |pr| pr.expect_full_res()) } - fn lower_import_res(&self, id: NodeId, span: Span) -> PerNS> { + fn lower_import_res(&self, id: NodeId, path: &ast::Path) -> PerNS> { let per_ns = self .curr_owner .owner @@ -962,7 +962,7 @@ impl<'hir> LoweringContext<'_, 'hir> { if per_ns.is_empty() { // Propagate the error to all namespaces, just to be sure. - self.dcx().span_delayed_bug(span, "no resolution for an import"); + self.dcx().span_delayed_bug(path.span(), "no resolution for an import"); let err = Some(Res::Err); return PerNS { type_ns: err, value_ns: err, macro_ns: err }; } @@ -1866,15 +1866,13 @@ impl<'hir> LoweringContext<'_, 'hir> { self.lower_lifetime(lt, LifetimeSource::PreciseCapturing, lt.ident.into()), ), PreciseCapturingArg::Arg(path, id) => { - let [segment] = path.segments.as_slice() else { - panic!(); - }; + let ident = path.as_single_argless_ident().unwrap(); let res = self.get_partial_res(*id).map_or(Res::Err, |partial_res| { partial_res.full_res().expect("no partial res expected for precise capture arg") }); hir::PreciseCapturingArg::Param(hir::PreciseCapturingNonLifetimeArg { hir_id: self.lower_node_id(*id), - ident: self.lower_ident(segment.ident), + ident: self.lower_ident(ident), res: self.lower_res(res), }) } diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 14f10e2b1c416..f7d89fce0a6ac 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -45,6 +45,8 @@ impl<'hir> LoweringContext<'_, 'hir> { let partial_res = self.get_partial_res(id).unwrap_or_else(|| PartialRes::new(Res::Err)); let base_res = partial_res.base_res(); let unresolved_segments = partial_res.unresolved_segments(); + let span = p.span(); + let p_num_segments = p.num_segments(); let mut res = self.lower_res(base_res); @@ -55,7 +57,7 @@ impl<'hir> LoweringContext<'_, 'hir> { if let Some(async_def_id) = self.map_trait_to_async_trait(def_id) { res = Res::Def(DefKind::Trait, async_def_id); } else { - self.dcx().emit_err(AsyncBoundOnlyForFnTraits { span: p.span }); + self.dcx().emit_err(AsyncBoundOnlyForFnTraits { span }); } } Res::Err => { @@ -65,7 +67,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // This error isn't actually emitted AFAICT, but it's best to keep // it around in case the resolver doesn't always check the defkind // of an item or something. - self.dcx().emit_err(AsyncBoundNotOnTrait { span: p.span, descr: res.descr() }); + self.dcx().emit_err(AsyncBoundNotOnTrait { span, descr: res.descr() }); } } } @@ -83,70 +85,70 @@ impl<'hir> LoweringContext<'_, 'hir> { // Only permit `impl Trait` in the final segment. E.g., we permit `Option`, // `option::Option::Xyz` and reject `option::Option::Xyz`. let itctx = |i| { - if i + 1 == p.segments.len() { + if i + 1 == p_num_segments { itctx } else { ImplTraitContext::Disallowed(ImplTraitPosition::Path) } }; - let path_span_lo = p.span.shrink_to_lo(); - let proj_start = p.segments.len() - unresolved_segments; - let path = self.arena.alloc(hir::Path { - res, - segments: self.arena.alloc_from_iter(p.segments[..proj_start].iter().enumerate().map( - |(i, segment)| { - let param_mode = match (qself_position, param_mode) { - (Some(j), ParamMode::Optional) if i < j => { - // This segment is part of the trait path in a - // qualified path - one of `a`, `b` or `Trait` - // in `::T::U::method`. - ParamMode::Explicit - } - _ => param_mode, - }; + let path_span_lo = span.shrink_to_lo(); + let proj_start = p_num_segments - unresolved_segments; + let mut last_prefix_span = path_span_lo; + let mut segment_iter = p.iter_segments().enumerate(); + let segments = self.arena.alloc_from_iter(segment_iter.by_ref().take(proj_start).map( + |(i, segment)| { + let param_mode = match (qself_position, param_mode) { + (Some(j), ParamMode::Optional) if i < j => { + // This segment is part of the trait path in a + // qualified path - one of `a`, `b` or `Trait` + // in `::T::U::method`. + ParamMode::Explicit + } + _ => param_mode, + }; - let generic_args_mode = match base_res { - // `a::b::Trait(Args)` - Res::Def(DefKind::Trait, _) if i + 1 == proj_start => { - GenericArgsMode::ParenSugar - } - // `a::b::Trait(Args)::TraitItem` - Res::Def(DefKind::AssocFn, _) - | Res::Def(DefKind::AssocConst, _) - | Res::Def(DefKind::AssocTy, _) - if i + 2 == proj_start => - { - GenericArgsMode::ParenSugar - } - Res::Def(DefKind::AssocFn, _) if i + 1 == proj_start => { - match allow_return_type_notation { - AllowReturnTypeNotation::Yes => GenericArgsMode::ReturnTypeNotation, - AllowReturnTypeNotation::No => GenericArgsMode::Err, - } + let generic_args_mode = match base_res { + // `a::b::Trait(Args)` + Res::Def(DefKind::Trait, _) if i + 1 == proj_start => { + GenericArgsMode::ParenSugar + } + // `a::b::Trait(Args)::TraitItem` + Res::Def(DefKind::AssocFn, _) + | Res::Def(DefKind::AssocConst, _) + | Res::Def(DefKind::AssocTy, _) + if i + 2 == proj_start => + { + GenericArgsMode::ParenSugar + } + Res::Def(DefKind::AssocFn, _) if i + 1 == proj_start => { + match allow_return_type_notation { + AllowReturnTypeNotation::Yes => GenericArgsMode::ReturnTypeNotation, + AllowReturnTypeNotation::No => GenericArgsMode::Err, } - // Avoid duplicated errors. - Res::Err => GenericArgsMode::Silence, - // An error - _ => GenericArgsMode::Err, - }; - - self.lower_path_segment( - p.span, - segment, - param_mode, - generic_args_mode, - itctx(i), - bound_modifier_allowed_features.clone(), - ) - }, - )), - span: self.lower_span( - p.segments[..proj_start] - .last() - .map_or(path_span_lo, |segment| path_span_lo.to(segment.span())), - ), - }); + } + // Avoid duplicated errors. + Res::Err => GenericArgsMode::Silence, + // An error + _ => GenericArgsMode::Err, + }; + + if i + 1 == proj_start { + last_prefix_span = path_span_lo.to(segment.span()); + } + + self.lower_path_segment( + span, + segment, + param_mode, + generic_args_mode, + itctx(i), + bound_modifier_allowed_features.clone(), + ) + }, + )); + let path = + self.arena.alloc(hir::Path { res, segments, span: self.lower_span(last_prefix_span) }); if let Some(bound_modifier_allowed_features) = bound_modifier_allowed_features { path.span = self.mark_span_with_reason( @@ -185,9 +187,11 @@ impl<'hir> LoweringContext<'_, 'hir> { // 2. `>::IntoIter` // 3. `<>::IntoIter>::Item` // * final path is `<<>::IntoIter>::Item>::clone` - for (i, segment) in p.segments.iter().enumerate().skip(proj_start) { + // + // We've already consumed the prefix from `segment_iter`, so we can resume it here. + for (i, segment) in segment_iter { // If this is a type-dependent `T::method(..)`. - let generic_args_mode = if i + 1 == p.segments.len() + let generic_args_mode = if i + 1 == p_num_segments && matches!(allow_return_type_notation, AllowReturnTypeNotation::Yes) { GenericArgsMode::ReturnTypeNotation @@ -196,7 +200,7 @@ impl<'hir> LoweringContext<'_, 'hir> { }; let hir_segment = self.arena.alloc(self.lower_path_segment( - p.span, + span, segment, param_mode, generic_args_mode, @@ -206,7 +210,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let qpath = hir::QPath::TypeRelative(ty, hir_segment); // It's finished, return the extension of the right node type. - if i == p.segments.len() - 1 { + if i == p_num_segments - 1 { return qpath; } @@ -218,11 +222,10 @@ impl<'hir> LoweringContext<'_, 'hir> { // We should've returned in the for loop above. self.dcx().span_bug( - p.span, + span, format!( "lower_qpath: no final extension segment in {}..{}", - proj_start, - p.segments.len() + proj_start, p_num_segments ), ); } @@ -234,11 +237,12 @@ impl<'hir> LoweringContext<'_, 'hir> { param_mode: ParamMode, ) -> &'hir hir::UsePath<'hir> { assert!(!res.is_empty()); + let path_span = p.span(); self.arena.alloc(hir::UsePath { res, - segments: self.arena.alloc_from_iter(p.segments.iter().map(|segment| { + segments: self.arena.alloc_from_iter(p.iter_segments().map(|segment| { self.lower_path_segment( - p.span, + path_span, segment, param_mode, GenericArgsMode::Err, @@ -246,14 +250,14 @@ impl<'hir> LoweringContext<'_, 'hir> { None, ) })), - span: self.lower_span(p.span), + span: self.lower_span(path_span), }) } pub(crate) fn lower_path_segment( &mut self, path_span: Span, - segment: &PathSegment, + segment: PathSegmentRef<'_>, param_mode: ParamMode, generic_args_mode: GenericArgsMode, itctx: ImplTraitContext, @@ -263,7 +267,7 @@ impl<'hir> LoweringContext<'_, 'hir> { bound_modifier_allowed_features: Option>, ) -> hir::PathSegment<'hir> { debug!("path_span: {:?}, lower_path_segment(segment: {:?})", path_span, segment); - let (mut generic_args, infer_args) = if let Some(generic_args) = segment.args.as_deref() { + let (mut generic_args, infer_args) = if let Some(generic_args) = segment.args { match generic_args { GenericArgs::AngleBracketed(data) => { self.lower_angle_bracketed_parameter_data(data, param_mode, itctx) @@ -406,7 +410,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ); hir::PathSegment { - ident: self.lower_ident(segment.ident), + ident: self.lower_ident(*segment.ident), hir_id, res: self.lower_res(res), infer_args, diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 010afc6be5534..1338dd9b8d05d 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1449,7 +1449,7 @@ impl Visitor<'_> for AstValidator<'_> { } if let (&Safety::Unsafe(span), &ImplPolarity::Negative(sp)) = (safety, polarity) { self.dcx().emit_err(diagnostics::UnsafeNegativeImpl { - span: sp.to(t.path.span), + span: sp.to(t.path.span()), negative: sp, r#unsafe: span, }); @@ -1465,7 +1465,7 @@ impl Visitor<'_> for AstValidator<'_> { Some(TraitOrImpl::TraitImpl { constness: *constness, polarity: *polarity, - trait_ref_span: t.path.span, + trait_ref_span: t.path.span(), }), |this| { walk_list!( @@ -1897,9 +1897,9 @@ impl Visitor<'_> for AstValidator<'_> { // Negative trait bounds are not allowed to have associated constraints if let BoundPolarity::Negative(_) = trait_ref.modifiers.polarity - && let Some(segment) = trait_ref.trait_ref.path.segments.last() + && let Some(segment) = trait_ref.trait_ref.path.last_segment() { - match segment.args.as_deref() { + match segment.args { Some(ast::GenericArgs::AngleBracketed(args)) => { for arg in &args.args { if let ast::AngleBracketedArg::Constraint(constraint) = arg { diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 45fa1863e715f..3a89116897315 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -163,7 +163,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { gate!( self, negative_impls, - span.to(of_trait.trait_ref.path.span), + span.to(of_trait.trait_ref.path.span()), "negative impls are experimental", "use marker types for now" ); diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index b76502814dbb7..b51675724a370 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -932,9 +932,9 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere } fn print_path(&mut self, path: &ast::Path, colons_before_params: bool, depth: usize) { - self.maybe_print_comment(path.span.lo()); + self.maybe_print_comment(path.span().lo()); - for (i, segment) in path.segments[..path.segments.len() - depth].iter().enumerate() { + for (i, segment) in path.iter_segments().take(path.num_segments() - depth).enumerate() { if i > 0 { self.word("::") } @@ -942,10 +942,10 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere } } - fn print_path_segment(&mut self, segment: &ast::PathSegment, colons_before_params: bool) { + fn print_path_segment(&mut self, segment: ast::PathSegmentRef<'_>, colons_before_params: bool) { if segment.ident.name != kw::PathRoot { - self.print_ident(segment.ident); - if let Some(args) = &segment.args { + self.print_ident(*segment.ident); + if let Some(args) = segment.args { self.print_generic_args(args, colons_before_params); } } @@ -1164,7 +1164,7 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere } fn attr_item_to_string(&self, ai: &ast::AttrItem) -> String { - Self::to_string(|s| s.print_attr_item(ai, ai.path.span)) + Self::to_string(|s| s.print_attr_item(ai, ai.path.span())) } fn tts_to_string(&self, tokens: &TokenStream) -> String { @@ -1883,14 +1883,14 @@ impl<'a> State<'a> { if qself.position > 0 { self.space(); self.word_space("as"); - let depth = path.segments.len() - qself.position; + let depth = path.num_segments() - qself.position; self.print_path(path, false, depth); } self.word(">"); - for item_segment in &path.segments[qself.position..] { + for item_segment in path.iter_segments().skip(qself.position) { self.word("::"); - self.print_ident(item_segment.ident); - if let Some(args) = &item_segment.args { + self.print_ident(*item_segment.ident); + if let Some(args) = item_segment.args { self.print_generic_args(args, colons_before_params) } } @@ -2411,7 +2411,7 @@ impl<'a> State<'a> { } pub(crate) fn path_segment_to_string(&self, p: &ast::PathSegment) -> String { - Self::to_string(|s| s.print_path_segment(p, false)) + Self::to_string(|s| s.print_path_segment(p.as_ref(), false)) } pub(crate) fn meta_list_item_to_string(&self, li: &ast::MetaItemInner) -> String { diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 6ce34cecc7e97..7901fde839328 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -898,14 +898,14 @@ impl<'a> State<'a> { } } ast::UseTreeKind::Glob(_) => { - if !tree.prefix.segments.is_empty() { + if !tree.prefix.is_empty() { self.print_path(&tree.prefix, false, 0); self.word("::"); } self.word("*"); } ast::UseTreeKind::Nested { items, .. } => { - if !tree.prefix.segments.is_empty() { + if !tree.prefix.is_empty() { self.print_path(&tree.prefix, false, 0); self.word("::"); } @@ -915,9 +915,9 @@ impl<'a> State<'a> { && !item .inner .prefix - .segments - .first() - .is_some_and(|seg| seg.ident.name == rustc_span::symbol::kw::SelfLower) + .iter_idents() + .next() + .is_some_and(|i| i.name == rustc_span::symbol::kw::SelfLower) { self.print_use_tree(&item.inner); } else { diff --git a/compiler/rustc_attr_ir/src/attr.rs b/compiler/rustc_attr_ir/src/attr.rs index 6068c11590a23..f8972fe0c0d32 100644 --- a/compiler/rustc_attr_ir/src/attr.rs +++ b/compiler/rustc_attr_ir/src/attr.rs @@ -42,13 +42,8 @@ impl IntoDiagArg for AttrPath { impl AttrPath { pub fn from_ast(path: &ast::Path, lower_span: impl Copy + Fn(Span) -> Span) -> Self { AttrPath { - segments: path - .segments - .iter() - .map(|i| i.ident.name) - .collect::>() - .into_boxed_slice(), - span: lower_span(path.span), + segments: path.iter_idents().map(|i| i.name).collect(), + span: lower_span(path.span()), } } } diff --git a/compiler/rustc_attr_ir/src/pretty_printing.rs b/compiler/rustc_attr_ir/src/pretty_printing.rs index cd8a0c0e1e96f..29428eb9607c9 100644 --- a/compiler/rustc_attr_ir/src/pretty_printing.rs +++ b/compiler/rustc_attr_ir/src/pretty_printing.rs @@ -112,7 +112,7 @@ impl PrintAttribute for Path { } fn print_attribute(&self, p: &mut Printer) { - p.word(join_path_idents(self.segments.iter().map(|seg| seg.ident))); + p.word(join_path_idents(self.iter_idents())); } } diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs index 53114d0ca9d8a..4df7f56147804 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs @@ -1,6 +1,6 @@ use std::ops::Range; -use rustc_ast::PathSegment; +use rustc_ast::PathSegmentRef; use rustc_attr_ir::diagnostic::{ Directive, Filter, FilterFormatString, Flag, FormatArg, FormatString, LitOrArg, Name, NameValue, Piece, Predicate, @@ -38,7 +38,7 @@ pub(crate) mod opaque; impl<'sess> crate::AttributeParser<'sess> { pub(crate) fn unknown_diagnostic_attr( &self, - segment: &PathSegment, + segment: PathSegmentRef<'_>, mut emit_lint: impl FnMut(LintId, MultiSpan, EmitAttribute), ) { const DIAGNOSTIC_ATTRIBUTES: [( diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index df89ffe57b9a0..b5d74c93c88e1 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -185,12 +185,10 @@ impl<'sess> AttributeParser<'sess> { expected_safety: AttributeSafety, ) -> Option { let attr_item = attr.get_normal_item(); - let parts = attr_item.path.segments.iter().map(|seg| seg.ident.name).collect::>(); - let path = AttrPath::from_ast(&attr_item.path, identity); let args = ArgParser::from_attr_args( &attr_item.args, - &parts, + path.segments.as_ref(), &sess.psess, emit_errors, allow_expr_metavar, @@ -364,11 +362,10 @@ impl<'sess> AttributeParser<'sess> { ast::AttrKind::Normal(n) => { attr_paths.push(PathParser(&n.item.path)); let attr_path = AttrPath::from_ast(&n.item.path, lower_span); - let parts = - n.item.path.segments.iter().map(|seg| seg.ident.name).collect::>(); + let parts = attr_path.segments.as_ref(); let inner_span = lower_span(n.item.span); - if let Some(accept) = ATTRIBUTE_PARSERS.accepters.get(parts.as_slice()) { + if let Some(accept) = ATTRIBUTE_PARSERS.accepters.get(parts) { self.check_attribute_safety( &attr_path, inner_span, @@ -377,13 +374,13 @@ impl<'sess> AttributeParser<'sess> { &mut emit_lint, ); self.check_attribute_stability(&attr_path, attr_span, accept.stability); - if let [part] = parts.as_slice() { + if let [part] = parts { debug_assert!(BUILTIN_ATTRIBUTE_SET.contains(part)); } let Some(args) = ArgParser::from_attr_args( &n.item.args, - &parts, + parts, &self.sess.psess, self.should_emit, AllowExprMetavar::No, @@ -437,7 +434,7 @@ impl<'sess> AttributeParser<'sess> { parsed_description: ParsedDescription::Attribute, template: &accept.template, attr_safety: n.item.unsafety, - attr_path: attr_path.clone(), + attr_path, #[cfg(debug_assertions)] has_target_been_checked: false, }; @@ -450,11 +447,14 @@ impl<'sess> AttributeParser<'sess> { if !cx.shared.has_lint_been_emitted.load(Ordering::Relaxed) { cx.shared.cx.check_args_used(attr, &args) } - } else if let [sym::diagnostic, _unknown, ..] = &*parts { - self.unknown_diagnostic_attr(&n.item.path.segments[1], &mut emit_lint); + } else if let [sym::diagnostic, _unknown, ..] = parts { + self.unknown_diagnostic_attr( + n.item.path.iter_segments().nth(1).unwrap(), + &mut emit_lint, + ); } else { let attr = AttrItem { - path: attr_path.clone(), + path: attr_path, args: self.lower_attr_args(&n.item.args, lower_span), id: HashIgnoredAttrId { attr_id: attr.id }, style: attr.style, @@ -462,7 +462,7 @@ impl<'sess> AttributeParser<'sess> { }; self.check_attribute_safety( - &attr_path, + &attr.path, inner_span, n.item.unsafety, AttributeSafety::Normal, diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index c85b1f8fee967..6014c5b8ff9a4 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -15,9 +15,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use rustc_ast::token::{self, Delimiter, MetaVarKind}; use rustc_ast::tokenstream::TokenStream; -use rustc_ast::{ - AttrArgs, Expr, ExprKind, LitKind, MetaItemLit, Path, PathSegment, StmtKind, UnOp, -}; +use rustc_ast::{AttrArgs, Expr, ExprKind, LitKind, MetaItemLit, Path, StmtKind, UnOp}; use rustc_ast_pretty::pprust; use rustc_attr_ir::AttrPath; use rustc_errors::{Applicability, Diag, PResult}; @@ -42,30 +40,27 @@ pub type RefPathParser<'p> = PathParser<&'p Path>; impl> PathParser

{ pub fn get_attribute_path(&self) -> AttrPath { - AttrPath { - segments: self.segments().map(|s| s.name).collect::>().into_boxed_slice(), - span: self.span(), - } + AttrPath { segments: self.segments().map(|s| s.name).collect(), span: self.span() } } - pub fn segments(&self) -> impl Iterator { - self.0.borrow().segments.iter().map(|seg| &seg.ident) + pub fn segments(&self) -> impl DoubleEndedIterator + ExactSizeIterator { + self.0.borrow().iter_idents() } pub fn span(&self) -> Span { - self.0.borrow().span + self.0.borrow().span() } pub fn len(&self) -> usize { - self.0.borrow().segments.len() + self.0.borrow().num_segments() } pub fn segments_is(&self, segments: &[Symbol]) -> bool { - self.segments().map(|segment| &segment.name).eq(segments) + *self.0.borrow() == segments } pub fn word(&self) -> Option { - (self.len() == 1).then(|| **self.segments().next().as_ref().unwrap()) + self.0.borrow().as_single_argless_ident() } pub fn word_sym(&self) -> Option { @@ -329,7 +324,7 @@ pub struct MetaItemParser { impl MetaItemParser { /// For a single-segment meta item, returns its name; otherwise, returns `None`. pub fn ident(&self) -> Option { - if let [PathSegment { ident, .. }] = self.path.0.segments[..] { Some(ident) } else { None } + self.path.0.as_single_argless_ident() } pub fn span(&self) -> Span { @@ -455,7 +450,7 @@ fn expr_to_lit<'sess>( // Suggest adding quotation marks to turn an identifier into a string literal if let ExprKind::Path(None, ref path) = expr.kind - && let [_] = path.segments.as_slice() + && path.is_single_argless_ident() { err.multipart_suggestion( "you might have meant to write a string literal", @@ -657,8 +652,8 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> { negative_sign: expr.span.until(val.span), }); } else if let StmtKind::Expr(expr) = &stmt.kind - && let ExprKind::Path(None, Path { segments, .. }) = &expr.kind - && segments.len() == 1 + && let ExprKind::Path(None, path) = &expr.kind + && path.num_segments() == 1 { while let token::Ident(..) | token::Literal(_) | token::Dot = self.parser.token.kind diff --git a/compiler/rustc_builtin_macros/Cargo.toml b/compiler/rustc_builtin_macros/Cargo.toml index f53bc5b33c899..71e89f95ed3cb 100644 --- a/compiler/rustc_builtin_macros/Cargo.toml +++ b/compiler/rustc_builtin_macros/Cargo.toml @@ -7,6 +7,7 @@ edition = "2024" doctest = false [dependencies] +itertools = "0.15.0" # tidy-alphabetical-start rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs index 11c97da0db5ad..7139cfcb637ef 100644 --- a/compiler/rustc_builtin_macros/src/assert/context.rs +++ b/compiler/rustc_builtin_macros/src/assert/context.rs @@ -1,3 +1,4 @@ +use itertools::Itertools; use rustc_ast::token::{self, Delimiter, IdentKind}; use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree}; use rustc_ast::{ @@ -249,9 +250,8 @@ impl<'cx, 'a> Context<'cx, 'a> { ExprKind::Move(local_expr, _) => { self.manage_cond_expr(local_expr); } - ExprKind::Path(_, Path { segments, .. }) if let [path_segment] = &segments[..] => { - let path_ident = path_segment.ident; - self.manage_initial_capture(expr, path_ident); + ExprKind::Path(_, path) if let Some(&ident) = path.iter_idents().exactly_one().ok() => { + self.manage_initial_capture(expr, ident); } ExprKind::Paren(local_expr) => { self.manage_cond_expr(local_expr); diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs index 21f424836e476..b4dc7544af842 100644 --- a/compiler/rustc_builtin_macros/src/autodiff.rs +++ b/compiler/rustc_builtin_macros/src/autodiff.rs @@ -54,9 +54,7 @@ mod llvm_enzyme { } } - let segments = &x.meta_item().unwrap().path.segments; - assert!(segments.len() == 1); - segments[0].ident + x.ident().unwrap() } fn name(x: &MetaItemInner) -> String { @@ -372,7 +370,7 @@ mod llvm_enzyme { (ast::AttrKind::Normal(a), ast::AttrKind::Normal(b)) => { let a = &a.item.path; let b = &b.item.path; - a.segments.iter().eq_by(&b.segments, |a, b| a.ident == b.ident) + a.iter_idents().eq(b.iter_idents()) } _ => false, } diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs index cf9d0aef7582b..edd518fa519c9 100644 --- a/compiler/rustc_builtin_macros/src/derive.rs +++ b/compiler/rustc_builtin_macros/src/derive.rs @@ -139,15 +139,15 @@ fn report_unexpected_meta_item_lit(sess: &Session, lit: &ast::MetaItemLit) { } fn report_path_args(sess: &Session, meta: &ast::MetaItem) { - let span = meta.span.with_lo(meta.path.span.hi()); + let span = || meta.span.with_lo(meta.path.span().hi()); match meta.kind { MetaItemKind::Word => {} MetaItemKind::List(..) => { - sess.dcx().emit_err(diagnostics::DerivePathArgsList { span }); + sess.dcx().emit_err(diagnostics::DerivePathArgsList { span: span() }); } MetaItemKind::NameValue(..) => { - sess.dcx().emit_err(diagnostics::DerivePathArgsValue { span }); + sess.dcx().emit_err(diagnostics::DerivePathArgsValue { span: span() }); } } } diff --git a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs index 7d0db12a450d6..22827581057a4 100644 --- a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs +++ b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs @@ -304,7 +304,7 @@ fn contains_maybe_sized_bound(bounds: &[GenericBound]) -> bool { fn is_sized_marker(path: &ast::Path) -> bool { const CORE_UNSIZE: [Symbol; 3] = [sym::core, sym::marker, sym::Sized]; const STD_UNSIZE: [Symbol; 3] = [sym::std, sym::marker, sym::Sized]; - let segments = || path.segments.iter().map(|segment| segment.ident.name); + let segments = || path.iter_idents().map(|i| i.name); if path.is_global() { segments().skip(1).eq(CORE_UNSIZE) || segments().skip(1).eq(STD_UNSIZE) } else { diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 8b15920511aca..ce7e627fb34c6 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -178,6 +178,7 @@ use std::ops::Not; use std::vec; pub(crate) use Substructure::*; +use itertools::Itertools; pub(crate) use rustc_ast as ast; use rustc_ast::token::{IdentKind, LitKind, Token, TokenKind}; use rustc_ast::tokenstream::{DelimSpan, Spacing, TokenTree}; @@ -402,8 +403,8 @@ fn find_type_parameters( } if let ast::TyKind::Path(_, path) = &ty.kind - && let Some(segment) = path.segments.first() - && self.ty_param_names.contains(&segment.ident.name) + && let Some(ident) = path.iter_idents().next() + && self.ty_param_names.contains(&ident.name) { self.type_params.push(TypeParameter { bound_generic_params: self.bound_generic_params_stack.clone(), @@ -586,12 +587,16 @@ impl<'a> TraitDef<'a> { let bounds: ThinVec<_> = self .additional_bounds .iter() - .map(|p| cx.trait_bound(ast::Path { span, ..p.clone() }, self.is_const)) + .map(|p| { + let mut p = p.clone(); + *p.force_general_mut().1 = span; + cx.trait_bound(p, self.is_const) + }) .chain( // Add a bound for the current trait. self.skip_path_as_bound.not().then(|| { let mut trait_path = self.path.clone(); - trait_path.span = span; + *trait_path.force_general_mut().1 = span; cx.trait_bound(trait_path, self.is_const) }), ) @@ -657,8 +662,8 @@ impl<'a> TraitDef<'a> { for field_ty_param in field_ty_params { // if we have already handled this type, skip it if let ast::TyKind::Path(_, p) = &field_ty_param.ty.kind - && let [sole_segment] = &*p.segments - && ty_param_names.contains(&sole_segment.ident.name) + && let Ok(sole_ident) = p.iter_idents().exactly_one() + && ty_param_names.contains(&sole_ident.name) { continue; } diff --git a/compiler/rustc_builtin_macros/src/eii.rs b/compiler/rustc_builtin_macros/src/eii.rs index ed37599c9e85a..f7223f3a26514 100644 --- a/compiler/rustc_builtin_macros/src/eii.rs +++ b/compiler/rustc_builtin_macros/src/eii.rs @@ -259,10 +259,9 @@ fn name_for_impl_macro( if meta_item.is_word() { Ok(item_ident) } else if let Some([first]) = meta_item.meta_item_list() - && let Some(m) = first.meta_item() - && m.path.segments.len() == 1 + && let Some(ident) = first.ident() { - Ok(m.path.segments[0].ident) + Ok(ident) } else { Err(ecx.dcx().emit_err(EiiMacroExpectedMaxOneArgument { span: meta_item.span, @@ -607,9 +606,9 @@ pub(crate) fn eii_shared_macro( false } else if let Some([first]) = meta_item.meta_item_list() && let Some(m) = first.meta_item() - && m.path.segments.len() == 1 + && let Some(name) = m.name() { - m.path.segments[0].ident.name == kw::Default + name == kw::Default } else { ecx.dcx().emit_err(EiiMacroExpectedMaxOneArgument { span: meta_item.span, @@ -623,7 +622,7 @@ pub(crate) fn eii_shared_macro( } *eii_impl = Some(Box::new(EiiImpl { node_id: DUMMY_NODE_ID, - inner_span: meta_item.path.span, + inner_span: meta_item.path.span(), eii_macro_path: meta_item.path.clone(), impl_safety: meta_item.unsafety, span, diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index e52da1136dd64..ecc2c7b0d296b 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -211,8 +211,7 @@ fn make_format_args( && let [stmt] = block.stmts.as_slice() && let StmtKind::Expr(expr) = &stmt.kind && let ExprKind::Path(None, path) = &expr.kind - && path.segments.len() == 1 - && path.segments[0].args.is_none() + && path.is_single_argless_ident() { err.multipart_suggestion( "quote your inlined format argument to use as string literal", diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index c564e14776c91..ae72579592642 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -5,7 +5,6 @@ #![allow(internal_features)] #![feature(decl_macro)] #![feature(deref_patterns)] -#![feature(iter_order_by)] #![feature(proc_macro_internals)] #![feature(proc_macro_quote)] #![feature(try_blocks)] diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs index ecf4f7e7fb1af..a8b4b3d34fd48 100644 --- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs +++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs @@ -181,8 +181,8 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { let prev_item = prev_attr.get_normal_item(); let item = attr.get_normal_item(); let path_str = pprust::path_to_string(&item.path); - let msg = if item.path.segments[0].ident.name - == prev_item.path.segments[0].ident.name + let msg = if item.path.iter_idents().next().unwrap().name + == prev_item.path.iter_idents().next().unwrap().name { format!( "only one `#[{path_str}]` attribute is allowed on any given function", @@ -232,7 +232,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { if !self.is_proc_macro_crate { let path = &attr.get_normal_item().path; self.dcx.emit_err(diagnostics::AttributeOnlyUsableWithCrateType { - span: path.span, + span: path.span(), path: &pprust::path_to_string(&attr.get_normal_item().path), }); return; diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index d6167d30558a3..0416221b83486 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -304,7 +304,7 @@ fn add_main(cx: &mut TestCtxt<'_>, c: &mut ast::Crate) { ecx.path(sp, vec![test_ident, Ident::from_str_and_span(runner_name, sp)]) }); - test_runner.span = sp; + *test_runner.force_general_mut().1 = sp; let test_main_path_expr = ecx.expr_path(test_runner); let call_test_main = ecx.expr_call(sp, test_main_path_expr, thin_vec![mk_tests_slice(cx, sp)]); diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 2a4089b1213ab..f487cd441fac4 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -24,7 +24,12 @@ impl<'a> ExtCtxt<'a> { self.path_all(span, false, strs, vec![]) } pub fn path_ident(&self, span: Span, id: Ident) -> ast::Path { - self.path(span, vec![id]) + let ident = id.with_span_pos(span); + if ident.span == span { + ast::Path::from_ident(ident) + } else { + ast::Path::General(Box::new((thin_vec![ast::PathSegment::from_ident(ident)], span))) + } } pub fn path_global(&self, span: Span, strs: Vec) -> ast::Path { self.path_all(span, true, strs, vec![]) @@ -38,6 +43,12 @@ impl<'a> ExtCtxt<'a> { ) -> ast::Path { assert!(!idents.is_empty()); let add_root = global && !idents[0].is_path_segment_keyword(); + if !add_root + && args.is_empty() + && let &[ident] = idents.as_slice() + { + return self.path_ident(span, ident); + } let mut segments = ThinVec::with_capacity(idents.len() + add_root as usize); if add_root { segments.push(ast::PathSegment::path_root(span)); @@ -57,7 +68,7 @@ impl<'a> ExtCtxt<'a> { id: ast::DUMMY_NODE_ID, args, }); - ast::Path { span, segments } + ast::Path::from_segments(segments, span) } pub fn macro_call( @@ -86,7 +97,7 @@ impl<'a> ExtCtxt<'a> { } pub fn ty_path(&self, path: ast::Path) -> Box { - self.ty(path.span, ast::TyKind::Path(None, path)) + self.ty(path.span(), ast::TyKind::Path(None, path)) } // Might need to take bounds as an argument in the future, if you ever want @@ -211,7 +222,7 @@ impl<'a> ExtCtxt<'a> { } pub fn trait_bound(&self, path: ast::Path, is_const: bool) -> ast::GenericBound { - ast::GenericBound::Trait(self.poly_trait_ref(path.span, path, is_const)) + ast::GenericBound::Trait(self.poly_trait_ref(path.span(), path, is_const)) } pub fn lifetime(&self, span: Span, ident: Ident) -> ast::Lifetime { @@ -306,7 +317,7 @@ impl<'a> ExtCtxt<'a> { } pub fn expr_path(&self, path: ast::Path) -> Box { - self.expr(path.span, ast::ExprKind::Path(None, path)) + self.expr(path.span(), ast::ExprKind::Path(None, path)) } pub fn expr_ident(&self, span: Span, id: Ident) -> Box { diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 3cab950963102..f1e3ef5c51dd6 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -341,7 +341,7 @@ impl<'a> StripUnconfigured<'a> { .to_attr_token_stream(), )); - let attr_item_path_span = attr_item.node.path.span; + let attr_item_path_span = attr_item.node.path.span(); let attr_tokens = Some(LazyAttrTokenStream::new_direct(AttrTokenStream::new(trees))); let attr = ast::attr::mk_attr_from_item( &self.sess.psess.attr_id_generator, diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 5a2442fbccf35..5024fda016f7a 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -422,7 +422,7 @@ impl Invocation { match &self.kind { InvocationKind::Bang { span, .. } => *span, InvocationKind::Attr { attr, .. } => attr.span, - InvocationKind::Derive { path, .. } => path.span, + InvocationKind::Derive { path, .. } => path.span(), InvocationKind::GlobDelegation { item, .. } => item.span, } } @@ -431,7 +431,7 @@ impl Invocation { match &mut self.kind { InvocationKind::Bang { span, .. } => span, InvocationKind::Attr { attr, .. } => &mut attr.span, - InvocationKind::Derive { path, .. } => &mut path.span, + InvocationKind::Derive { path, .. } => path.force_general_mut().1, InvocationKind::GlobDelegation { item, .. } => &mut item.span, } } @@ -2056,7 +2056,8 @@ fn build_single_delegations<'a, Node: InvocationCollectorNode>( suffixes.iter().map(move |&(ident, rename)| { let mut path = deleg.prefix.clone(); - path.segments.push(ast::PathSegment { ident, id: ast::DUMMY_NODE_ID, args: None }); + let (segments, _) = path.force_general_mut(); + segments.push(ast::PathSegment::from_ident(ident)); ast::Item { attrs: item.attrs.clone(), @@ -2291,7 +2292,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { crate::diagnostics::UnusedBuiltinAttribute { attr_name, macro_name: pprust::path_to_string(&call.path), - invoc_span: call.path.span, + invoc_span: call.path.span(), attr_span: attr.span, }, ); diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index 736a8d1de4c19..6d0074106569a 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -562,7 +562,7 @@ fn transcribe_pnr<'tx>( ) } ParseNtResult::Path(path) => { - mk_delimited(path.node.span, MetaVarKind::Path, TokenStream::from_ast(path)) + mk_delimited(path.node.span(), MetaVarKind::Path, TokenStream::from_ast(path)) } ParseNtResult::Vis(vis) => { mk_delimited(vis.node.span, MetaVarKind::Vis, TokenStream::from_ast(vis)) diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index ad6ae5481da39..0d687db3bc41b 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -5,7 +5,6 @@ use rustc_ast::{self as ast}; use rustc_data_structures::fx::FxHashMap; use rustc_span::{DUMMY_SP, Ident}; use smallvec::{SmallVec, smallvec}; -use thin_vec::ThinVec; use crate::expand::{AstFragment, AstFragmentKind}; @@ -16,7 +15,7 @@ pub(crate) fn placeholder( ) -> AstFragment { fn mac_placeholder() -> Box { Box::new(ast::MacCall { - path: ast::Path { span: DUMMY_SP, segments: ThinVec::new() }, + path: ast::Path::from_ident(Ident::dummy()), args: Box::new(ast::DelimArgs { dspan: ast::tokenstream::DelimSpan::dummy(), delim: Delimiter::Parenthesis, diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index d8c57bfbdb56b..84bf06796d160 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -10,8 +10,8 @@ use std::vec; use rustc_abi::ExternAbi; use rustc_ast as ast; +use rustc_ast::DelimArgs; use rustc_ast::util::parser::{self, ExprPrecedence, Fixity}; -use rustc_ast::{DUMMY_NODE_ID, DelimArgs}; use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent}; use rustc_ast_pretty::pp::{self, BoxMarker, Breaks}; use rustc_ast_pretty::pprust::state::MacHeader; @@ -24,7 +24,7 @@ use rustc_hir::{ PreciseCapturingArg, RangeEnd, Term, TyFieldPath, TyPatKind, }; use rustc_span::source_map::SourceMap; -use rustc_span::{DUMMY_SP, FileName, Ident, Span, Spanned, Symbol, kw, sym}; +use rustc_span::{FileName, Ident, Span, Spanned, Symbol, kw, sym}; pub fn id_to_string(cx: &dyn rustc_hir::intravisit::HirTyCtxt<'_>, hir_id: HirId) -> String { to_string(&cx, |s| s.print_node(cx.hir_node(hir_id))) @@ -131,19 +131,14 @@ impl<'a> State<'a> { fn print_attr_item(&mut self, item: &AttrItem, span: Span) { let ib = self.ibox(0); - let path = ast::Path { - span, - segments: item - .path + let path = ast::Path::from_segments( + item.path .segments .iter() - .map(|i| ast::PathSegment { - ident: Ident { name: *i, span: DUMMY_SP }, - args: None, - id: DUMMY_NODE_ID, - }) + .map(|&s| ast::PathSegment::from_ident(Ident::with_dummy_span(s))) .collect(), - }; + span, + ); match &item.args { AttrArgs::Delimited(DelimArgs { dspan: _, delim, tokens }) => self.print_mac_common( diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 7a6a49f37278a..27a88c4c0fc7d 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -68,7 +68,7 @@ pub(crate) fn parse_cfg(sess: &Session, cfgs: Vec) -> Cfg { if parser.token == token::Eof && parser.dcx().has_errors().is_none() => { - if meta_item.path.segments.len() != 1 { + if meta_item.path.num_segments() != 1 { error!("argument key must be an identifier"); } match &meta_item.kind { diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index b32fa2515238a..cfde4b0a89d9e 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -513,10 +513,10 @@ declare_lint_pass!(LintPassImpl => [LINT_PASS_IMPL_WITHOUT_MACRO]); impl EarlyLintPass for LintPassImpl { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { if let ast::ItemKind::Impl(ast::Impl { of_trait: Some(of_trait), .. }) = &item.kind - && let Some(last) = of_trait.trait_ref.path.segments.last() - && last.ident.name == sym::LintPass + && let Some(last_ident) = of_trait.trait_ref.path.last_ident() + && last_ident.name == sym::LintPass { - let expn_data = of_trait.trait_ref.path.span.ctxt().outer_expn_data(); + let expn_data = of_trait.trait_ref.path.span().ctxt().outer_expn_data(); let call_site = expn_data.call_site; if expn_data.kind != ExpnKind::Macro(MacroKind::Bang, sym::impl_lint_pass) && call_site.ctxt().outer_expn_data().kind @@ -524,7 +524,7 @@ impl EarlyLintPass for LintPassImpl { { cx.emit_span_lint( LINT_PASS_IMPL_WITHOUT_MACRO, - of_trait.trait_ref.path.span, + of_trait.trait_ref.path.span(), LintPassByHand, ); } @@ -690,7 +690,7 @@ declare_lint_pass!(BadUseOfFindAttr => [BAD_USE_OF_FIND_ATTR]); impl EarlyLintPass for BadUseOfFindAttr { fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &rustc_ast::Arm) { fn path_contains_attribute_kind(cx: &EarlyContext<'_>, path: &Path) { - for segment in &path.segments { + for segment in path.iter_segments() { if segment.ident.as_str() == "AttributeKind" { cx.emit_span_lint( BAD_USE_OF_FIND_ATTR, diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index f847d4c6ae904..49e1db306a443 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -759,8 +759,9 @@ where continue; } }; - let tool_ident = if meta_item.path.segments.len() > 1 { - Some(meta_item.path.segments.remove(0).ident) + let tool_ident = if meta_item.path.num_segments() > 1 { + let (segments, _) = meta_item.path.force_general_mut(); + Some(segments.remove(0).ident) } else { None }; @@ -771,8 +772,7 @@ where let (ids, name) = match lint_result { CheckLintNameResult::Ok(ids) => { - let name = - meta_item.path.segments.last().expect("empty lint name").ident.name; + let name = meta_item.path.last_ident().expect("empty lint name").name; (ids, name) } diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 361d1768087ea..ef40c085349d9 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1016,10 +1016,9 @@ impl EarlyLintPass for UnusedParens { let is_last = i == bounds.len() - 1; if let ast::GenericBound::Trait(poly_trait_ref) = &bounds[i] { - let fn_with_explicit_ret_ty = if let [.., segment] = - &*poly_trait_ref.trait_ref.path.segments - && let Some(args) = segment.args.as_ref() - && let ast::GenericArgs::Parenthesized(paren_args) = &**args + let fn_with_explicit_ret_ty = if let Some(segment) = + poly_trait_ref.trait_ref.path.last_segment() + && let Some(ast::GenericArgs::Parenthesized(paren_args)) = segment.args && let ast::FnRetTy::Ty(ret_ty) = &paren_args.output { self.in_no_bounds_pos.insert( @@ -1043,13 +1042,7 @@ impl EarlyLintPass for UnusedParens { let dyn2015_exception = cx.sess().psess.edition == Edition2015 && matches!(ty.kind, ast::TyKind::TraitObject(..)) && i == 0 - && poly_trait_ref - .trait_ref - .path - .segments - .first() - .map(|s| s.ident.name == kw::PathRoot) - .unwrap_or(false); + && poly_trait_ref.trait_ref.path.is_global(); if let ast::Parens::Yes = poly_trait_ref.parens && (is_last || !fn_with_explicit_ret_ty) @@ -1340,7 +1333,7 @@ impl UnusedImportBraces { // Trigger the lint if the nested item is a non-self single item let node_name = match tree.inner.kind { ast::UseTreeKind::Simple(rename) => { - let orig_ident = tree.inner.prefix.segments.last().unwrap().ident; + let orig_ident = tree.inner.prefix.last_ident().unwrap(); if orig_ident.name == kw::SelfLower { return; } diff --git a/compiler/rustc_parse/Cargo.toml b/compiler/rustc_parse/Cargo.toml index 2655e62391ef9..50a4270e751cc 100644 --- a/compiler/rustc_parse/Cargo.toml +++ b/compiler/rustc_parse/Cargo.toml @@ -6,6 +6,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start bitflags = "2.4.1" +itertools = "0.15.0" rustc-literal-escaper = "0.0.8" rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index fd4d012ebe2f9..f56b49893edcb 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -1,13 +1,13 @@ -use std::mem::take; use std::ops::{Deref, DerefMut}; use ast::token::IdentKind; +use itertools::Itertools; use rustc_ast::token::{self, Lit, LitKind, Token, TokenKind}; use rustc_ast::util::parser::{AssocOp, ExprPrecedence}; use rustc_ast::{ self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode, Block, BlockCheckMode, Expr, ExprKind, GenericArg, GenericArgs, Generics, Item, ItemKind, - Param, Pat, PatKind, Path, PathSegment, QSelf, Recovered, Ty, TyKind, + Param, Pat, PatKind, Path, PathSegment, PathSegmentRef, QSelf, Recovered, Ty, TyKind, }; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; @@ -84,7 +84,7 @@ impl RecoverQPath for Ty { Some(Box::new(self.clone())) } fn recovered(qself: Option>, path: ast::Path) -> Self { - Self { span: path.span, kind: TyKind::Path(qself, path), id: ast::DUMMY_NODE_ID } + Self { span: path.span(), kind: TyKind::Path(qself, path), id: ast::DUMMY_NODE_ID } } } @@ -94,7 +94,7 @@ impl RecoverQPath for Pat { self.to_ty() } fn recovered(qself: Option>, path: ast::Path) -> Self { - Self { span: path.span, kind: PatKind::Path(qself, path), id: ast::DUMMY_NODE_ID } + Self { span: path.span(), kind: PatKind::Path(qself, path), id: ast::DUMMY_NODE_ID } } } @@ -104,7 +104,7 @@ impl RecoverQPath for Expr { } fn recovered(qself: Option>, path: ast::Path) -> Self { Self { - span: path.span, + span: path.span(), kind: ExprKind::Path(qself, path), attrs: AttrVec::new(), id: ast::DUMMY_NODE_ID, @@ -764,7 +764,7 @@ impl<'a> Parser<'a> { let mut snapshot = self.create_snapshot_for_diagnostic(); if let [attr] = &expr.attrs[..] && let ast::AttrKind::Normal(attr_kind) = &attr.kind - && let [segment] = &attr_kind.item.path.segments[..] + && let Ok(segment) = attr_kind.item.path.iter_segments().exactly_one() && segment.ident.name == sym::cfg && let Some(args_span) = attr_kind.item.args.span() && let next_attr = match snapshot.parse_attribute(InnerAttrPolicy::Forbidden(None)) @@ -777,7 +777,7 @@ impl<'a> Parser<'a> { } && let ast::AttrKind::Normal(next_attr_kind) = next_attr.kind && let Some(next_attr_args_span) = next_attr_kind.item.args.span() - && let [next_segment] = &next_attr_kind.item.path.segments[..] + && let Ok(next_segment) = next_attr_kind.item.path.iter_segments().exactly_one() && next_segment.ident.name == sym::cfg { let next_expr = match snapshot.parse_expr() { @@ -877,7 +877,7 @@ impl<'a> Parser<'a> { // } debug!(?maybe_struct_name, ?self.token); let mut snapshot = self.create_snapshot_for_diagnostic(); - let path = Path { segments: ThinVec::new(), span: self.prev_token.span.shrink_to_lo() }; + let path = Path::from_segments(ThinVec::new(), self.prev_token.span.shrink_to_lo()); let struct_expr = snapshot.parse_expr_struct(None, path, false); let block_tail = self.parse_block_tail(lo, s, AttemptLocalParseRecovery::No); return Some(match (struct_expr, block_tail) { @@ -1000,7 +1000,7 @@ impl<'a> Parser<'a> { /// up until one of the tokens in 'end' was encountered, and an error was emitted. pub(super) fn check_trailing_angle_brackets( &mut self, - segment: &PathSegment, + segment: PathSegmentRef<'_>, end: &[ExpTokenPair], ) -> Option { if !self.may_recover() { @@ -1031,8 +1031,7 @@ impl<'a> Parser<'a> { // have already been parsed): // // `x.foo::>>(3)` - let parsed_angle_bracket_args = - segment.args.as_ref().is_some_and(|args| args.is_angle_bracketed()); + let parsed_angle_bracket_args = segment.args.is_some_and(|args| args.is_angle_bracketed()); debug!( "check_trailing_angle_brackets: parsed_angle_bracket_args={:?}", @@ -1609,9 +1608,9 @@ impl<'a> Parser<'a> { ) -> PResult<'a, T> { self.expect(exp!(PathSep))?; - let mut path = ast::Path { segments: ThinVec::new(), span: DUMMY_SP }; - self.parse_path_segments(&mut path.segments, T::PATH_STYLE, None)?; - path.span = ty_span.to(self.prev_token.span); + let mut segments = ThinVec::new(); + self.parse_path_segments(&mut segments, T::PATH_STYLE, None)?; + let path = ast::Path::from_segments(segments, ty_span.to(self.prev_token.span)); self.dcx().emit_err(BadQPathStage2 { span: ty_span, @@ -2047,8 +2046,8 @@ impl<'a> Parser<'a> { // cc: https://github.com/rust-lang/rust/pull/146305 if let PatKind::Ref(_, _, _) = &inner_pat.kind && let PatKind::Path(_, path) = &pat.peel_refs().kind - && let [a, ..] = path.segments.as_slice() - && a.ident.name == kw::SelfLower => + && let Some(ident) = path.iter_idents().next() + && ident.name == kw::SelfLower => { let mut inner = inner_pat; let mut span_vec = vec![pat.span]; @@ -2547,51 +2546,55 @@ impl<'a> Parser<'a> { match &mut pat.kind { PatKind::Struct(qself @ None, path, ..) | PatKind::TupleStruct(qself @ None, path, _) - | PatKind::Path(qself @ None, path) => match &first_pat.kind { + | PatKind::Path(qself @ None, path) => match &mut first_pat.kind { PatKind::Ident(_, ident, _) => { - path.segments.insert(0, PathSegment::from_ident(*ident)); - path.span = new_span; + let (segments, span) = path.force_general_mut(); + segments.insert(0, PathSegment::from_ident(*ident)); + *span = new_span; show_sugg = true; first_pat = pat; } PatKind::Path(old_qself, old_path) => { - path.segments = old_path - .segments - .iter() - .cloned() - .chain(take(&mut path.segments)) - .collect(); - path.span = new_span; - *qself = old_qself.clone(); + let (segments, span) = path.force_general_mut(); + match old_path { + Path::Ident { ident, id } => segments.insert( + 0, + PathSegment { ident: *ident, id: *id, args: None }, + ), + Path::NoSpan { segments: old_segments } + | Path::General((old_segments, _)) => { + std::mem::swap(segments, old_segments); + segments.append(old_segments); + } + } + *span = new_span; + *qself = old_qself.take(); first_pat = pat; show_sugg = true; } _ => {} }, PatKind::Ident(BindingMode::NONE, ident, None) => { - match &first_pat.kind { + match &mut first_pat.kind { PatKind::Ident(_, old_ident, _) => { let path = PatKind::Path( None, - Path { - span: new_span, - segments: thin_vec![ + Path::from_segments( + thin_vec![ PathSegment::from_ident(*old_ident), PathSegment::from_ident(*ident), ], - }, + new_span, + ), ); first_pat = self.mk_pat(new_span, path); show_sugg = true; } - PatKind::Path(old_qself, old_path) => { - let mut segments = old_path.segments.clone(); + PatKind::Path(_, old_path) => { + let (segments, span) = old_path.force_general_mut(); segments.push(PathSegment::from_ident(*ident)); - let path = PatKind::Path( - old_qself.clone(), - Path { span: new_span, segments }, - ); - first_pat = self.mk_pat(new_span, path); + *span = new_span; + first_pat.span = new_span; show_sugg = true; } _ => {} @@ -2739,19 +2742,17 @@ impl<'a> Parser<'a> { pub(crate) fn maybe_recover_bounds_doubled_colon(&mut self, ty: &Ty) -> PResult<'a, ()> { let TyKind::Path(qself, path) = &ty.kind else { return Ok(()) }; let qself_position = qself.as_ref().map(|qself| qself.position); - for (i, segments) in path.segments.windows(2).enumerate() { + for (i, (a, b)) in path.iter_segments().tuple_windows().enumerate() { if qself_position.is_some_and(|pos| i < pos) { continue; } - if let [a, b] = segments { - let (a_span, b_span) = (a.span(), b.span()); - let between_span = a_span.shrink_to_hi().to(b_span.shrink_to_lo()); - if self.span_to_snippet(between_span).as_deref() == Ok(":: ") { - return Err(self.dcx().create_err(DoubleColonInBound { - span: path.span.shrink_to_hi(), - between: between_span, - })); - } + let (a_span, b_span) = (a.span(), b.span()); + let between_span = a_span.shrink_to_hi().to(b_span.shrink_to_lo()); + if self.span_to_snippet(between_span).as_deref() == Ok(":: ") { + return Err(self.dcx().create_err(DoubleColonInBound { + span: path.span().shrink_to_hi(), + between: between_span, + })); } } Ok(()) @@ -2939,13 +2940,13 @@ impl<'a> Parser<'a> { let TyKind::Path(_, path) = ty.kind else { return origin_error; }; - let Some(GenericArgs::AngleBracketed(AngleBracketedArgs { span: _, ref args })) = - path.segments[0].args + let Some(GenericArgs::AngleBracketed(AngleBracketedArgs { span: _, args })) = + path.iter_segments().next().unwrap().args else { return origin_error; }; - let path_span = path.span; + let path_span = path.span(); let mut new_error = snapshot.dcx().create_err(FoundPathInGenerics { span: path_span, path: snapshot.span_to_snippet(path_span).unwrap(), diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 9e74a7b1ab38c..c578d733df621 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -5,7 +5,7 @@ use core::ops::{Bound, ControlFlow}; use ast::mut_visit::{self, MutVisitor}; use ast::token::IdentKind; -use ast::{ForLoopKind, MatchKind, Pat, Path, PathSegment, Recovered}; +use ast::{ForLoopKind, MatchKind, Pat, Path, Recovered}; use rustc_ast::token::{self, Delimiter, InvisibleOrigin, MetaVarKind, Token, TokenKind}; use rustc_ast::util::case::Case; use rustc_ast::util::classify; @@ -581,8 +581,10 @@ impl<'a> Parser<'a> { match self.parse_path(PathStyle::Expr) { Ok(path) => { let span_after_type = parser_snapshot_after_type.token.span; - let expr = - mk_expr(self, self.mk_ty(path.span, TyKind::Path(None, path.clone()))); + let expr = mk_expr( + self, + self.mk_ty(path.span(), TyKind::Path(None, path.clone())), + ); let args_span = self.look_ahead(1, |t| t.span).to(span_after_type); match self.token.kind { @@ -1052,18 +1054,11 @@ impl<'a> Parser<'a> { } break; } - ExprKind::Path(None, Path { ref segments, .. }) => { - match &segments[..] { - [PathSegment { ident, args: None, .. }] => { - trailing_dot = None; - fields.insert(start_idx, *ident) - } - _ => { - self.dcx() - .emit_err(crate::diagnostics::InvalidOffsetOf(current.span)); - break; - } - } + ExprKind::Path(None, ref path) + if let Some(ident) = path.as_single_argless_ident() => + { + trailing_dot = None; + fields.insert(start_idx, ident); break; } _ => { @@ -1267,7 +1262,7 @@ impl<'a> Parser<'a> { let fn_span_lo = self.token.span; let mut seg = self.parse_path_segment(PathStyle::Expr, None)?; - self.check_trailing_angle_brackets(&seg, &[exp!(OpenParen)]); + self.check_trailing_angle_brackets(seg.as_ref(), &[exp!(OpenParen)]); self.check_turbofish_missing_angle_brackets(&mut seg); if self.check(exp!(OpenParen)) { @@ -1567,20 +1562,20 @@ impl<'a> Parser<'a> { // MACRO INVOCATION expression if qself.is_some() { self.dcx() - .emit_err(crate::diagnostics::MacroInvocationWithQualifiedPath(path.span)); + .emit_err(crate::diagnostics::MacroInvocationWithQualifiedPath(path.span())); } - let lo = path.span; + let lo = path.span(); let mac = Box::new(MacCall { path, args: self.parse_delim_args()? }); (lo.to(self.prev_token.span), ExprKind::MacCall(mac)) } else if self.check(exp!(OpenBrace)) && let Some(expr) = self.maybe_parse_struct_expr(&qself, &path) { if qself.is_some() { - self.psess.gated_spans.gate(sym::more_qualified_paths, path.span); + self.psess.gated_spans.gate(sym::more_qualified_paths, path.span()); } return expr; } else { - (path.span, ExprKind::Path(qself, path)) + (path.span(), ExprKind::Path(qself, path)) }; let expr = self.mk_expr(span, kind); @@ -1835,8 +1830,7 @@ impl<'a> Parser<'a> { // Recover `break label aaaaa` if self.may_recover() && let ExprKind::Path(None, p) = &expr.kind - && let [segment] = &*p.segments - && let &ast::PathSegment { ident, args: None, .. } = segment + && let Some(ident) = p.as_single_argless_ident() && let Some(next) = self.parse_expr_opt()? { label = Some(self.recover_ident_into_label(ident)); @@ -3618,7 +3612,7 @@ impl<'a> Parser<'a> { self.dcx().emit_err(crate::diagnostics::StructLiteralNotAllowedHere { span: expr.span, sub: crate::diagnostics::StructLiteralNotAllowedHereSugg { - left: path.span.shrink_to_lo(), + left: path.span().shrink_to_lo(), right: expr.span.shrink_to_hi(), }, }); @@ -3743,9 +3737,9 @@ impl<'a> Parser<'a> { Ok(f) => Ok(f), Err(mut e) => { if pth == kw::Async { - async_block_err(&mut e, pth.span); + async_block_err(&mut e, pth.span()); } else { - e.span_label(pth.span, "while parsing this struct"); + e.span_label(pth.span(), "while parsing this struct"); } if let Some((ident, _)) = self.token.ident() @@ -3790,9 +3784,11 @@ impl<'a> Parser<'a> { // make sure the block is at the end by eating a `;`, // we shouldn't report such diagnostic for `let a = foo{return 43;}+bar;` // also skip the suggestion if the span cross macro boundaries - if might_be_stmt && snapshot.eat(exp!(Semi)) && pth.span.eq_ctxt(open_span) + if might_be_stmt + && snapshot.eat(exp!(Semi)) + && pth.span().eq_ctxt(open_span) { - let span = pth.span.between(open_span); + let span = pth.span().between(open_span); e.subdiagnostic(crate::diagnostics::MissingElseInLet { span }); self.restore_snapshot(snapshot); return Err(e); @@ -3841,9 +3837,9 @@ impl<'a> Parser<'a> { } Err(mut e) => { if pth == kw::Async { - async_block_err(&mut e, pth.span); + async_block_err(&mut e, pth.span()); } else { - e.span_label(pth.span, "while parsing this struct"); + e.span_label(pth.span(), "while parsing this struct"); if peek.is_some() { e.span_suggestion( self.prev_token.span.shrink_to_hi(), @@ -3881,7 +3877,7 @@ impl<'a> Parser<'a> { pth: ast::Path, recover: bool, ) -> PResult<'a, Box> { - let lo = pth.span; + let lo = pth.span(); let (fields, base, recovered_async) = self.parse_struct_fields(pth.clone(), recover, exp!(CloseBrace))?; let span = lo.to(self.token.span); @@ -4261,7 +4257,7 @@ impl MutVisitor for CondChecker<'_> { } } - let expr_span = lhs.span.to(path.span); + let expr_span = lhs.span.to(path.span()); if let Some(later_rhs) = find_let_some(rhs) && depth > 0 diff --git a/compiler/rustc_parse/src/parser/function.rs b/compiler/rustc_parse/src/parser/function.rs index d3aba0b98eb8b..54cb6aa4b7f35 100644 --- a/compiler/rustc_parse/src/parser/function.rs +++ b/compiler/rustc_parse/src/parser/function.rs @@ -793,13 +793,13 @@ impl<'a> Parser<'a> { if let Ok(t) = &ty { // Check for trailing angle brackets - if let TyKind::Path(_, Path { segments, .. }) = &t.kind - && let Some(segment) = segments.last() + if let TyKind::Path(_, path) = &t.kind + && let Some(segment) = path.last_segment() && let Some(guar) = this.check_trailing_angle_brackets(segment, &[exp!(CloseParen)]) { return Ok(( - dummy_arg(segment.ident, guar), + dummy_arg(*segment.ident, guar), Trailing::No, UsePreAttrPos::No, )); diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index 5d2aa1bed5b6e..0a0a672b2dd90 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -1,3 +1,4 @@ +use itertools::Itertools; use rustc_ast::{ self as ast, AttrVec, DUMMY_NODE_ID, GenericBounds, GenericParam, GenericParamKind, TyKind, WhereClause, token, @@ -678,12 +679,13 @@ fn suggest_replacing_equality_pred_with_assoc_item_constraint( lhs_ty: ast::Ty, rhs_ty: ast::Ty, ) { - let TyKind::Path(qself, ast::Path { segments, .. }) = lhs_ty.kind else { return }; + let TyKind::Path(qself, path) = lhs_ty.kind else { return }; let mut parts = Vec::new(); let applicability = match qself { // We have something like `Ty::Item = Rhs`. - None if let [self_ty_seg, assoc_item_seg] = &segments[..] + None if let segments_iter = path.iter_segments() + && let Some((self_ty_seg, assoc_item_seg)) = segments_iter.collect_tuple() && self_ty_seg.ident.name != kw::PathRoot => { parts.push(( @@ -692,13 +694,17 @@ fn suggest_replacing_equality_pred_with_assoc_item_constraint( )); Applicability::HasPlaceholders } - Some(qself) if let [assoc_item_seg] = &segments[qself.position..] => { + Some(qself) + if let Ok(assoc_item_seg) = path.iter_segments().skip(qself.position).exactly_one() => + { parts.push((lhs_ty.span.until(qself.ty.span), String::new())); + let mut segments_iter = path.iter_segments().take(qself.position); // We have something like ` as self::Trait>::Item = Rhs`. - if let trait_segs @ [.., final_trait_seg] = &segments[..qself.position] { - parts.push((qself.ty.span.between(trait_segs[0].span()), ": ".into())); - let (span, snippet) = match &final_trait_seg.args { + if let Some(first_trait_seg) = segments_iter.next() { + let final_trait_seg = segments_iter.next_back().unwrap_or(first_trait_seg); + parts.push((qself.ty.span.between(first_trait_seg.span()), ": ".into())); + let (span, snippet) = match final_trait_seg.args { Some(args) => { let ast::GenericArgs::AngleBracketed(args) = args else { return }; let Some(args) = args.args.last() else { return }; diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 9fb16bc3fbf27..f8473771c2886 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -615,11 +615,11 @@ impl<'a> Parser<'a> { Err(mut err) => { // Maybe the user misspelled `macro_rules` (issue #91227) if self.token.is_ident() - && let [segment] = path.segments.as_slice() - && edit_distance("macro_rules", &segment.ident.to_string(), 2).is_some() + && let Some(ident) = path.as_single_argless_ident() + && edit_distance("macro_rules", &ident.to_string(), 2).is_some() { err.span_suggestion_verbose( - path.span, + path.span(), "perhaps you meant to define a macro", "macro_rules", Applicability::MachineApplicable, @@ -943,7 +943,7 @@ impl<'a> Parser<'a> { })) } else { let rename = rename(self)?; - let ident = rename.unwrap_or_else(|| path.segments.last().unwrap().ident); + let ident = rename.unwrap_or_else(|| path.last_ident().unwrap()); ItemKind::Delegation(Box::new(Delegation { id: DUMMY_NODE_ID, @@ -1341,16 +1341,16 @@ impl<'a> Parser<'a> { ) -> PResult<'a, UseTree> { let lo = self.token.span; - let mut prefix = ast::Path { segments: ThinVec::new(), span: lo.shrink_to_lo() }; + let mut prefix; let kind = if self.check(exp!(OpenBrace)) || self.check(exp!(Star)) || self.is_import_coupler() { // `use *;` or `use ::*;` or `use {...};` or `use ::{...};` let mod_sep_ctxt = self.token.span.ctxt(); - if self.eat_path_sep() { - prefix - .segments - .push(PathSegment::path_root(lo.shrink_to_lo().with_ctxt(mod_sep_ctxt))); - } + prefix = if self.eat_path_sep() { + ast::Path::path_root(lo.shrink_to_lo().with_ctxt(mod_sep_ctxt)) + } else { + ast::Path::from_segments(ThinVec::new(), lo.shrink_to_lo()) + }; self.parse_use_tree_glob_or_nested(use_token_span, use_path)? } else { @@ -1358,7 +1358,8 @@ impl<'a> Parser<'a> { prefix = self.parse_path(PathStyle::Mod)?; if self.eat_path_sep() { - let use_path = UsePathList { elements: &prefix.segments, prev: use_path }; + let (segments, _) = prefix.force_general_mut(); + let use_path = UsePathList { elements: &*segments, prev: use_path }; self.parse_use_tree_glob_or_nested(use_token_span, Some(&use_path))? } else { // Recover from using a colon as path separator. @@ -1368,8 +1369,9 @@ impl<'a> Parser<'a> { }); // We parse the rest of the path and append it to the original prefix. - self.parse_path_segments(&mut prefix.segments, PathStyle::Mod, None)?; - prefix.span = lo.to(self.prev_token.span); + let (segments, span) = prefix.force_general_mut(); + self.parse_path_segments(segments, PathStyle::Mod, None)?; + *span = lo.to(self.prev_token.span); } UseTreeKind::Simple(self.parse_rename()?) @@ -2379,8 +2381,8 @@ impl<'a> Parser<'a> { format!("expected `,`, or `}}`, found {}", super::token_descr(&self.token)); // Try to recover extra trailing angle brackets - if let TyKind::Path(_, Path { segments, .. }) = &a_var.ty.kind - && let Some(last_segment) = segments.last() + if let TyKind::Path(_, path) = &a_var.ty.kind + && let Some(last_segment) = path.last_segment() { let guar = self.check_trailing_angle_brackets( last_segment, @@ -2872,11 +2874,11 @@ impl<'a> Parser<'a> { // Check if this looks like `macro_rules!(name) { ... }` // a common mistake when trying to define a macro. if let Some(path) = path - && path.segments.first().is_some_and(|seg| seg.ident.name == sym::macro_rules) + && path.iter_idents().next().is_some_and(|i| i.name == sym::macro_rules) && args.delim == Delimiter::Parenthesis { let replace = - if path.span.hi() + rustc_span::BytePos(1) < open.lo() { "" } else { " " }; + if path.span().hi() + rustc_span::BytePos(1) < open.lo() { "" } else { " " }; err.multipart_suggestion( "to define a macro, remove the parentheses around the macro name", vec![(open, replace.to_string()), (close, String::new())], diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 99d5a1fa6f257..16c6a7c232ba5 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -1535,7 +1535,7 @@ impl<'a> Parser<'a> { let path_str = pprust::path_to_string(&path); self.dcx() - .emit_err(IncorrectVisibilityRestriction { span: path.span, inner_str: path_str }); + .emit_err(IncorrectVisibilityRestriction { span: path.span(), inner_str: path_str }); Ok(()) } @@ -1611,13 +1611,15 @@ impl<'a> Parser<'a> { match restriction_kind { ParsingRestrictionKind::Impl => { self.dcx().emit_err(IncorrectImplRestriction { - span: path.span, + span: path.span(), inner_str: path_str, }); } ParsingRestrictionKind::Mut => { - self.dcx() - .emit_err(IncorrectMutRestriction { span: path.span, inner_str: path_str }); + self.dcx().emit_err(IncorrectMutRestriction { + span: path.span(), + inner_str: path_str, + }); } } Ok((RestrictionKind::Unrestricted, self.token.span.shrink_to_lo(), lo.to(end))) diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index f53cfc792bd69..ac8c0a3f2cc5b 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -1556,11 +1556,11 @@ impl<'a> Parser<'a> { fn parse_pat_struct(&mut self, qself: Option>, path: Path) -> PResult<'a, PatKind> { if qself.is_some() { // Feature gate the use of qualified paths in patterns - self.psess.gated_spans.gate(sym::more_qualified_paths, path.span); + self.psess.gated_spans.gate(sym::more_qualified_paths, path.span()); } self.bump(); let (fields, etc) = self.parse_pat_fields().unwrap_or_else(|mut e| { - e.span_label(path.span, "while parsing the fields for this pattern"); + e.span_label(path.span(), "while parsing the fields for this pattern"); let guar = e.emit_err(); self.recover_stmt(); // When recovering, pretend we had `Foo { .. }`, to avoid cascading errors. @@ -1585,7 +1585,7 @@ impl<'a> Parser<'a> { ) })?; if qself.is_some() { - self.psess.gated_spans.gate(sym::more_qualified_paths, path.span); + self.psess.gated_spans.gate(sym::more_qualified_paths, path.span()); } Ok(PatKind::TupleStruct(qself, path, fields)) } diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 615d1bb221cc9..3e7ccf31932ee 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -1,5 +1,6 @@ use std::mem; +use itertools::Itertools; use rustc_ast::token::{self, MetaVarKind, Token, TokenKind}; use rustc_ast::{ self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AssocItemConstraint, @@ -81,14 +82,15 @@ impl<'a> Parser<'a> { // if any (e.g., `U` in the `::*` examples // above). `path_span` has the span of that path, or an empty // span in the case of something like `::Bar`. - let (mut path, path_span); + let (mut path, segments, span); if self.eat_keyword(exp!(As)) { let path_lo = self.token.span; path = self.parse_path(PathStyle::Type)?; - path_span = path_lo.to(self.prev_token.span); + (segments, span) = path.force_general_mut(); + *span = path_lo.to(self.prev_token.span); } else { - path_span = self.token.span.to(self.token.span); - path = ast::Path { segments: ThinVec::new(), span: path_span }; + path = ast::Path::from_segments(ThinVec::new(), self.token.span); + (segments, span) = path.force_general_mut(); } // See doc comment for `unmatched_angle_bracket_count`. @@ -103,12 +105,13 @@ impl<'a> Parser<'a> { self.expect(exp!(PathSep))?; } - let qself = Box::new(QSelf { ty, path_span, position: path.segments.len() }); + let qself = Box::new(QSelf { ty, path_span: *span, position: segments.len() }); if !is_import_coupler { - self.parse_path_segments(&mut path.segments, style, None)?; + self.parse_path_segments(segments, style, None)?; } - Ok((qself, Path { segments: path.segments, span: lo.to(self.prev_token.span) })) + *span = lo.to(self.prev_token.span); + Ok((qself, path)) } /// Recover from an invalid single colon, when the user likely meant a qualified path. @@ -163,7 +166,7 @@ impl<'a> Parser<'a> { style: PathStyle, ty_generics: Option<&Generics>, ) -> PResult<'a, Path> { - let reject_generics_if_mod_style = |parser: &Parser<'_>, path: Path| { + let reject_generics_if_mod_style = |parser: &Parser<'_>, mut path: Path| { // Ensure generic arguments don't end up in attribute paths, such as: // // macro_rules! m { @@ -172,25 +175,20 @@ impl<'a> Parser<'a> { // // m!(inline); //~ ERROR: unexpected generic arguments in path // - if style == PathStyle::Mod && path.segments.iter().any(|segment| segment.args.is_some()) - { - let span = path - .segments + if style == PathStyle::Mod && path.iter_segments().any(|s| s.args.is_some()) { + let segments = path.force_general_mut().0; + let span = segments .iter() .filter_map(|segment| segment.args.as_ref()) .map(|arg| arg.span()) .collect::>(); parser.dcx().emit_err(diagnostics::GenericsInPath { span }); // Ignore these arguments to prevent unexpected behaviors. - let segments = path - .segments - .iter() - .map(|segment| PathSegment { ident: segment.ident, id: segment.id, args: None }) - .collect(); - Path { segments, ..path } - } else { - path + for segment in segments { + segment.args = None; + } } + path }; if let Some(path) = @@ -214,7 +212,23 @@ impl<'a> Parser<'a> { segments.push(PathSegment::path_root(lo.shrink_to_lo().with_ctxt(mod_sep_ctxt))); } self.parse_path_segments(&mut segments, style, ty_generics)?; - Ok(Path { segments, span: lo.to(self.prev_token.span) }) + // Fast-paths to avoid redundant span construction in `Path::from_segments`, and to avoid + // the cost of combining spans too early. + let hi = self.prev_token.span; + if let [segment] = segments.as_slice() + && segment.args.is_none() + && segment.ident.span == lo + && lo == hi + { + return Ok(Path::Ident { ident: segment.ident, id: segment.id }); + } + let first_span = segments[0].ident.span; + let last = segments.last().unwrap(); + let last_span = last.args.as_deref().map(|a| a.span()).unwrap_or(last.ident.span); + if first_span == lo && last_span == hi { + return Ok(Path::NoSpan { segments }); + } + Ok(Path::from_segments(segments, lo.to(hi))) } pub(super) fn parse_path_segments( @@ -242,7 +256,7 @@ impl<'a> Parser<'a> { // `PathStyle::Expr` is only provided at the root invocation and never in // `parse_path_segment` to recurse and therefore can be checked to maintain // this invariant. - self.check_trailing_angle_brackets(&segment, &[exp!(PathSep)]); + self.check_trailing_angle_brackets(segment.as_ref(), &[exp!(PathSep)]); } segments.push(segment); @@ -852,12 +866,7 @@ impl<'a> Parser<'a> { } // We can only resolve single-segment paths at the moment, because multi-segment paths // require type-checking: see `visit_generic_arg` in `src/librustc_resolve/late.rs`. - ast::ExprKind::Path(None, path) - if let [segment] = path.segments.as_slice() - && segment.args.is_none() => - { - true - } + ast::ExprKind::Path(None, path) if path.is_single_argless_ident() => true, ast::ExprKind::ConstBlock(_) => { self.psess.gated_spans.gate(sym::gca_min_const_items, expr.span); true @@ -981,15 +990,15 @@ impl<'a> Parser<'a> { if let GenericArg::Type(ty) = gen_arg { if let ast::TyKind::Path(qself, path) = &ty.kind && qself.is_none() - && let [seg] = path.segments.as_slice() + && let Ok(seg) = path.iter_segments().exactly_one() { - return Ok((false, seg.ident, seg.args.as_deref().cloned())); + return Ok((false, *seg.ident, seg.args.cloned())); } else if let ast::TyKind::TraitObject(bounds, ast::TraitObjectSyntax::None) = &ty.kind && let [ast::GenericBound::Trait(trait_ref)] = bounds.as_slice() && trait_ref.modifiers == ast::TraitBoundModifiers::NONE - && let [seg] = trait_ref.trait_ref.path.segments.as_slice() + && let Ok(seg) = trait_ref.trait_ref.path.iter_segments().exactly_one() { - return Ok((true, seg.ident, seg.args.as_deref().cloned())); + return Ok((true, *seg.ident, seg.args.cloned())); } } Err(()) diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index d59ef93eded21..1a40442fc793b 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -3,6 +3,7 @@ use std::mem; use std::ops::Bound; use ast::Label; +use itertools::Itertools; use rustc_ast::token::{self, Delimiter, InvisibleOrigin, MetaVarKind, TokenKind}; use rustc_ast::tokenstream::TokenTree; use rustc_ast::util::classify::{self, TrailingBrace}; @@ -630,7 +631,7 @@ impl<'a> Parser<'a> { (token::CloseBrace, StmtKind::Expr(expr)) if let ExprKind::Struct(expr) = &expr.kind && let None = expr.qself - && expr.path.segments.len() == 1 => + && expr.path.num_segments() == 1 => { // This is specific to "mistyped `if` condition followed by empty body" // @@ -1030,8 +1031,8 @@ impl<'a> Parser<'a> { } if self.token == token::Colon - && let ExprKind::Path(None, ast::Path { segments, .. }) = &expr.kind - && let [segment] = segments.as_slice() + && let ExprKind::Path(None, path) = &expr.kind + && let Some(ident) = path.iter_idents().exactly_one().ok() && self.look_ahead(1, |t| { t.is_metavar_block() || t.kind == token::OpenBrace @@ -1043,8 +1044,8 @@ impl<'a> Parser<'a> { let snapshot = self.create_snapshot_for_diagnostic(); let label = Label { ident: Ident::from_str_and_span( - &format!("'{}", segment.ident), - segment.ident.span, + &format!("'{}", ident), + ident.span, ), }; match self.parse_expr_labeled(label, false) { diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 29b83e006d195..78e5c0804e5a9 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -1370,7 +1370,7 @@ impl<'a> Parser<'a> { if let TyKind::Path(_, path) = &ty.peel_refs().kind => { ( - ty.span.until(path.span), + ty.span.until(path.span()), "consider removing the indirection", "", path, @@ -1445,9 +1445,8 @@ impl<'a> Parser<'a> { match self.parse_fn_decl(&mode, AllowPlus::No, RecoverReturnSign::OnlyFatArrow) { Ok(decl) => { self.dcx().emit_err(ExpectedFnPathFoundFnKeyword { fn_token_span }); - Some(ast::Path { - span: fn_token_span.to(self.prev_token.span), - segments: thin_vec![ast::PathSegment { + Some(ast::Path::from_segments( + thin_vec![ast::PathSegment { ident: Ident::new(sym::Fn, fn_token_span), id: DUMMY_NODE_ID, args: Some(Box::new(ast::GenericArgs::Parenthesized( @@ -1459,7 +1458,8 @@ impl<'a> Parser<'a> { } ))), }], - }) + fn_token_span.to(self.prev_token.span), + )) } Err(diag) => { diag.cancel(); @@ -1498,7 +1498,8 @@ impl<'a> Parser<'a> { fn_path: &mut ast::Path, lifetime_defs: &mut ThinVec, ) -> PResult<'a, ()> { - let fn_path_segment = fn_path.segments.last_mut().unwrap(); + let (fn_path_segments, _) = fn_path.force_general_mut(); + let fn_path_segment = fn_path_segments.last_mut().unwrap(); let generic_args = if let Some(p_args) = &fn_path_segment.args { *p_args.clone() } else { @@ -1610,7 +1611,7 @@ impl<'a> Parser<'a> { "for<{}> ", lifetimes.iter().map(|lt| lt.ident.as_str()).intersperse(", ").collect::(), ); - let before_fn_path = fn_path.span.shrink_to_lo(); + let before_fn_path = fn_path.span().shrink_to_lo(); self.dcx() .struct_span_err(generic_args_span, "`Fn` traits cannot take lifetime parameters") .with_multipart_suggestion( diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index d8524efb35bc2..030e73f0ac0cf 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -54,15 +54,23 @@ impl Node { /// There are some types in the AST and HIR tree that the visitors do not have /// a `visit_*` method for, and so we cannot measure these, which is /// unfortunate. +#[derive(Default)] struct StatCollector<'k> { tcx: Option>, nodes: FxHashMap<&'static str, Node>, seen: FxHashSet, + paths: PathStats, +} + +#[derive(Default)] +struct PathStats { + idents: usize, + no_spans: usize, + generals: [usize; 5], } pub fn print_hir_stats(tcx: TyCtxt<'_>) { - let mut collector = - StatCollector { tcx: Some(tcx), nodes: FxHashMap::default(), seen: FxHashSet::default() }; + let mut collector = StatCollector { tcx: Some(tcx), ..StatCollector::default() }; tcx.hir_walk_toplevel_module(&mut collector); tcx.hir_walk_attributes(&mut collector); collector.print(tcx, "HIR STATS", "hir-stats"); @@ -71,8 +79,7 @@ pub fn print_hir_stats(tcx: TyCtxt<'_>) { pub fn print_ast_stats(tcx: TyCtxt<'_>, krate: &ast::Crate) { use rustc_ast::visit::Visitor; - let mut collector = - StatCollector { tcx: None, nodes: FxHashMap::default(), seen: FxHashSet::default() }; + let mut collector = StatCollector::default(); collector.visit_crate(krate); collector.print(tcx, "POST EXPANSION AST STATS", "ast-stats"); } @@ -194,6 +201,30 @@ impl<'k> StatCollector<'k> { "", usize_with_underscores(total_count), ); + _ = writeln!(s, "{prefix} {}", "-".repeat(banner_w)); + if self.tcx.is_none() { + _ = writeln!( + s, + "{prefix} - Path Ident: {} Other segs 0: {} 1: {} 2: {} 3: {} 4+: {} NoSpan: {}", + self.paths.idents, + self.paths.generals[0], + self.paths.generals[1], + self.paths.generals[2], + self.paths.generals[3], + self.paths.generals[4], + self.paths.no_spans, + ); + } else { + _ = writeln!( + s, + "{prefix} - Path segments 0: {} 1: {} 2: {} 3: {} 4+: {}", + self.paths.generals[0], + self.paths.generals[1], + self.paths.generals[2], + self.paths.generals[3], + self.paths.generals[4], + ); + } _ = writeln!(s, "{prefix} {}", "=".repeat(banner_w)); eprint!("{s}"); } @@ -457,6 +488,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { // HIR, which causes `p` to be double- or triple-counted. Instead just // walk the path internals (i.e. the segments) directly. let hir::Path { span: _, res: _, segments } = *tree.prefix; + self.paths.generals[segments.len().min(4)] += 1; ast_visit::walk_list!(self, visit_path_segment, segments); match tree.kind { hir::UseKind::Single(_) | hir::UseKind::Glob => {} @@ -537,6 +569,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { fn visit_path(&mut self, path: &hir::Path<'v>, _id: HirId) { self.record("Path", None, path); + self.paths.generals[path.segments.len().min(4)] += 1; hir_visit::walk_path(self, path) } @@ -766,6 +799,22 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { ast_visit::walk_path_segment(self, path_segment) } + fn visit_path(&mut self, path: &'v ast::Path) { + match path { + ast::Path::Ident { .. } => self.paths.idents += 1, + ast::Path::NoSpan { segments } | ast::Path::General((segments, _)) => { + let len = segments.len().min(4); + self.paths.generals[len] += 1; + if let ast::Path::General(boxed) = path { + self.record("Path::General payload", None, boxed.as_ref()); + } else { + self.paths.no_spans += 1; + } + } + } + ast_visit::walk_path(self, path); + } + // `GenericArgs` has one inline use (in `ast::AssocItemConstraint::gen_args`) and one // non-inline use (in `ast::PathSegment::args`). The latter case is more // common, so we implement `visit_generic_args` and tolerate the double diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 09d7f5dd11006..f039c575ca4eb 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -253,13 +253,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // paths" right now, so on 2018 edition we only allow module-relative paths for now. // On 2015 edition visibilities are resolved as crate-relative by default, // so we are prepending a root segment if necessary. - let ident = path.segments.get(0).expect("empty path in visibility").ident; + let ident = path.iter_idents().next().expect("empty path in visibility"); let crate_root = if ident.is_path_segment_keyword() { None } else if ident.span.is_rust_2015() { Some(Segment::from_ident(Ident::new( kw::PathRoot, - path.span.shrink_to_lo().with_ctxt(ident.span.ctxt()), + path.span().shrink_to_lo().with_ctxt(ident.span.ctxt()), ))) } else { return Err(VisResolutionError::Relative2018( @@ -269,11 +269,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }; let segments = crate_root .into_iter() - .chain(path.segments.iter().map(|seg| seg.into())) + .chain(path.iter_segments().map(|seg| seg.into())) .collect::>(); let expected_found_error = |res| { Err(VisResolutionError::ExpectedFound( - path.span, + path.span(), Segment::names_to_string(&segments), res, )) @@ -282,7 +282,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { &segments, None, parent_scope, - finalize.then(|| Finalize::new(id, path.span)), + finalize.then(|| Finalize::new(id, path.span())), None, None, ) { @@ -305,7 +305,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } Ok(vis.expect_local()) } else { - Err(VisResolutionError::AncestorOnly(path.span)) + Err(VisResolutionError::AncestorOnly(path.span())) } } } @@ -313,7 +313,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { expected_found_error(res) } } - PathResult::Module(..) => Err(VisResolutionError::ModuleOnly(path.span)), + PathResult::Module(..) => Err(VisResolutionError::ModuleOnly(path.span())), PathResult::NonModule(partial_res) => { expected_found_error(partial_res.expect_full_res()) } @@ -327,7 +327,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { message, }) } - PathResult::Indeterminate => Err(VisResolutionError::Indeterminate(path.span)), + PathResult::Indeterminate => { + Err(VisResolutionError::Indeterminate(path.span())) + } } } } @@ -615,7 +617,7 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { let mut prefix_iter = parent_prefix .iter() .cloned() - .chain(use_tree.prefix.segments.iter().map(|seg| seg.into())) + .chain(use_tree.prefix.iter_segments().map(|seg| seg.into())) .peekable(); // On 2015 edition imports are resolved as crate-relative by default, @@ -636,7 +638,7 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { .map(|ctxt| { Segment::from_ident(Ident::new( kw::PathRoot, - use_tree.prefix.span.shrink_to_lo().with_ctxt(ctxt), + use_tree.prefix.span().shrink_to_lo().with_ctxt(ctxt), )) }); @@ -757,9 +759,10 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { } } ast::UseTreeKind::Nested { ref items, .. } => { + let use_tree_span = use_tree.span(); for tree in items { let id = tree.id; - let feed = self.create_def(id, None, DefKind::Use, use_tree.span()); + let feed = self.create_def(id, None, DefKind::Use, use_tree_span); self.build_reduced_graph_for_use_tree( item, &tree.inner, diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 91e6188a8c63c..d096ef0ce5dd6 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -359,7 +359,7 @@ fn calc_unused_spans( } else { // Delete everything until the next import, to delete the trailing commas let inner = &nested[pos + 1].inner; - use_tree.inner.prefix.span.to(inner.prefix.span.shrink_to_lo()) + use_tree.inner.prefix.span().to(inner.prefix.span().shrink_to_lo()) }; // Try to collapse adjacent spans into a single one. This prevents all cases of @@ -400,7 +400,7 @@ fn calc_unused_spans( .unwrap() .inner .prefix - .span + .span() .shrink_to_lo()), ); // Right brace, from the end of the last item to the end of the nested group. diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index f3329c3d0398e..0cc4ca7e21b95 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -562,7 +562,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { if rustc_attr_parsing::is_builtin_attr(&normal.item) { self.r .builtin_attrs - .push((normal.item.path.segments[0].ident, self.parent_scope)); + .push((*normal.item.path.iter_idents().next().unwrap(), self.parent_scope)); } } AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace(_)) => {} diff --git a/compiler/rustc_resolve/src/diagnostics/impls.rs b/compiler/rustc_resolve/src/diagnostics/impls.rs index 744e7c22265ea..184b71c7c8fd1 100644 --- a/compiler/rustc_resolve/src/diagnostics/impls.rs +++ b/compiler/rustc_resolve/src/diagnostics/impls.rs @@ -1447,7 +1447,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { VisResolutionError::Relative2018(span, path) => { self.dcx().create_err(diagnostics::Relative2018 { span, - path_span: path.span, + path_span: path.span(), // intentionally converting to String, as the text would also be used as // in suggestion context path_str: pprust::path_to_string(&path), @@ -1756,7 +1756,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { segms.append(&mut path_segments.clone()); segms.push(ast::PathSegment::from_ident(ident.orig(orig_ident_span))); - let path = Path { span: name_binding.span, segments: segms }; + let path = Path::from_segments(segms, name_binding.span); if child_accessible // Remove invisible match if exists @@ -1769,7 +1769,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let is_stable = if is_stable && let Some(did) = did - && this.is_stable(did, path.span) + && this.is_stable(did, path.span()) { true } else { @@ -2564,11 +2564,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // doesn't start with `Crate`, prepend it (edition 2015 paths are relative // to the crate root without an explicit `crate::` prefix). let candidate_names = { - let filtered_segments: Vec<_> = - path.segments.iter().filter(|segment| segment.ident.name != kw::PathRoot).collect(); - let mut candidate_names: Vec = - filtered_segments.iter().map(|segment| segment.ident.name).collect(); + path.iter_idents().map(|i| i.name).filter(|&n| n != kw::PathRoot).collect(); if candidate_names.first() != Some(&kw::Crate) { candidate_names.insert(0, kw::Crate); } @@ -2614,11 +2611,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } // Only apply if the result is strictly shorter than the original path. - if new_segments.len() >= path.segments.len() { + if new_segments.len() >= path.num_segments() { return; } - *path = Path { span: path.span, segments: new_segments }; + *path = Path::from_segments(new_segments, path.span()); } fn report_privacy_error(&mut self, privacy_error: &PrivacyError<'ra>) { @@ -2816,18 +2813,18 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { module_path.map(|module_path| { // `import.module_path` is relative to the import's module, not to the // failing use site. - let mut path = Path { - span: ident.span, - segments: module_path + let mut path = Path::from_segments( + module_path .into_iter() .chain(std::iter::once(ident.name)) .map(|name| { ast::PathSegment::from_ident(Ident::with_dummy_span(name)) }) .collect(), - }; + ident.span, + ); self.shorten_import_path(res_def_id, &mut path, parent_scope.module); - path.segments.iter().map(|seg| seg.ident).collect() + path.iter_idents().copied().collect() }) } else { // Don't include `{{root}}` in suggestions - it's an internal symbol @@ -2939,7 +2936,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let ast::ExprKind::Struct(struct_expr) = &expr.kind else { return }; // We don't have to handle type-relative paths because they're forbidden in ADT // expressions, but that would change with `#[feature(more_qualified_paths)]`. - let Some(segment) = struct_expr.path.segments.last() else { return }; + let Some(segment) = struct_expr.path.last_segment() else { return }; let Some(partial_res) = self.partial_res_map.get(&segment.id) else { return }; let Some(Res::Def(_, def_id)) = partial_res.full_res() else { return; @@ -3101,23 +3098,21 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let is_mod = |res| matches!(res, Res::Def(DefKind::Mod, _)); let mut candidates = self.lookup_import_candidates(ident, TypeNS, parent_scope, is_mod); candidates - .sort_by_cached_key(|c| (c.path.segments.len(), pprust::path_to_string(&c.path))); - if let Some(candidate) = candidates.get(0) { - let path = { - // remove the possible common prefix of the path - let len = candidate.path.segments.len(); - let start_index = (0..=failed_segment_idx.min(len - 1)) - .find(|&i| path[i].ident.name != candidate.path.segments[i].ident.name) - .unwrap_or_default(); - let segments = - (start_index..len).map(|s| candidate.path.segments[s].clone()).collect(); - Path { segments, span: Span::default() } - }; + .sort_by_cached_key(|c| (c.path.num_segments(), pprust::path_to_string(&c.path))); + if let Some(candidate) = candidates.first_mut() { + // remove the possible common prefix of the path + let (segments, span) = candidate.path.force_general_mut(); + *span = Span::default(); + let len = segments.len(); + let start_index = (0..=failed_segment_idx.min(len - 1)) + .find(|&i| path[i].ident.name != segments[i].ident.name) + .unwrap_or_default(); + segments.drain(..start_index); ( message, String::from("unresolved import"), Some(( - vec![(ident.span, pprust::path_to_string(&path))], + vec![(ident.span, pprust::path_to_string(&candidate.path))], String::from("a similar path exists"), Applicability::MaybeIncorrect, )), diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 6a5a73715c840..1016d38748779 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -581,7 +581,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if ext.helper_attrs.contains(&ident.name) { let decl = self.arenas.new_pub_def_decl( Res::NonMacroAttr(NonMacroAttrKind::DeriveHelperCompat), - derive.span, + derive.span(), LocalExpnId::ROOT, ); result = Ok(decl); diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index ab595de3e39e7..3769c0b056057 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -534,15 +534,12 @@ impl PathSource<'_, '_, '_> { // this is not precise but usually more helpful than just "value". Some(ExprKind::Call(call_expr, _)) => match &call_expr.kind { // the case of `::some_crate()` - ExprKind::Path(_, path) - if let [segment, _] = path.segments.as_slice() - && segment.ident.name == kw::PathRoot => - { + ExprKind::Path(_, path) if path.num_segments() == 2 && path.is_global() => { "external crate" } ExprKind::Path(_, path) - if let Some(segment) = path.segments.last() - && let Some(c) = segment.ident.to_string().chars().next() + if let Some(ident) = path.last_ident() + && let Some(c) = ident.to_string().chars().next() && c.is_uppercase() => { "function, tuple struct or tuple variant" @@ -938,9 +935,8 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc // If we have a path that ends with `(..)`, then it must be // return type notation. Resolve that path in the *value* // namespace. - let source = if let Some(seg) = path.segments.last() - && let Some(args) = &seg.args - && matches!(**args, GenericArgs::ParenthesizedElided(..)) + let source = if let Some(seg) = path.last_segment() + && matches!(seg.args, Some(GenericArgs::ParenthesizedElided(..))) { PathSource::ReturnTypeNotation } else { @@ -958,7 +954,7 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc // This path is actually a bare trait object. In case of a bare `Fn`-trait // object with anonymous lifetimes, we need this rib to correctly place the // synthetic lifetimes. - let span = ty.span.shrink_to_lo().to(path.span.shrink_to_lo()); + let span = ty.span.shrink_to_lo().to(path.span().shrink_to_lo()); self.with_generic_param_rib( &[], RibKind::Normal, @@ -1069,7 +1065,7 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc } fn visit_poly_trait_ref(&mut self, tref: &'ast PolyTraitRef) { - let span = tref.span.shrink_to_lo().to(tref.trait_ref.path.span.shrink_to_lo()); + let span = tref.span.shrink_to_lo().to(tref.trait_ref.path.span().shrink_to_lo()); self.with_generic_param_rib( &tref.bound_generic_params, RibKind::Normal, @@ -1256,9 +1252,9 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc // it doesn't really matter which we try resolving first, but just like // `Ty::Param` we just fall back to the value namespace only if it's missing // from the type namespace. - let mut check_ns = |ns| { - self.maybe_resolve_ident_in_lexical_scope(path.segments[0].ident, ns).is_some() - }; + let ident = *path.iter_idents().next().unwrap(); + let mut check_ns = + |ns| self.maybe_resolve_ident_in_lexical_scope(ident, ns).is_some(); // Like `Ty::Param`, we try resolving this as both a const and a type. if !check_ns(TypeNS) && check_ns(ValueNS) { self.smart_resolve_path( @@ -2818,14 +2814,14 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } fn future_proof_import(&mut self, use_tree: &UseTree) { - if let [segment, rest @ ..] = use_tree.prefix.segments.as_slice() { - let ident = segment.ident; + let mut ident_iter = use_tree.prefix.iter_idents(); + if let Some(&ident) = ident_iter.next() { if ident.is_path_segment_keyword() || ident.span.is_rust_2015() { return; } let nss = match use_tree.kind { - UseTreeKind::Simple(..) if rest.is_empty() => &[TypeNS, ValueNS][..], + UseTreeKind::Simple(..) if ident_iter.next().is_none() => &[TypeNS, ValueNS][..], _ => &[TypeNS], }; let report_error = |this: &Self, ns| { @@ -3106,7 +3102,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } ItemKind::Delegation(delegation) => { - let span = delegation.path.segments.last().unwrap().ident.span; + let span = delegation.last_segment_span(); self.with_generic_param_rib( &[], RibKind::Item(HasGenericParams::Yes(span), def_kind), @@ -3464,7 +3460,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { RibKind::AssocItem, item.id, LifetimeBinderKind::Function, - delegation.path.segments.last().unwrap().ident.span, + delegation.last_segment_span(), |this| this.resolve_delegation(delegation, item.id, false), ); } @@ -3495,7 +3491,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { &None, &path, PathSource::Trait(AliasPossibility::No), - Finalize::new(trait_ref.ref_id, trait_ref.path.span), + Finalize::new(trait_ref.ref_id, trait_ref.path.span()), RecordPartialRes::Yes, None, ); @@ -3772,7 +3768,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { RibKind::AssocItem, item.id, LifetimeBinderKind::Function, - delegation.path.segments.last().unwrap().ident.span, + delegation.last_segment_span(), |this| { if !is_in_trait_impl { this.fill_delegation_inherent_fn_map( @@ -3903,7 +3899,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { match seen_trait_items.entry(id_in_trait) { Entry::Occupied(entry) => { let trait_span = decl.parent_module.unwrap().span.shrink_to_lo(); - let impl_span = self.current_trait_ref.as_ref().unwrap().1.path.span; + let impl_span = self.current_trait_ref.as_ref().unwrap().1.path.span(); self.report_error( span, ResolutionError::TraitImplDuplicate { @@ -3945,7 +3941,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { }; let trait_path = path_names_to_string(path); let trait_span = decl.parent_module.unwrap().span.shrink_to_lo(); - let impl_span = self.current_trait_ref.as_ref().unwrap().1.path.span; + let impl_span = self.current_trait_ref.as_ref().unwrap().1.path.span(); self.report_error( span, ResolutionError::TraitImplMismatch { @@ -4041,7 +4037,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { }) .unwrap_or_else(|| { DelegationResolution::Error(self.r.tcx.dcx().span_delayed_bug( - delegation.path.span, + delegation.path.span(), format!("bad resolution for delegation {item_id:?}"), )) }); @@ -4647,7 +4643,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { qself, &Segment::from_path(path), source, - Finalize::new(id, path.span), + Finalize::new(id, path.span()), RecordPartialRes::Yes, None, ); diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index b2061cd9d9d91..ba82ba0b6d02b 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -2,13 +2,12 @@ use std::borrow::Cow; use std::iter; -use std::ops::Deref; use rustc_ast::visit::{FnCtxt, FnKind, LifetimeCtxt, Visitor, walk_ty}; use rustc_ast::{ self as ast, AngleBracketedArg, AssocItemKind, DUMMY_NODE_ID, Expr, ExprKind, GenericArg, GenericArgs, GenericParam, GenericParamKind, Item, ItemKind, MethodCall, NodeId, Path, - PathSegment, Ty, TyKind, + PathSegment, PathSegmentRef, Ty, TyKind, }; use rustc_ast_pretty::pprust::{path_to_string, where_bound_predicate_to_string}; use rustc_attr_ir::diagnostic::{CustomDiagnostic, FormatArgs}; @@ -82,10 +81,10 @@ fn is_self_value(path: &[Segment], namespace: Namespace) -> bool { fn path_to_string_without_assoc_item_bindings(path: &Path) -> String { let mut path = path.clone(); - for segment in &mut path.segments { + let segments = path.force_general_mut().0; + for segment in segments { let mut remove_args = false; - if let Some(args) = segment.args.as_deref_mut() - && let ast::GenericArgs::AngleBracketed(angle_bracketed) = args + if let Some(ast::GenericArgs::AngleBracketed(angle_bracketed)) = segment.args.as_deref_mut() { angle_bracketed.args.retain(|arg| matches!(arg, ast::AngleBracketedArg::Arg(_))); remove_args = angle_bracketed.args.is_empty(); @@ -102,11 +101,8 @@ fn import_candidate_to_enum_paths(suggestion: &ImportSuggestion) -> (String, Str let variant_path = &suggestion.path; let variant_path_string = path_names_to_string(variant_path); - let path_len = suggestion.path.segments.len(); - let enum_path = ast::Path { - span: suggestion.path.span, - segments: suggestion.path.segments[0..path_len - 1].iter().cloned().collect(), - }; + let mut enum_path = suggestion.path.clone(); + let _ = enum_path.force_general_mut().0.pop(); let enum_path_string = path_names_to_string(&enum_path); (variant_path_string, enum_path_string) @@ -246,7 +242,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { if poly_trait_ref.modifiers != ast::TraitBoundModifiers::NONE { return; } - let Some(trait_seg) = poly_trait_ref.trait_ref.path.segments.last() else { + let Some(trait_seg) = poly_trait_ref.trait_ref.path.last_segment() else { return; }; let Some(partial_res) = this.r.partial_res_map.get(&trait_seg.id) else { @@ -291,8 +287,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { let ast::TyKind::Path(None, bounded_path) = &where_bound.bounded_ty.kind else { continue; }; - let [ast::PathSegment { ident, args: None, .. }] = &bounded_path.segments[..] - else { + let Some(name) = bounded_path.as_single_argless_name() else { continue; }; @@ -307,7 +302,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { for bound in &where_bound.bounds { let ast::GenericBound::Trait(poly_trait_ref) = bound else { continue }; - record_bound(this, ident.name, poly_trait_ref); + record_bound(this, name, poly_trait_ref); } } }; @@ -900,18 +895,19 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { res: Option, qself: Option<&QSelf>, ) { + use itertools::Itertools; if let Some(Res::Def(DefKind::AssocFn, _)) = res && let PathSource::TraitItem(TypeNS, _) = source && let None = following_seg && let Some(qself) = qself && let TyKind::Path(None, ty_path) = &qself.ty.kind - && ty_path.segments.len() == 1 + && let Ok(ident) = ty_path.iter_idents().exactly_one() && self.diag_metadata.current_where_predicate.is_some() { err.span_suggestion_verbose( span, "you might have meant to use the return type notation syntax", - format!("{}::{}(..)", ty_path.segments[0].ident, path[path.len() - 1].ident), + format!("{ident}::{}(..)", path[path.len() - 1].ident), Applicability::MaybeIncorrect, ); } @@ -928,13 +924,8 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { let TyKind::Path(_, path) = &ty.kind else { return; }; - for segment in &path.segments { - let Some(params) = &segment.args else { - continue; - }; - let ast::GenericArgs::AngleBracketed(params) = params.deref() else { - continue; - }; + for segment in path.iter_segments() { + let Some(ast::GenericArgs::AngleBracketed(params)) = segment.args else { continue }; for param in ¶ms.args { let ast::AngleBracketedArg::Constraint(constraint) = param else { continue; @@ -1760,6 +1751,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } fn suggest_at_operator_in_slice_pat_with_range(&self, err: &mut Diag<'_>, path: &[Segment]) { + use itertools::Itertools; let Some(pat) = self.diag_metadata.current_pat else { return }; let (bound, side, range) = match &pat.kind { ast::PatKind::Range(Some(bound), None, range) => (bound, Side::Start, range), @@ -1767,20 +1759,20 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { _ => return, }; if let ExprKind::Path(None, range_path) = &bound.kind - && let [segment] = &range_path.segments[..] + && let Ok(&ident) = range_path.iter_idents().exactly_one() && let [s] = path - && segment.ident == s.ident - && segment.ident.span.eq_ctxt(range.span) + && ident == s.ident + && ident.span.eq_ctxt(range.span) { // We've encountered `[first, rest..]` (#88404) or `[first, ..rest]` (#120591) // where the user might have meant `[first, rest @ ..]`. let (span, snippet) = match side { - Side::Start => (segment.ident.span.between(range.span), " @ ".into()), - Side::End => (range.span.to(segment.ident.span), format!("{} @ ..", segment.ident)), + Side::Start => (ident.span.between(range.span), " @ ".into()), + Side::End => (range.span.to(ident.span), format!("{ident} @ ..")), }; err.subdiagnostic(diagnostics::UnexpectedResUseAtOpInSlicePatWithRangeSugg { span, - ident: segment.ident, + ident, snippet, }); } @@ -1882,16 +1874,15 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { && let PathResult::Module(ModuleOrUniformRoot::Module(module)) = self.resolve_path(&Segment::from_path(self_ty_path), Some(TypeNS), None, source) && module.def_kind() == Some(DefKind::Trait) - && trait_ref.path.span == span + && trait_ref.path.span() == span && let PathSource::Trait(_) = source && let Some(Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, _)) = res && let Ok(self_ty_str) = self.r.tcx.sess.source_map().span_to_snippet(self_ty.span) - && let Ok(trait_ref_str) = - self.r.tcx.sess.source_map().span_to_snippet(trait_ref.path.span) + && let Ok(trait_ref_str) = self.r.tcx.sess.source_map().span_to_snippet(span) { err.multipart_suggestion( "`impl` items mention the trait being implemented first and the type it is being implemented for second", - vec![(trait_ref.path.span, self_ty_str), (self_ty.span, trait_ref_str)], + vec![(span, self_ty_str), (self_ty.span, trait_ref_str)], Applicability::MaybeIncorrect, ); } @@ -2053,6 +2044,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { /// Given `where ::Baz: String`, suggest `where T: Bar`. fn restrict_assoc_type_in_where_clause(&self, span: Span, err: &mut Diag<'_>) -> bool { + use itertools::Itertools; // Detect that we are actually in a `where` predicate. let Some(ast::WherePredicate { kind: @@ -2088,13 +2080,14 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { if !matches!(partial_res.full_res(), Some(Res::Def(DefKind::TyParam, _))) { return false; } - let ([ast::PathSegment { args: None, .. }], [ast::GenericBound::Trait(poly_trait_ref)]) = - (&type_param_path.segments[..], &bounds[..]) - else { + if !type_param_path.is_single_argless_ident() { + return false; + } + let [ast::GenericBound::Trait(poly_trait_ref)] = &bounds[..] else { return false; }; - let [ast::PathSegment { ident, args: None, id }] = - &poly_trait_ref.trait_ref.path.segments[..] + let Ok(ast::PathSegmentRef { ident, args: None, id }) = + poly_trait_ref.trait_ref.path.iter_segments().exactly_one() else { return false; }; @@ -2135,8 +2128,8 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { let mut expr_kind = &args[0].kind; loop { match expr_kind { - ExprKind::Path(_, arg_name) if arg_name.segments.len() == 1 => { - if arg_name.segments[0].ident.name == kw::SelfLower { + ExprKind::Path(_, arg_name) if arg_name.num_segments() == 1 => { + if arg_name.iter_idents().next().unwrap().name == kw::SelfLower { let call_span = parent.span; let tail_args_span = if args.len() > 1 { Some(Span::new( @@ -3172,8 +3165,8 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { && let ast::ExprKind::Path(None, ref path) = lhs.kind && self.r.tcx.sess.source_map().is_line_before_span_empty(ident_span) { - let (span, text) = match path.segments.first() { - Some(seg) if let Some(name) = seg.ident.as_str().strip_prefix("let") => { + let (span, text) = match path.iter_idents().next() { + Some(ident) if let Some(name) = ident.as_str().strip_prefix("let") => { // a special case for #117894 let name = name.trim_prefix('_'); (ident_span, format!("let {name}")) @@ -3231,7 +3224,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { let doc_visible = doc_visible && (module_def_id.is_local() || !r.tcx.is_doc_hidden(module_def_id)); if module_def_id == def_id { - let path = Path { span: name_binding.span, segments: path_segments }; + let path = Path::from_segments(path_segments, name_binding.span); result = Some(( r.expect_module(module_def_id), ImportSuggestion { @@ -3264,9 +3257,10 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { let mut variants = Vec::new(); enum_module.for_each_child(self.r, |_, ident, orig_ident_span, _, name_binding| { if let Res::Def(DefKind::Ctor(CtorOf::Variant, kind), def_id) = name_binding.res() { - let mut segms = enum_import_suggestion.path.segments.clone(); - segms.push(ast::PathSegment::from_ident(ident.orig(orig_ident_span))); - let path = Path { span: name_binding.span, segments: segms }; + let mut path = enum_import_suggestion.path.clone(); + let (segments, span) = path.force_general_mut(); + segments.push(ast::PathSegment::from_ident(ident.orig(orig_ident_span))); + *span = name_binding.span; variants.push((path, def_id, kind)); } }); @@ -3307,8 +3301,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { }) => { let dot_span = receiver.span.between(*span); let found_tuple_variant = variant_ctors.iter().any(|(path, _, ctor_kind)| { - *ctor_kind == CtorKind::Fn - && path.segments.last().is_some_and(|seg| seg.ident == *ident) + *ctor_kind == CtorKind::Fn && path.last_ident() == Some(*ident) }); (found_tuple_variant.then_some(dot_span), false) } @@ -3316,9 +3309,8 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { // otherwise suggest adding a variant after `Type`. ExprKind::Field(base, ident) => { let dot_span = base.span.between(ident.span); - let found_tuple_or_unit_variant = variant_ctors.iter().any(|(path, ..)| { - path.segments.last().is_some_and(|seg| seg.ident == *ident) - }); + let found_tuple_or_unit_variant = + variant_ctors.iter().any(|(path, ..)| path.last_ident() == Some(*ident)); (found_tuple_or_unit_variant.then_some(dot_span), false) } _ => (None, false), @@ -3488,6 +3480,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { path: &[Segment], source: PathSource<'_, 'ast, 'ra>, ) -> Option> { + use itertools::Itertools; let Some(item) = self.diag_metadata.current_item else { return None }; let ItemKind::Impl(impl_) = &item.kind else { return None }; let self_ty = &impl_.self_ty; @@ -3506,11 +3499,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { return None; }; - let Some(args) = parent_segment.args.as_ref() else { - return None; - }; - - let GenericArgs::AngleBracketed(angle) = args.as_ref() else { + let Some(GenericArgs::AngleBracketed(angle)) = parent_segment.args else { return None; }; @@ -3523,7 +3512,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { .filter_map(|(pos, arg)| { if let AngleBracketedArg::Arg(GenericArg::Type(ty)) = arg && let TyKind::Path(_, path) = &ty.kind - && let [segment] = path.segments.as_slice() + && let Ok(segment) = path.iter_segments().exactly_one() { Some((segment.id, pos)) } else { @@ -3560,12 +3549,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { && let GenericParamKind::Const { ty, .. } = &target_param.kind && let TyKind::Path(_, path) = &ty.kind { - let full_type = path - .segments - .iter() - .map(|seg| seg.ident.to_string()) - .collect::>() - .join("::"); + let full_type = path.iter_idents().join("::"); // Find the first impl param whose position in C // is strictly greater than our missing param's index @@ -3578,8 +3562,8 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { .find_map(|arg| { if let AngleBracketedArg::Arg(GenericArg::Type(ty)) = arg && let TyKind::Path(_, path) = &ty.kind - && let [segment] = path.segments.as_slice() - && segment.ident == impl_param.ident + && let Ok(segment) = path.iter_segments().exactly_one() + && *segment.ident == impl_param.ident { usage_to_pos.get(&segment.id).copied() } else { @@ -4061,7 +4045,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { if let ast::GenericBound::Trait(poly_trait_ref) = bound && let span = poly_trait_ref .span - .with_hi(poly_trait_ref.trait_ref.path.span.lo()) + .with_hi(poly_trait_ref.trait_ref.path.span().lo()) && !span.is_empty() { rm_inner_binders.insert(span); @@ -4672,7 +4656,10 @@ fn mk_where_bound_predicate( ty: &Ty, ) -> Option { let modified_segments = { - let mut segments = path.segments.clone(); + let (Path::NoSpan { segments } | Path::General((segments, _))) = path else { + return None; + }; + let mut segments = segments.clone(); let [preceding @ .., second_last, last] = segments.as_mut_slice() else { return None; }; @@ -4717,7 +4704,7 @@ fn mk_where_bound_predicate( bound_generic_params: ThinVec::new(), modifiers: ast::TraitBoundModifiers::NONE, trait_ref: ast::TraitRef { - path: ast::Path { segments: modified_segments, span: DUMMY_SP }, + path: ast::Path::from_segments(modified_segments, DUMMY_SP), ref_id: DUMMY_NODE_ID, }, span: DUMMY_SP, @@ -4801,7 +4788,7 @@ pub(super) fn signal_label_shadowing(sess: &Session, orig: Span, shadower: Ident struct ParentPathVisitor<'a> { target: Ident, - parent: Option<&'a PathSegment>, + parent: Option>, stack: Vec<&'a Ty>, } @@ -4816,6 +4803,7 @@ impl<'a> ParentPathVisitor<'a> { impl<'a> Visitor<'a> for ParentPathVisitor<'a> { fn visit_ty(&mut self, ty: &'a Ty) { + use itertools::Itertools; if self.parent.is_some() { return; } @@ -4825,13 +4813,13 @@ impl<'a> Visitor<'a> for ParentPathVisitor<'a> { if let TyKind::Path(_, path) = &ty.kind // is this just `N`? - && let [segment] = path.segments.as_slice() - && segment.ident == self.target + && let Ok(&ident) = path.iter_idents().exactly_one() + && ident == self.target // parent is previous element in stack && let [.., parent_ty, _ty] = self.stack.as_slice() && let TyKind::Path(_, parent_path) = &parent_ty.kind { - self.parent = parent_path.segments.first(); + self.parent = parent_path.iter_segments().next(); } walk_ty(self, ty); diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index b92da685bc6a5..38a25fc21e6b3 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -374,7 +374,18 @@ struct Segment { impl Segment { fn from_path(path: &Path) -> Vec { - path.segments.iter().map(|s| s.into()).collect() + match path { + &Path::Ident { ident, id } => vec![Segment { + ident, + id: Some(id), + has_generic_args: false, + has_lifetime_args: false, + args_span: DUMMY_SP, + }], + Path::NoSpan { segments } | Path::General((segments, _)) => { + segments.iter().map(|s| s.as_ref().into()).collect() + } + } } fn from_ident(ident: Ident) -> Segment { @@ -392,10 +403,11 @@ impl Segment { } } -impl<'a> From<&'a ast::PathSegment> for Segment { - fn from(seg: &'a ast::PathSegment) -> Segment { +impl<'a> From> for Segment { + #[inline] + fn from(seg: ast::PathSegmentRef<'a>) -> Segment { let has_generic_args = seg.args.is_some(); - let (args_span, has_lifetime_args) = if let Some(args) = seg.args.as_deref() { + let (args_span, has_lifetime_args) = if let Some(args) = seg.args { match args { GenericArgs::AngleBracketed(args) => { let found_lifetimes = args @@ -411,7 +423,7 @@ impl<'a> From<&'a ast::PathSegment> for Segment { (DUMMY_SP, false) }; Segment { - ident: seg.ident, + ident: *seg.ident, id: Some(seg.id), has_generic_args, has_lifetime_args, @@ -2647,7 +2659,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }; // Don't perform legacy const generics rewriting if the path already // has generic arguments. - if path.segments.last().unwrap().args.is_some() { + if path.last_segment().unwrap().args.is_some() { return None; } @@ -2795,7 +2807,7 @@ fn names_to_string(names: impl Iterator) -> String { } fn path_names_to_string(path: &Path) -> String { - names_to_string(path.segments.iter().map(|seg| seg.ident.name)) + names_to_string(path.iter_idents().map(|i| i.name)) } /// A somewhat inefficient routine to obtain the name of a module. diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 29a82f809f124..cca6db7ad0483 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -4,6 +4,7 @@ use std::mem; use std::sync::Arc; +use itertools::Itertools; use rustc_ast::{self as ast, Crate, DelegationSuffixes, NodeId}; use rustc_ast_pretty::pprust; use rustc_attr_ir::{Attribute, AttributeKind, CfgEntry, StabilityLevel, StrippedCfgItem}; @@ -104,16 +105,16 @@ pub(crate) fn sub_namespace_match( // `format!("{}", path)`, because that tries to insert // line-breaks and is slow. fn fast_print_path(path: &ast::Path) -> Symbol { - if let [segment] = path.segments.as_slice() { - segment.ident.name + if let Ok(ident) = path.iter_idents().exactly_one() { + ident.name } else { let mut path_str = String::with_capacity(64); - for (i, segment) in path.segments.iter().enumerate() { + for (i, ident) in path.iter_idents().enumerate() { if i != 0 { path_str.push_str("::"); } - if segment.ident.name != kw::PathRoot { - path_str.push_str(segment.ident.as_str()) + if ident.name != kw::PathRoot { + path_str.push_str(ident.as_str()) } } Symbol::intern(&path_str) @@ -439,7 +440,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { ) { Ok((Some(ext), _)) => { if !ext.helper_attrs.is_empty() { - let span = resolution.path.segments.last().unwrap().ident.span; + let span = resolution.path.last_ident().unwrap().span; let ctxt = Macros20NormalizedSyntaxContext::new(span.ctxt()); entry.helper_attrs.extend( ext.helper_attrs @@ -631,7 +632,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if deleg_impl.is_some() { if !matches!(res, Res::Err | Res::Def(DefKind::Trait, _)) { self.dcx().emit_err(MacroExpectedFound { - span: path.span, + span: path.span(), expected: "trait", article: "a", found: res.descr(), @@ -646,8 +647,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } // Report errors for the resolved macro. - for (idx, segment) in path.segments.iter().enumerate() { - if let Some(args) = &segment.args { + for (idx, segment) in path.iter_segments().enumerate() { + if let Some(args) = segment.args { self.dcx().emit_err(diagnostics::GenericArgumentsInMacroPath { span: args.span() }); } if kind == MacroKind::Attr && segment.ident.as_str().starts_with("rustc") { @@ -669,7 +670,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { self.unused_macros.swap_remove(&def_id); if self.proc_macro_stubs.contains(&def_id) { self.dcx().emit_err(diagnostics::ProcMacroSameCrate { - span: path.span, + span: path.span(), is_test: self.tcx.sess.is_test_crate(), }); } @@ -701,7 +702,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let path_str = pprust::path_to_string(path); let mut err = MacroExpectedFound { - span: path.span, + span: path.span(), expected, article, found: res.descr(), @@ -711,12 +712,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }; // Suggest moving the macro out of the derive() if the macro isn't Derive - if !path.span.from_expansion() + if !path.span().from_expansion() && kind == MacroKind::Derive && !ext.macro_kinds().contains(MacroKinds::DERIVE) && ext.macro_kinds().contains(MacroKinds::ATTR) { - err.remove_surrounding_derive = Some(RemoveSurroundingDerive { span: path.span }); + err.remove_surrounding_derive = Some(RemoveSurroundingDerive { span: path.span() }); err.add_as_non_derive = Some(AddAsNonDerive { macro_path: &path_str }); } @@ -737,7 +738,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } else { "custom inner attributes are unstable" }; - feature_err(&self.tcx.sess, sym::custom_inner_attributes, path.span, msg).emit(); + feature_err(&self.tcx.sess, sym::custom_inner_attributes, path.span(), msg).emit(); } Ok((ext, res)) @@ -773,7 +774,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ignore_import: Option>, suggestion_span: Option, ) -> Result<(Option<&'ra Arc>, Res), Determinacy> { - let path_span = ast_path.span; + let path_span = ast_path.span(); let mut path = Segment::from_path(ast_path); // Possibly apply the macro helper hack @@ -1082,11 +1083,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { path: &ast::Path, node_id: NodeId, ) { - let span = path.span; if let Some(stability) = &ext.stability && let StabilityLevel::Unstable { reason, issue, implied_by, .. } = stability.level { let feature = stability.feature; + let span = path.span(); let is_allowed = |feature| self.features.enabled(feature) || span.allows_unstable(feature); @@ -1103,6 +1104,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } if let Some(depr) = &ext.deprecation { + let span = path.span(); let path = pprust::path_to_string(path); stability::early_report_macro_deprecation( &mut self.lint_buffer, @@ -1153,7 +1155,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // If such resolution is successful and gives the same result // (e.g. if the macro is re-imported), then silence the lint. let no_macro_rules = self.arenas.alloc_macro_rules_scope(MacroRulesScope::Empty); - let ident = path.segments[0].ident; + let ident = *path.iter_idents().next().unwrap(); let fallback_binding = self.reborrow().resolve_ident_in_scope_set( ident, ScopeSet::Macro(MacroKind::Bang), @@ -1180,10 +1182,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }; self.tcx.sess.psess.buffer_lint( OUT_OF_SCOPE_MACRO_CALLS, - path.span, + path.span(), node_id, diagnostics::OutOfScopeMacroCalls { - span: path.span, + span: path.span(), path: pprust::path_to_string(path), location, }, @@ -1247,7 +1249,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { path: &ast::Path, namespaces: &[Namespace], ) -> Result { - let span = path.span; + let span = path.span(); let path = &Segment::from_path(path); let parent_scope = self.invocation_parent_scopes[&expn_id]; diff --git a/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs b/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs index d67a194b02062..4cacbbc862d5f 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs @@ -19,14 +19,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &MsrvStack) { // check for `rustfmt_skip` and `rustfmt::skip` && let Some(skip_item) = &items[1].meta_item() && (skip_item.has_name(sym::rustfmt_skip) - || skip_item - .path - .segments - .last() - .expect("empty path in attribute") - .ident - .name - == sym::skip) + || skip_item.path.last_ident().expect("empty path in attribute").name == sym::skip) // Only lint outer attributes, because custom inner attributes are unstable // Tracking issue: https://github.com/rust-lang/rust/issues/54726 && attr.style == AttrStyle::Outer diff --git a/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs b/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs index c9cbf29f2b670..22d5d8a293e9c 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs @@ -20,13 +20,7 @@ impl From<&AttrKind> for SimpleAttrKind { fn from(value: &AttrKind) -> Self { match value { AttrKind::Normal(attr) => { - let path_symbols = attr - .item - .path - .segments - .iter() - .map(|seg| seg.ident.name) - .collect::>(); + let path_symbols = attr.item.path.iter_idents().map(|i| i.name).collect::>(); Self::Normal(path_symbols) }, AttrKind::Synthetic(synthetic) => match &**synthetic { diff --git a/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs b/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs index 9e9fbb2c52582..1683dec2336d2 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs @@ -22,8 +22,8 @@ pub(super) fn check( let mut clippy_lints = Vec::with_capacity(items.len()); for item in items { if let Some(meta_item) = item.meta_item() - && let [part1, _] = meta_item.path.segments.as_slice() - && part1.ident.name == sym::clippy + && let Some((part1_ident, _)) = meta_item.path.iter_idents().collect_tuple() + && part1_ident.name == sym::clippy { clippy_lints.push(item.span()); } diff --git a/src/tools/clippy/clippy_lints/src/attrs/utils.rs b/src/tools/clippy/clippy_lints/src/attrs/utils.rs index 8305754028f10..cd6ab3c1af29d 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/utils.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/utils.rs @@ -53,9 +53,12 @@ pub(super) fn extract_clippy_lint(lint: &MetaItemInner) -> Option { /// Returns the lint namespace, if any, as well as the lint name. (`None`, `None`) means /// the lint had less than 1 or more than 2 segments. pub(super) fn namespace_and_lint(lint: &MetaItemInner) -> (Option, Option) { - match lint.meta_item().map_or_default(|m| m.path.segments.as_slice()) { - [name] => (None, Some(name.ident.name)), - [namespace, name] => (Some(namespace.ident.name), Some(name.ident.name)), + let Some(mut idents) = lint.meta_item().map(|m| m.path.iter_idents()) else { + return (None, None); + }; + match (idents.next(), idents.next(), idents.next()) { + (Some(name), None, None) => (None, Some(name.name)), + (Some(namespace), Some(name), None) => (Some(namespace.name), Some(name.name)), _ => (None, None), } } diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs index 10814cdb4c585..71e314c1b5175 100644 --- a/src/tools/clippy/clippy_lints/src/collapsible_if.rs +++ b/src/tools/clippy/clippy_lints/src/collapsible_if.rs @@ -231,6 +231,7 @@ impl CollapsibleIf { inner_if_expr: &Expr<'_>, expected_lint_name: Symbol, ) -> bool { + use itertools::Itertools; match cx.tcx.hir_attrs(inner_if_expr.hir_id) { [] => { // There aren't any attributes, so just check for significant tokens @@ -242,9 +243,9 @@ impl CollapsibleIf { if matches!(Level::from_opt_symbol(attr.name()), Some(Level::Expect)) && let Some(metas) = attr.meta_item_list() && let Some(MetaItemInner::MetaItem(meta_item)) = metas.first() - && let [tool, lint_name] = meta_item.path.segments.as_slice() - && tool.ident.name == sym::clippy - && [expected_lint_name, sym::style, sym::all].contains(&lint_name.ident.name) => + && let Some((tool, lint_name)) = meta_item.path.iter_idents().collect_tuple() + && tool.name == sym::clippy + && [expected_lint_name, sym::style, sym::all].contains(&lint_name.name) => { // There is an `expect` attribute -- check that there is no _other_ significant text let span_before_attr = inner_if.span.split_at(1).1.until(attr.span()); diff --git a/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs b/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs index ddd666477612a..740f0d4187aaf 100644 --- a/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs +++ b/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use rustc_ast::ast::{AttrKind, Attribute, Item, ItemKind}; +use rustc_ast::ast::{Attribute, Item, ItemKind}; use rustc_ast::token::{Token, TokenKind}; use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_errors::Applicability; @@ -71,13 +71,7 @@ impl EarlyLintPass for CrateInMacroDef { } fn is_macro_export(attr: &Attribute) -> bool { - if let AttrKind::Normal(normal) = &attr.kind - && let [segment] = normal.item.path.segments.as_slice() - { - segment.ident.name == sym::macro_export - } else { - false - } + attr.has_name(sym::macro_export) } fn contains_unhygienic_crate_reference(tts: &TokenStream) -> Option { diff --git a/src/tools/clippy/clippy_lints/src/field_scoped_visibility_modifiers.rs b/src/tools/clippy/clippy_lints/src/field_scoped_visibility_modifiers.rs index 20a7ee9d28941..3e05bd5086aad 100644 --- a/src/tools/clippy/clippy_lints/src/field_scoped_visibility_modifiers.rs +++ b/src/tools/clippy/clippy_lints/src/field_scoped_visibility_modifiers.rs @@ -59,7 +59,9 @@ impl EarlyLintPass for FieldScopedVisibilityModifiers { let VisibilityKind::Restricted { path, .. } = &field.vis.kind else { continue; }; - if !path.segments.is_empty() && path.segments[0].ident.name == rustc_span::symbol::kw::SelfLower { + if let Some(ident) = path.iter_idents().next() + && ident.name == rustc_span::symbol::kw::SelfLower + { // pub(self) is equivalent to not using pub at all, so we ignore it continue; } diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs index a072e3dfff503..a6d20252673af 100644 --- a/src/tools/clippy/clippy_lints/src/format_args.rs +++ b/src/tools/clippy/clippy_lints/src/format_args.rs @@ -635,15 +635,14 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> { if !matches!(arg.kind, FormatArgumentKind::Captured(_)) && let rustc_ast::ExprKind::Path(None, path) = &arg.expr.kind - && let [segment] = path.segments.as_slice() - && segment.args.is_none() + && let Some(name) = path.as_single_argless_name() && let Some(arg_span) = format_arg_removal_span(self.format_args, index) && let Some(pos_span) = pos.span { let replacement = match usage { - FormatParamUsage::Argument => segment.ident.name.to_string(), - FormatParamUsage::Width => format!("{}$", segment.ident.name), - FormatParamUsage::Precision => format!(".{}$", segment.ident.name), + FormatParamUsage::Argument => name.to_string(), + FormatParamUsage::Width => format!("{name}$"), + FormatParamUsage::Precision => format!(".{name}$"), }; fixes.push((pos_span, replacement)); fixes.push((arg_span, String::new())); diff --git a/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs b/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs index 96c4cedda7ed4..f9cd951e97cf4 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs @@ -9,12 +9,7 @@ use super::UNNEEDED_FIELD_PATTERN; pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { if let PatKind::Struct(_, ref npat, ref pfields, _) = pat.kind { let mut wilds = 0; - let type_name = npat - .segments - .last() - .expect("A path must have at least one segment") - .ident - .name; + let type_name = npat.last_ident().expect("A path must have at least one segment").name; for field in pfields { if let PatKind::Wild = field.pat.kind { diff --git a/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs b/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs index 9d7b3c0d27224..3ba01bf28991f 100644 --- a/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs +++ b/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs @@ -65,6 +65,8 @@ enum Mode { impl EarlyLintPass for NeedlessArbitrarySelfType { fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) { + use itertools::Itertools; + // Bail out if the parameter it's not a receiver or was not written by the user if !p.is_self() || p.span.from_expansion() { return; @@ -84,8 +86,8 @@ impl EarlyLintPass for NeedlessArbitrarySelfType { }; let span = p.span.to(p.ty.span); - if let [segment] = &path.segments[..] - && segment.ident.name == kw::SelfUpper + if let Ok(ident) = path.iter_idents().exactly_one() + && ident.name == kw::SelfUpper { span_lint_and_then( cx, diff --git a/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs b/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs index 46a251b7b7278..41a30b4974f9d 100644 --- a/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs +++ b/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs @@ -53,7 +53,7 @@ impl MacroBraces { impl EarlyLintPass for MacroBraces { fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::MacCall) { - if let Some(last_segment) = mac.path.segments.last() + if let Some(last_segment) = mac.path.last_segment() && let name = last_segment.ident.as_str() && let Some(&braces) = self.macro_braces.0.get(name) && let Some(snip) = snippet_opt(cx.sess(), mac.span().with_lo(last_segment.span().lo())) diff --git a/src/tools/clippy/clippy_lints/src/redundant_field_names.rs b/src/tools/clippy/clippy_lints/src/redundant_field_names.rs index cfcbdffdd8b3c..1f7b34ab95381 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_field_names.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_field_names.rs @@ -54,9 +54,8 @@ impl EarlyLintPass for RedundantFieldNames { for field in &se.fields { if !field.is_shorthand && let ExprKind::Path(None, path) = &field.expr.kind - && let [segment] = path.segments.as_slice() - && segment.args.is_none() - && segment.ident == field.ident + && let Some(ident) = path.as_single_argless_ident() + && ident == field.ident && field.span.eq_ctxt(field.ident.span) && !field.span.in_external_macro(cx.sess().source_map()) { diff --git a/src/tools/clippy/clippy_lints/src/returns/needless_return.rs b/src/tools/clippy/clippy_lints/src/returns/needless_return.rs index 64a136d96a610..be9a4cf618cd4 100644 --- a/src/tools/clippy/clippy_lints/src/returns/needless_return.rs +++ b/src/tools/clippy/clippy_lints/src/returns/needless_return.rs @@ -121,6 +121,7 @@ fn check_final_expr<'tcx>( replacement: RetReplacement<'tcx>, match_ty_opt: Option>, ) { + use itertools::Itertools; let peeled_drop_expr = expr.peel_drop_temps(); match &peeled_drop_expr.kind { // simple return is always "bad" @@ -186,10 +187,10 @@ fn check_final_expr<'tcx>( && let metas = attr.meta_item_list() && let Some(lst) = metas && let [MetaItemInner::MetaItem(meta_item), ..] = lst.as_slice() - && let [tool, lint_name] = meta_item.path.segments.as_slice() - && tool.ident.name == sym::clippy + && let Some((tool, lint)) = meta_item.path.iter_idents().collect_tuple() + && tool.name == sym::clippy && matches!( - lint_name.ident.name, + lint.name, sym::needless_return | sym::style | sym::all | sym::warnings ) { diff --git a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs index dfcab8c74434a..89855c4953614 100644 --- a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs +++ b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs @@ -103,20 +103,24 @@ struct ImportUsageVisitor { impl Visitor<'_> for ImportUsageVisitor { fn visit_expr(&mut self, expr: &Expr) { if let ExprKind::Path(_, path) = &expr.kind - && path.segments.len() > 1 - && path.segments[0].ident.name == kw::SelfLower + && let mut path_iter = path.iter_idents() + && let Some(ident) = path_iter.next() + && ident.name == kw::SelfLower + && let Some(ident) = path_iter.next() { - self.imports_referenced_with_self.push(path.segments[1].ident.name); + self.imports_referenced_with_self.push(ident.name); } walk_expr(self, expr); } fn visit_ty(&mut self, ty: &Ty) { if let TyKind::Path(_, path) = &ty.kind - && path.segments.len() > 1 - && path.segments[0].ident.name == kw::SelfLower + && let mut path_iter = path.iter_idents() + && let Some(ident) = path_iter.next() + && ident.name == kw::SelfLower + && let Some(ident) = path_iter.next() { - self.imports_referenced_with_self.push(path.segments[1].ident.name); + self.imports_referenced_with_self.push(ident.name); } } } @@ -175,6 +179,7 @@ impl SingleComponentPathImports { single_use_usages: &mut Vec, macros: &mut Vec, ) { + use itertools::Itertools; if item.span.from_expansion() || item.vis.kind.is_pub() { return; } @@ -187,12 +192,12 @@ impl SingleComponentPathImports { macros.push(ident.name); }, ItemKind::Use(use_tree) => { - let segments = &use_tree.prefix.segments; + let num_segments = use_tree.prefix.num_segments(); // keep track of `use some_module;` usages - if segments.len() == 1 { + if num_segments == 1 { if let UseTreeKind::Simple(None) = use_tree.kind { - let name = segments[0].ident.name; + let name = use_tree.prefix.iter_idents().next().unwrap().name; if !macros.contains(&name) { single_use_usages.push(SingleUse { name, @@ -205,15 +210,14 @@ impl SingleComponentPathImports { return; } - if segments.is_empty() { + if num_segments == 0 { // keep track of `use {some_module, some_other_module};` usages if let UseTreeKind::Nested { items, .. } = &use_tree.kind { for tree in items { - let segments = &tree.inner.prefix.segments; - if segments.len() == 1 - && let UseTreeKind::Simple(None) = tree.inner.kind + if let UseTreeKind::Simple(None) = tree.inner.kind + && let Ok(ident) = tree.inner.prefix.iter_idents().exactly_one() { - let name = segments[0].ident.name; + let name = ident.name; if !macros.contains(&name) { single_use_usages.push(SingleUse { name, @@ -227,19 +231,21 @@ impl SingleComponentPathImports { } } // keep track of `use self::some_module` usages - else if segments[0].ident.name == kw::SelfLower { + else if let mut ident_iter = use_tree.prefix.iter_idents() + && let Some(ident) = ident_iter.next() + && ident.name == kw::SelfLower + { // simple case such as `use self::module::SomeStruct` - if segments.len() > 1 { - imports_reused_with_self.push(segments[1].ident.name); + if let Some(ident) = ident_iter.next() { + imports_reused_with_self.push(ident.name); return; } // nested case such as `use self::{module1::Struct1, module2::Struct2}` if let UseTreeKind::Nested { items, .. } = &use_tree.kind { for tree in items { - let segments = &tree.inner.prefix.segments; - if !segments.is_empty() { - imports_reused_with_self.push(segments[0].ident.name); + if let Some(ident) = tree.inner.prefix.iter_idents().next() { + imports_reused_with_self.push(ident.name); } } } diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs b/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs index cd31d728ead14..eab491fa762a3 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs @@ -41,7 +41,7 @@ impl EarlyLintPass for UnnecessarySelfImports { self_tree, is_toplevel, } = self_import; - let Some(last_segment) = tree.prefix.segments.last() else { + let Some(last_segment) = tree.prefix.last_segment() else { return; }; @@ -103,10 +103,11 @@ struct SelfImport<'a> { // XXX: rewrite as a generator returning `SelfImport`s, if those ever get stabilized fn for_each_self_import<'a>(tree: &'a UseTree, emit_lint: impl Fn(SelfImport<'a>) + Copy) { fn inner<'a>(tree: &'a UseTree, emit_lint: impl Fn(SelfImport<'a>) + Copy, is_toplevel: bool) { + use itertools::Itertools; if let UseTreeKind::Nested { items, .. } = &tree.kind { if let [self_tree] = &**items - && let [self_seg] = &*self_tree.inner.prefix.segments - && self_seg.ident.name == kw::SelfLower + && let Ok(self_ident) = self_tree.inner.prefix.iter_idents().exactly_one() + && self_ident.name == kw::SelfLower { emit_lint(SelfImport { tree, diff --git a/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs b/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs index 20b5d36b8d5f3..3b029d6aeba1e 100644 --- a/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs +++ b/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs @@ -42,12 +42,7 @@ fn check_use_tree(use_tree: &UseTree, cx: &EarlyContext<'_>, span: Span) { if new_name.as_str() == "_" { return; } - let old_name = use_tree - .prefix - .segments - .last() - .expect("use paths cannot be empty") - .ident; + let old_name = use_tree.prefix.last_ident().expect("use paths cannot be empty"); unsafe_to_safe_check(old_name, new_name, cx, span); }, UseTreeKind::Simple(None) | UseTreeKind::Glob(_) => {}, diff --git a/src/tools/clippy/clippy_lints/src/visibility.rs b/src/tools/clippy/clippy_lints/src/visibility.rs index 7bbdb7af75eb5..83708142225e6 100644 --- a/src/tools/clippy/clippy_lints/src/visibility.rs +++ b/src/tools/clippy/clippy_lints/src/visibility.rs @@ -106,7 +106,7 @@ impl EarlyLintPass for Visibility { if (**path == kw::Super || **path == kw::SelfLower || **path == kw::Crate) && !*shorthand - && let [.., last] = &*path.segments + && let Some(last) = path.last_ident() && !is_from_proc_macro(cx, item.vis.span) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] @@ -119,7 +119,7 @@ impl EarlyLintPass for Visibility { diag.span_suggestion( item.vis.span, "remove it", - format!("pub({})", last.ident), + format!("pub({last})"), Applicability::MachineApplicable, ); }, @@ -127,7 +127,7 @@ impl EarlyLintPass for Visibility { } if *shorthand - && let [.., last] = &*path.segments + && let Some(last) = path.last_ident() && !is_from_proc_macro(cx, item.vis.span) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] @@ -140,7 +140,7 @@ impl EarlyLintPass for Visibility { diag.span_suggestion( item.vis.span, "add it", - format!("pub(in {})", last.ident), + format!("pub(in {last})"), Applicability::MachineApplicable, ); }, diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index 0774fa729bc12..3289a467ade02 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -95,13 +95,20 @@ pub fn eq_maybe_qself(l: Option<&QSelf>, r: Option<&QSelf>) -> bool { } pub fn eq_path(l: &Path, r: &Path) -> bool { - over(&l.segments, &r.segments, eq_path_seg) + l.num_segments() == r.num_segments() + && l.iter_segments() + .zip(r.iter_segments()) + .all(|(a, b)| eq_path_seg_ref(a, b)) } fn eq_path_seg(l: &PathSegment, r: &PathSegment) -> bool { eq_id(l.ident, r.ident) && both(l.args.as_ref(), r.args.as_ref(), |l, r| eq_generic_args(l, r)) } +fn eq_path_seg_ref(l: PathSegmentRef<'_>, r: PathSegmentRef<'_>) -> bool { + eq_id(*l.ident, *r.ident) && both(l.args, r.args, |l, r| eq_generic_args(l, r)) +} + fn eq_generic_args(l: &GenericArgs, r: &GenericArgs) -> bool { match (l, r) { (AngleBracketed(l), AngleBracketed(r)) => over(&l.args, &r.args, eq_angle_arg), diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs index 828fb1d3313b4..eb2cc12ed7455 100644 --- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs +++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs @@ -472,14 +472,14 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { TyKind::Path(qself_path, path) => { let start = if qself_path.is_some() { Pat::Str("<") - } else if let Some(first) = path.segments.first() { - ident_search_pat(first.ident).0 + } else if let Some(first_ident) = path.iter_idents().next() { + ident_search_pat(*first_ident).0 } else { // this shouldn't be possible, but sure Pat::Str("") }; - let end = if let Some(last) = path.segments.last() { - match last.args.as_deref() { + let end = if let Some(last) = path.last_segment() { + match last.args { // last `>` in `std::foo::Bar` Some(GenericArgs::AngleBracketed(_)) => Pat::Str(">"), Some(GenericArgs::Parenthesized(par_args)) => match &par_args.output { @@ -498,7 +498,7 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { // last `..` in `(..)` -- `)` gets stripped Some(GenericArgs::ParenthesizedElided(_)) => Pat::Str(".."), // `bar` in `std::foo::bar` - None => ident_search_pat(last.ident).1, + None => ident_search_pat(*last.ident).1, } } else { // this shouldn't be possible @@ -524,8 +524,8 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { } }, TyKind::MacCall(mac_call) => { - let start = if let Some(first) = mac_call.path.segments.first() { - ident_search_pat(first.ident).0 + let start = if let Some(first) = mac_call.path.iter_idents().next() { + ident_search_pat(*first).0 } else { Pat::Str("") }; diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs index 48f17fb0fd1a8..0bd38d4a2de10 100644 --- a/src/tools/rustfmt/src/expr.rs +++ b/src/tools/rustfmt/src/expr.rs @@ -1557,7 +1557,7 @@ pub(crate) fn rewrite_call( pub(crate) fn is_simple_expr(expr: &ast::Expr) -> bool { match expr.kind { ast::ExprKind::Lit(..) => true, - ast::ExprKind::Path(ref qself, ref path) => qself.is_none() && path.segments.len() <= 1, + ast::ExprKind::Path(ref qself, ref path) => qself.is_none() && path.num_segments() <= 1, ast::ExprKind::AddrOf(_, _, ref expr) | ast::ExprKind::Cast(ref expr, _) | ast::ExprKind::Field(ref expr, _) diff --git a/src/tools/rustfmt/src/header.rs b/src/tools/rustfmt/src/header.rs index 5a9d2beb010b4..0fa649ce8d167 100644 --- a/src/tools/rustfmt/src/header.rs +++ b/src/tools/rustfmt/src/header.rs @@ -84,9 +84,7 @@ impl<'a> HeaderPart<'a> { ast::VisibilityKind::Public => Cow::from("pub"), ast::VisibilityKind::Inherited => Cow::from(""), ast::VisibilityKind::Restricted { ref path, .. } => { - let ast::Path { ref segments, .. } = **path; - let mut segments_iter = - segments.iter().map(|seg| rewrite_ident(context, seg.ident)); + let mut segments_iter = path.iter_idents().map(|&i| rewrite_ident(context, i)); if path.is_global() { segments_iter .next() diff --git a/src/tools/rustfmt/src/imports.rs b/src/tools/rustfmt/src/imports.rs index f062eaa332d92..4e38ba095bc2d 100644 --- a/src/tools/rustfmt/src/imports.rs +++ b/src/tools/rustfmt/src/imports.rs @@ -30,7 +30,7 @@ use crate::visitor::FmtVisitor; /// Returns a name imported by a `use` declaration. /// E.g., returns `Ordering` for `std::cmp::Ordering` and `self` for `std::cmp::self`. pub(crate) fn path_to_imported_ident(path: &ast::Path) -> symbol::Ident { - path.segments.last().unwrap().ident + path.last_ident().unwrap() } /// Returns all but the last portion of the module path, except in the case of @@ -185,12 +185,12 @@ impl UseSegment { } } - fn from_path_segment( + fn from_ident( context: &RewriteContext<'_>, - path_seg: &ast::PathSegment, + ident: symbol::Ident, modsep: bool, ) -> Option { - let name = rewrite_ident(context, path_seg.ident); + let name = rewrite_ident(context, ident); if name.is_empty() { return None; } @@ -446,8 +446,8 @@ impl UseTree { let mut modsep = leading_modsep; - for p in &a.prefix.segments { - if let Some(use_segment) = UseSegment::from_path_segment(context, p, modsep) { + for &ident in a.prefix.iter_idents() { + if let Some(use_segment) = UseSegment::from_ident(context, ident, modsep) { result.path.push(use_segment); modsep = false; } @@ -458,7 +458,7 @@ impl UseTree { match a.kind { UseTreeKind::Glob(_) => { // in case of a global path and the glob starts at the root, e.g., "::*" - if a.prefix.segments.len() == 1 && leading_modsep { + if a.prefix.num_segments() == 1 && leading_modsep { let kind = UseSegmentKind::Ident("".to_owned(), None); result.path.push(UseSegment { kind, @@ -480,7 +480,7 @@ impl UseTree { list.iter().map(|tree| &tree.inner), "}", ",", - |tree| tree.prefix.span.lo(), + |tree| tree.prefix.span().lo(), |tree| tree.hi_span().hi(), |_| Ok("".to_owned()), // We only need comments for now. context.snippet_provider.span_after(a.span(), "{"), @@ -490,7 +490,7 @@ impl UseTree { // in case of a global path and the nested list starts at the root, // e.g., "::{foo, bar}" - if a.prefix.segments.len() == 1 && leading_modsep { + if a.prefix.num_segments() == 1 && leading_modsep { let kind = UseSegmentKind::Ident("".to_owned(), None); result.path.push(UseSegment { kind, @@ -515,8 +515,8 @@ impl UseTree { // bypass the call to path_to_imported_ident which would get only the ident and // lose the path root, e.g., `that` in `::that`. // The span of `a.prefix` contains the leading colons. - let name = if a.prefix.segments.len() == 2 && leading_modsep { - context.snippet(a.prefix.span).to_owned() + let name = if a.prefix.num_segments() == 2 && leading_modsep { + context.snippet(a.prefix.span()).to_owned() } else { rewrite_ident(context, path_to_imported_ident(&a.prefix)).to_owned() }; diff --git a/src/tools/rustfmt/src/macros.rs b/src/tools/rustfmt/src/macros.rs index 9a88569b7909b..f4bc07161610b 100644 --- a/src/tools/rustfmt/src/macros.rs +++ b/src/tools/rustfmt/src/macros.rs @@ -105,9 +105,9 @@ impl Rewrite for MacroArg { /// Rewrite macro name without using pretty-printer if possible. fn rewrite_macro_name(context: &RewriteContext<'_>, path: &ast::Path) -> String { - if path.segments.len() == 1 { + if let Some(ident) = path.as_single_argless_ident() { // Avoid using pretty-printer in the common case. - format!("{}!", rewrite_ident(context, path.segments[0].ident)) + format!("{}!", rewrite_ident(context, ident)) } else { format!("{}!", pprust::path_to_string(path)) } @@ -165,7 +165,7 @@ pub(crate) fn rewrite_macro( let should_skip = context .skip_context .macros - .skip(context.snippet(mac.path.span)); + .skip(context.snippet(mac.path.span())); if should_skip { Err(RewriteError::SkipFormatting) } else { diff --git a/src/tools/rustfmt/src/modules.rs b/src/tools/rustfmt/src/modules.rs index 99e72dcebd8ea..bed958cf20ba3 100644 --- a/src/tools/rustfmt/src/modules.rs +++ b/src/tools/rustfmt/src/modules.rs @@ -607,8 +607,8 @@ fn find_path_value(attrs: &[ast::Attribute]) -> Option { fn is_cfg_if(item: &ast::Item) -> bool { match item.kind { ast::ItemKind::MacCall(ref mac) => { - if let Some(first_segment) = mac.path.segments.first() { - if first_segment.ident.name == Symbol::intern("cfg_if") { + if let Some(first_ident) = mac.path.iter_idents().next() { + if first_ident.name == Symbol::intern("cfg_if") { return true; } } @@ -621,8 +621,8 @@ fn is_cfg_if(item: &ast::Item) -> bool { fn is_cfg_select(item: &ast::Item) -> bool { match item.kind { ast::ItemKind::MacCall(ref mac) => { - if let Some(last_segment) = mac.path.segments.last() { - if last_segment.ident.name == Symbol::intern("cfg_select") { + if let Some(last_ident) = mac.path.last_ident() { + if last_ident.name == Symbol::intern("cfg_select") { return true; } } diff --git a/src/tools/rustfmt/src/modules/visitor.rs b/src/tools/rustfmt/src/modules/visitor.rs index 886128763c87a..d838fc8f8f13f 100644 --- a/src/tools/rustfmt/src/modules/visitor.rs +++ b/src/tools/rustfmt/src/modules/visitor.rs @@ -53,9 +53,9 @@ impl<'a, 'ast: 'a> CfgIfVisitor<'a> { // extern crate cfg_if; // cfg_if! {..} // ``` - match mac.path.segments.first() { - Some(first_segment) => { - if first_segment.ident.name != Symbol::intern("cfg_if") { + match mac.path.iter_idents().next() { + Some(first_ident) => { + if first_ident.name != Symbol::intern("cfg_if") { return Err("Expected cfg_if"); } } @@ -112,9 +112,9 @@ impl<'a, 'ast: 'a> CfgSelectVisitor<'a> { // use std::cfg_select; // cfg_select! {..} // ``` - match mac.path.segments.last() { - Some(last_segment) => { - if last_segment.ident.name != Symbol::intern("cfg_select") { + match mac.path.last_ident() { + Some(last_ident) => { + if last_ident.name != Symbol::intern("cfg_select") { return Err("Expected cfg_select"); } } diff --git a/src/tools/rustfmt/src/pairs.rs b/src/tools/rustfmt/src/pairs.rs index b4e390f206167..91260dd75167c 100644 --- a/src/tools/rustfmt/src/pairs.rs +++ b/src/tools/rustfmt/src/pairs.rs @@ -276,7 +276,7 @@ struct PairList<'a, 'b, T: Rewrite> { fn is_ident_or_bool_lit(expr: &ast::Expr) -> bool { match &expr.kind { - ast::ExprKind::Path(None, path) if path.segments.len() == 1 => true, + ast::ExprKind::Path(None, path) => path.num_segments() == 1, ast::ExprKind::Lit(token::Lit { kind: token::LitKind::Bool, .. diff --git a/src/tools/rustfmt/src/patterns.rs b/src/tools/rustfmt/src/patterns.rs index dd0c09b6e9609..39dc28cd1ffde 100644 --- a/src/tools/rustfmt/src/patterns.rs +++ b/src/tools/rustfmt/src/patterns.rs @@ -68,7 +68,7 @@ fn is_short_pattern_inner(context: &RewriteContext<'_>, pat: &ast::Pat) -> bool | ast::PatKind::Guard(..) => false, ast::PatKind::Tuple(ref subpats) => subpats.len() <= 1, ast::PatKind::TupleStruct(_, ref path, ref subpats) => { - path.segments.len() <= 1 && subpats.len() <= 1 + path.num_segments() <= 1 && subpats.len() <= 1 } PatKind::Deref(ref p) | ast::PatKind::Ref(ref p, _, _) | ast::PatKind::Paren(ref p) => { is_short_pattern_inner(context, &*p) diff --git a/src/tools/rustfmt/src/skip.rs b/src/tools/rustfmt/src/skip.rs index cd3860d3d7f0a..81e5d23719a77 100644 --- a/src/tools/rustfmt/src/skip.rs +++ b/src/tools/rustfmt/src/skip.rs @@ -87,18 +87,15 @@ static RUSTFMT: &str = "rustfmt"; static SKIP: &str = "skip"; /// Say if you're playing with `rustfmt`'s skip attribute -pub(crate) fn is_skip_attr(segments: &[ast::PathSegment]) -> bool { - if segments.len() < 2 || segments[0].ident.to_string() != RUSTFMT { - return false; - } - match segments.len() { - 2 => segments[1].ident.to_string() == SKIP, - 3 => { - segments[1].ident.to_string() == SKIP - && ["macros", "attributes"] - .iter() - .any(|&n| n == pprust::path_segment_to_string(&segments[2])) - } +pub(crate) fn is_skip_attr(path: &ast::Path) -> bool { + let mut idents = path.iter_idents().map(|i| i.as_str()); + match std::array::from_fn(|_| idents.next()) { + [ + Some("rustfmt"), + Some("skip"), + None | Some("macros" | "attributes"), + None, + ] => true, _ => false, } } diff --git a/src/tools/rustfmt/src/spanned.rs b/src/tools/rustfmt/src/spanned.rs index 90331ce926e5c..5ee84ca373e62 100644 --- a/src/tools/rustfmt/src/spanned.rs +++ b/src/tools/rustfmt/src/spanned.rs @@ -200,7 +200,7 @@ impl Spanned for ast::PreciseCapturingArg { fn span(&self) -> Span { match self { ast::PreciseCapturingArg::Lifetime(lt) => lt.ident.span, - ast::PreciseCapturingArg::Arg(path, _) => path.span, + ast::PreciseCapturingArg::Arg(path, _) => path.span(), } } } diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index fcf8321f18994..7bc338a70b302 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -1,5 +1,3 @@ -use std::ops::Deref; - use rustc_ast::ast::{self, FnRetTy, Mutability, Term}; use rustc_span::{BytePos, Pos, Span, symbol::kw}; use tracing::debug; @@ -51,7 +49,7 @@ pub(crate) fn rewrite_path( result.push_str("::"); } - let mut span_lo = path.span.lo(); + let mut span_lo = path.span().lo(); if let Some(qself) = qself { result.push('<'); @@ -66,14 +64,14 @@ pub(crate) fn rewrite_path( } // 3 = ">::".len() - let shape = shape.sub_width(3, path.span)?; + let shape = shape.sub_width(3, path.span())?; result = rewrite_path_segments( PathContext::Type, result, - path.segments.iter().take(skip_count), + path.iter_segments().take(skip_count), span_lo, - path.span.hi(), + path.span().hi(), context, shape, )?; @@ -86,9 +84,9 @@ pub(crate) fn rewrite_path( rewrite_path_segments( path_context, result, - path.segments.iter().skip(skip_count), + path.iter_segments().skip(skip_count), span_lo, - path.span.hi(), + path.span().hi(), context, shape, ) @@ -104,7 +102,7 @@ fn rewrite_path_segments<'a, I>( shape: Shape, ) -> RewriteResult where - I: Iterator, + I: Iterator>, { let mut first = true; let shape = shape.visual_indent(0); @@ -264,14 +262,14 @@ impl Rewrite for ast::AssocItemConstraintKind { // so that invariants described above will hold for the next segment. fn rewrite_segment( path_context: PathContext, - segment: &ast::PathSegment, + segment: ast::PathSegmentRef<'_>, span_lo: &mut BytePos, span_hi: BytePos, context: &RewriteContext<'_>, shape: Shape, ) -> RewriteResult { let mut result = String::with_capacity(128); - result.push_str(rewrite_ident(context, segment.ident)); + result.push_str(rewrite_ident(context, *segment.ident)); let ident_len = result.len(); let span = mk_sp(*span_lo, span_hi); @@ -1169,6 +1167,7 @@ fn join_bounds_inner( need_indent: bool, force_newline: bool, ) -> RewriteResult { + use itertools::Itertools; debug_assert!(!items.is_empty()); let generic_bounds_in_order = is_generic_bounds_in_order(items); @@ -1182,19 +1181,17 @@ fn join_bounds_inner( // that contains more than one item let is_item_with_multi_items_array = |item: &ast::GenericBound| match item { ast::GenericBound::Trait(ref poly_trait_ref, ..) => { - let segments = &poly_trait_ref.trait_ref.path.segments; - if segments.len() > 1 { - true + let Ok(segment) = poly_trait_ref.trait_ref.path.iter_segments().exactly_one() else { + return true; + }; + if let Some(args_in) = segment.args { + matches!( + args_in, + ast::GenericArgs::AngleBracketed(bracket_args) + if bracket_args.args.len() > 1 + ) } else { - if let Some(args_in) = &segments[0].args { - matches!( - args_in.deref(), - ast::GenericArgs::AngleBracketed(bracket_args) - if bracket_args.args.len() > 1 - ) - } else { - false - } + false } } ast::GenericBound::Use(args, _) => args.len() > 1, diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs index f7933ac52f75c..301113e61af55 100644 --- a/src/tools/rustfmt/src/utils.rs +++ b/src/tools/rustfmt/src/utils.rs @@ -3,7 +3,7 @@ use std::borrow::Cow; use rustc_ast::YieldKind; use rustc_ast::ast::{ self, Attribute, ImplRestriction, MetaItem, MetaItemInner, MetaItemKind, MutRestriction, - NodeId, Path, RestrictionKind, Visibility, VisibilityKind, + NodeId, RestrictionKind, Visibility, VisibilityKind, }; use rustc_ast_pretty::pprust; use rustc_feature::is_builtin_attr_name; @@ -59,8 +59,7 @@ pub(crate) fn format_visibility( VisibilityKind::Public => Cow::from("pub "), VisibilityKind::Inherited => Cow::from(""), VisibilityKind::Restricted { ref path, .. } => { - let Path { ref segments, .. } = **path; - let mut segments_iter = segments.iter().map(|seg| rewrite_ident(context, seg.ident)); + let mut segments_iter = path.iter_idents().map(|&i| rewrite_ident(context, i)); if path.is_global() { segments_iter .next() @@ -101,8 +100,7 @@ fn format_restriction( id: _, shorthand, } => { - let Path { ref segments, .. } = **path; - let mut segments_iter = segments.iter().map(|seg| rewrite_ident(context, seg.ident)); + let mut segments_iter = path.iter_idents().map(|&i| rewrite_ident(context, i)); if path.is_global() && segments_iter.next().is_none() { panic!("non-global path in {kw}(restricted)?"); } diff --git a/src/tools/rustfmt/src/visitor.rs b/src/tools/rustfmt/src/visitor.rs index 5043038d4fc09..0cd4b0bc75893 100644 --- a/src/tools/rustfmt/src/visitor.rs +++ b/src/tools/rustfmt/src/visitor.rs @@ -884,7 +884,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { } else { match &attr.kind { ast::AttrKind::Normal(ref normal) - if self.is_unknown_rustfmt_attr(&normal.item.path.segments) => + if self.is_unknown_rustfmt_attr(&normal.item.path) => { let file_name = self.psess.span_to_filename(attr.span); self.report.append( @@ -920,11 +920,12 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { false } - fn is_unknown_rustfmt_attr(&self, segments: &[ast::PathSegment]) -> bool { - if segments[0].ident.to_string() != "rustfmt" { - return false; + fn is_unknown_rustfmt_attr(&self, path: &ast::Path) -> bool { + match path.iter_idents().next() { + None => false, + Some(ident) if ident.as_str() != "rustfmt" => false, + _ => !is_skip_attr(path), } - !is_skip_attr(segments) } fn walk_mod_items(&mut self, items: &[Box]) { diff --git a/tests/ui-fulldeps/pprust-expr-roundtrip.rs b/tests/ui-fulldeps/pprust-expr-roundtrip.rs index 266afab4d7b2f..735be0c7ebee4 100644 --- a/tests/ui-fulldeps/pprust-expr-roundtrip.rs +++ b/tests/ui-fulldeps/pprust-expr-roundtrip.rs @@ -48,8 +48,7 @@ fn expr(kind: ExprKind) -> Box { } fn make_x() -> Box { - let seg = PathSegment::from_ident(Ident::from_str("x")); - let path = Path { segments: thin_vec![seg], span: DUMMY_SP }; + let path = Path::from_ident(Ident::from_str("x")); expr(ExprKind::Path(None, path)) } diff --git a/tests/ui/stats/input-stats.stderr b/tests/ui/stats/input-stats.stderr index 420b01102e41e..7e7b4034beacd 100644 --- a/tests/ui/stats/input-stats.stderr +++ b/tests/ui/stats/input-stats.stderr @@ -10,7 +10,6 @@ ast-stats - Impl 144 (NN.N%) 1 ast-stats - Trait 144 (NN.N%) 1 ast-stats - Fn 288 (NN.N%) 2 ast-stats - Use 576 (NN.N%) 4 -ast-stats PathSegment 840 (NN.N%) 35 24 ast-stats Ty 784 (NN.N%) 14 56 ast-stats - Ptr 56 (NN.N%) 1 ast-stats - Ref 56 (NN.N%) 1 @@ -27,6 +26,7 @@ ast-stats Pat 448 (NN.N%) 7 64 ast-stats - Struct 64 (NN.N%) 1 ast-stats - Wild 64 (NN.N%) 1 ast-stats - Ident 320 (NN.N%) 5 +ast-stats PathSegment 432 (NN.N%) 18 24 ast-stats GenericParam 400 (NN.N%) 5 80 ast-stats GenericBound 320 (NN.N%) 4 80 ast-stats - Trait 320 (NN.N%) 4 @@ -58,7 +58,9 @@ ast-stats GenericArgs 40 (NN.N%) 1 40 ast-stats - AngleBracketed 40 (NN.N%) 1 ast-stats Crate 40 (NN.N%) 1 40 ast-stats ---------------------------------------------------------------- -ast-stats Total 6_872 126 +ast-stats Total 6_464 109 +ast-stats ---------------------------------------------------------------- +ast-stats - Path Ident: 17 Other segs 0: 0 1: 1 2: 2 3: 3 4+: 1 NoSpan: 7 ast-stats ================================================================ hir-stats ================================================================ hir-stats HIR STATS: input_stats @@ -121,4 +123,6 @@ hir-stats ImplItemId 8 (NN.N%) 2 4 hir-stats ForeignItemId 4 (NN.N%) 1 4 hir-stats ---------------------------------------------------------------- hir-stats Total 8_592 172 +hir-stats ---------------------------------------------------------------- +hir-stats - Path segments 0: 0 1: 20 2: 2 3: 3 4+: 1 hir-stats ================================================================