1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
//! Primary type parsing: groups, generic args (incl. array dispatch), splats, prefixes.
use crate::apply::{err_ty, err_ty_at};
use crate::ast::*;
use crate::parse::generic::{
empty, is_trait_base, parse_angle_bracket_contents, parse_generic,
parse_type_params, primitive,
};
use crate::parse::parse_atom::{
parse_attribute, parse_function, parse_group, parse_prefix, parse_range,
};
use crate::parse::parse_primitive;
use crate::parse::trailing::attach_wrapper;
use crate::parse::{parse_item, split_at_depth0};
use crate::util::Cursor;
use proc_macro2::{Delimiter, Ident, TokenTree};
pub(crate) fn parse_primary(tokens: &[TokenTree], trait_name: Option<&Ident>) -> Ty {
if let Some((attr, rest)) = parse_attribute(tokens) {
return attach_wrapper(
TyWithAttr(TyAttr(attr), None).into(),
rest,
trait_name,
);
}
if let Some(function) = parse_function(tokens, trait_name) {
return function;
}
// Bare `fn` (no params): `fn^(A,B)` gets its args filled in later by the `^` operator
if let [TokenTree::Ident(name)] = tokens
&& name == "fn"
{
return TyFn(None, None, false).into();
}
if let Some((prefix, rest)) = parse_prefix(tokens) {
// `unsafe` prefix disambiguation:
// - bare `unsafe` (rest empty) → unsafe impl marker (unsafe^T / unsafe-T), passthrough verbatim
// - `unsafe fn...` → unsafe fn type (TyFn.is_unsafe set)
// - `unsafe X` (X not fn) → error: in Rust, unsafe only qualifies fn types; writing it next to
// any other type is almost certainly a forgotten `^` (unsafe^Vec<T>)
if matches!(prefix, TyPrefix::Unsafe) && !rest.is_empty() {
if matches!(rest.first(), Some(TokenTree::Ident(f)) if f == "fn") {
let inner = parse_primitive(rest, trait_name);
return match inner.kind {
TyKind::Fn(mut f) => {
f.2 = true;
f.to_ty().with_span(inner.span)
}
// rest starts with `fn`, so parse_primitive must return TyFn; defensive fallback
other => Ty { span: inner.span, kind: other },
};
}
return err_ty(
"batch-impl: `unsafe` can only qualify a fn type (e.g. `unsafe fn(u32) -> u32`) \
or act as a bare impl marker (e.g. `unsafe^T`)",
);
}
let inner =
attach_wrapper(TyWithPrefix(prefix, None).into(), rest, trait_name);
return inner;
}
// Splat prefix: `*[...]` / `*(...)` — flatten a container's elements into
// the enclosing list / `^` argument list. `*const`/`*mut` stay pointers
// (handled by parse_prefix above). The group's contents are comma-split
// and each chunk parsed as a full expression (`parse_item` — so `*()^3`
// keeps its generator); splats are flattened at consumption (container
// collection / apply), not here.
if let [TokenTree::Punct(star), TokenTree::Group(group), rest @ ..] = tokens
&& star.as_char() == '*'
&& matches!(
group.delimiter(),
Delimiter::Bracket | Delimiter::Parenthesis
)
{
let inner = group.stream().into_iter().collect::<Vec<TokenTree>>();
let elems = if inner.is_empty() {
Vec::new()
} else {
split_at_depth0(&inner, ',')
.iter()
// `*(A,)` — a trailing comma yields an empty chunk; skip it
// (empty splat elements are not elements at all).
.filter(|c| !c.is_empty())
.map(|c| {
parse_item(&mut Cursor::new(c), Op::Dash, trait_name)
.unwrap_or_else(empty)
})
.collect()
};
// `*[...]` is set semantics (distribute), `*(...)` is list semantics
// (append) — the variant mirrors the parse-time delimiter.
let splat = if matches!(group.delimiter(), Delimiter::Bracket) {
TySplat::Array(TyArray(elems)).to_ty()
} else {
TySplat::Tuple(TyTuple(elems)).to_ty()
}
.with_span(star.span());
return if rest.is_empty() {
splat
} else {
splat.apply(parse_primitive(rest, trait_name))
};
}
// A bare `*` that is neither a splat (`*[...]` / `*(...)`) nor a raw
// pointer (`*const`/`*mut` — handled by parse_prefix) is a mistake;
// surface a targeted error instead of rustc's raw-pointer confusion.
if let [TokenTree::Punct(star), ..] = tokens
&& star.as_char() == '*'
{
return err_ty_at(
"batch-impl: `*` must be a splat (`*[...]` / `*(...)`) or a raw \
pointer (`*const T` / `*mut T`)",
star.span(),
);
}
if let Some(range) = parse_range(tokens) {
return range;
}
if let [TokenTree::Literal(literal)] = tokens
&& let Ok(number) = literal.to_string().parse()
{
return TyNum(number).into();
}
// An angle-bracket group (`delimiter![<>]`) is a generic list; must go through
// parse_type_params (else `HashMap^<A,B>`'s right operand is swallowed as empty by parse_group)
if let [TokenTree::Group(group)] = tokens
&& group.delimiter() != delimiter![<>]
{
return parse_group(group, trait_name);
}
if let Some((base, args, rest)) = parse_generic(tokens) {
let args_vec = args.into_iter().collect::<Vec<TokenTree>>();
let params = parse_angle_bracket_contents(
&args_vec,
trait_name,
is_trait_base(&base, trait_name),
);
let generic = if is_trait_base(&base, trait_name) {
TyTrait(base.iter().cloned().collect(), params).into()
} else {
// rest non-empty and not an angle-bracket group (`Vec<T><U>` = chained generics, via apply):
// anything else (e.g. `Vec<T>U`) is treated as a passthrough
if !rest.is_empty()
&& !matches!(rest.first(), Some(TokenTree::Group(g)) if g.delimiter() == delimiter![<>])
{
return primitive(tokens);
}
TyGeneric(primitive(&base).into(), params).into()
};
return if rest.is_empty() {
generic
} else {
generic.apply(parse_primitive(&rest, trait_name))
};
}
if let Some((args, rest)) = parse_type_params(tokens) {
let args_vec = args.into_iter().collect::<Vec<_>>();
let params = parse_angle_bracket_contents(&args_vec, trait_name, true);
let params = params.into();
return if rest.is_empty() {
params
} else {
params.apply(parse_primitive(&rest, trait_name))
};
}
primitive(tokens)
}