Skip to content

Commit ec35d1b

Browse files
committed
Handle all project* attributes in that scope with one wrapper attribute
1 parent 952983a commit ec35d1b

File tree

6 files changed

+301
-55
lines changed

6 files changed

+301
-55
lines changed

pin-project-internal/src/project.rs

+62-24
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ use syn::{
66
};
77

88
use crate::utils::{
9-
determine_lifetime_name, insert_lifetime, parse_as_empty, Mutability, SliceExt, VecExt,
9+
determine_lifetime_name, insert_lifetime, parse_as_empty, Immutable, Mutability, Mutable,
10+
Owned, SliceExt, VecExt,
1011
};
1112

1213
pub(crate) fn attribute(args: &TokenStream, input: Stmt, mutability: Mutability) -> TokenStream {
@@ -199,21 +200,30 @@ fn replace_item_impl(item: &mut ItemImpl, mutability: Mutability) -> Result<()>
199200
}
200201

201202
fn replace_item_fn(item: &mut ItemFn, mutability: Mutability) -> Result<()> {
202-
struct FnVisitor {
203-
res: Result<()>,
204-
mutability: Mutability,
205-
}
203+
struct FnVisitor(Result<()>);
206204

207205
impl FnVisitor {
208206
fn visit_stmt(&mut self, node: &mut Stmt) -> Result<()> {
209207
match node {
210208
Stmt::Expr(expr) | Stmt::Semi(expr, _) => self.visit_expr(expr),
211209
Stmt::Local(local) => {
212210
visit_mut::visit_local_mut(self, local);
213-
if let Some(attr) = local.attrs.find_remove(self.mutability.method_name())? {
214-
parse_as_empty(&attr.tokens)?;
215-
Context::new(self.mutability).replace_local(local)?;
211+
212+
let mut prev = None;
213+
for &mutability in &[Immutable, Mutable, Owned] {
214+
if let Some(attr) = local.attrs.find_remove(mutability.method_name())? {
215+
if let Some(prev) = prev.replace(mutability) {
216+
return Err(error!(
217+
attr,
218+
"attributes `{}` and `{}` are mutually exclusive",
219+
prev.method_name(),
220+
mutability.method_name(),
221+
));
222+
}
223+
Context::new(mutability).replace_local(local)?;
224+
}
216225
}
226+
217227
Ok(())
218228
}
219229
// Do not recurse into nested items.
@@ -223,41 +233,69 @@ fn replace_item_fn(item: &mut ItemFn, mutability: Mutability) -> Result<()> {
223233

224234
fn visit_expr(&mut self, node: &mut Expr) -> Result<()> {
225235
visit_mut::visit_expr_mut(self, node);
226-
let attr = match node {
227-
Expr::Match(expr) => expr.attrs.find_remove(self.mutability.method_name())?,
236+
match node {
237+
Expr::Match(expr) => {
238+
let mut prev = None;
239+
for &mutability in &[Immutable, Mutable, Owned] {
240+
if let Some(attr) = expr.attrs.find_remove(mutability.method_name())? {
241+
if let Some(prev) = prev.replace(mutability) {
242+
return Err(error!(
243+
attr,
244+
"attributes `{}` and `{}` are mutually exclusive",
245+
prev.method_name(),
246+
mutability.method_name(),
247+
));
248+
}
249+
}
250+
}
251+
if let Some(mutability) = prev {
252+
replace_expr(node, mutability);
253+
}
254+
}
228255
Expr::If(expr_if) => {
229256
if let Expr::Let(_) = &*expr_if.cond {
230-
expr_if.attrs.find_remove(self.mutability.method_name())?
231-
} else {
232-
None
257+
let mut prev = None;
258+
for &mutability in &[Immutable, Mutable, Owned] {
259+
if let Some(attr) =
260+
expr_if.attrs.find_remove(mutability.method_name())?
261+
{
262+
if let Some(prev) = prev.replace(mutability) {
263+
return Err(error!(
264+
attr,
265+
"attributes `{}` and `{}` are mutually exclusive",
266+
prev.method_name(),
267+
mutability.method_name(),
268+
));
269+
}
270+
}
271+
}
272+
if let Some(mutability) = prev {
273+
replace_expr(node, mutability);
274+
}
233275
}
234276
}
235-
_ => return Ok(()),
236-
};
237-
if let Some(attr) = attr {
238-
parse_as_empty(&attr.tokens)?;
239-
replace_expr(node, self.mutability);
277+
_ => {}
240278
}
241279
Ok(())
242280
}
243281
}
244282

245283
impl VisitMut for FnVisitor {
246284
fn visit_stmt_mut(&mut self, node: &mut Stmt) {
247-
if self.res.is_err() {
285+
if self.0.is_err() {
248286
return;
249287
}
250288
if let Err(e) = self.visit_stmt(node) {
251-
self.res = Err(e)
289+
self.0 = Err(e)
252290
}
253291
}
254292

255293
fn visit_expr_mut(&mut self, node: &mut Expr) {
256-
if self.res.is_err() {
294+
if self.0.is_err() {
257295
return;
258296
}
259297
if let Err(e) = self.visit_expr(node) {
260-
self.res = Err(e)
298+
self.0 = Err(e)
261299
}
262300
}
263301

@@ -270,9 +308,9 @@ fn replace_item_fn(item: &mut ItemFn, mutability: Mutability) -> Result<()> {
270308
return Err(error!(attr, "duplicate #[{}] attribute", mutability.method_name()));
271309
}
272310

273-
let mut visitor = FnVisitor { res: Ok(()), mutability };
311+
let mut visitor = FnVisitor(Ok(()));
274312
visitor.visit_block_mut(&mut item.block);
275-
visitor.res
313+
visitor.0
276314
}
277315

278316
fn replace_item_use(item: &mut ItemUse, mutability: Mutability) -> Result<()> {

tests/project.rs

+29-1
Original file line numberDiff line numberDiff line change
@@ -239,11 +239,39 @@ fn issue_206() {
239239
}
240240
}
241241

242+
#[project]
243+
#[test]
244+
fn combine() {
245+
#[pin_project(Replace)]
246+
enum Enum<A> {
247+
V1(#[pin] A),
248+
V2,
249+
}
250+
251+
let mut x = Enum::V1(1);
252+
#[project]
253+
match Pin::new(&mut x).project() {
254+
Enum::V1(_) => {}
255+
Enum::V2 => unreachable!(),
256+
}
257+
#[project_ref]
258+
match Pin::new(&x).project_ref() {
259+
Enum::V1(_) => {}
260+
Enum::V2 => unreachable!(),
261+
}
262+
#[project_replace]
263+
match Pin::new(&mut x).project_replace(Enum::V2) {
264+
Enum::V1(_) => {}
265+
Enum::V2 => unreachable!(),
266+
}
267+
}
268+
269+
// FIXME: This should be denied, but allowed for compatibility at this time.
242270
#[project]
243271
#[project_ref]
244272
#[project_replace]
245273
#[test]
246-
fn combine() {
274+
fn combine_compat() {
247275
#[pin_project(Replace)]
248276
enum Enum<A> {
249277
V1(#[pin] A),

tests/project_ref.rs

+27
Original file line numberDiff line numberDiff line change
@@ -145,3 +145,30 @@ fn project_impl() {
145145
fn foo<'pin>(&'pin self) {}
146146
}
147147
}
148+
149+
#[project_ref]
150+
#[test]
151+
fn combine() {
152+
#[pin_project(Replace)]
153+
enum Enum<A> {
154+
V1(#[pin] A),
155+
V2,
156+
}
157+
158+
let mut x = Enum::V1(1);
159+
#[project]
160+
match Pin::new(&mut x).project() {
161+
Enum::V1(_) => {}
162+
Enum::V2 => unreachable!(),
163+
}
164+
#[project_ref]
165+
match Pin::new(&x).project_ref() {
166+
Enum::V1(_) => {}
167+
Enum::V2 => unreachable!(),
168+
}
169+
#[project_replace]
170+
match Pin::new(&mut x).project_replace(Enum::V2) {
171+
Enum::V1(_) => {}
172+
Enum::V2 => unreachable!(),
173+
}
174+
}

tests/project_replace.rs

+27
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,30 @@ fn project_replace_stmt_expr() {
6969
Enum::None => panic!(),
7070
}
7171
}
72+
73+
#[project_replace]
74+
#[test]
75+
fn combine() {
76+
#[pin_project(Replace)]
77+
enum Enum<A> {
78+
V1(#[pin] A),
79+
V2,
80+
}
81+
82+
let mut x = Enum::V1(1);
83+
#[project]
84+
match Pin::new(&mut x).project() {
85+
Enum::V1(_) => {}
86+
Enum::V2 => unreachable!(),
87+
}
88+
#[project_ref]
89+
match Pin::new(&x).project_ref() {
90+
Enum::V1(_) => {}
91+
Enum::V2 => unreachable!(),
92+
}
93+
#[project_replace]
94+
match Pin::new(&mut x).project_replace(Enum::V2) {
95+
Enum::V1(_) => {}
96+
Enum::V2 => unreachable!(),
97+
}
98+
}

tests/ui/project/invalid.rs

+72
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,78 @@ mod attribute {
7373
let A(_) = Pin::new(&mut x).project();
7474
}
7575

76+
#[project]
77+
fn combine_stmt_project1() {
78+
let mut x = A(());
79+
#[project]
80+
#[project_ref] //~ ERROR are mutually exclusive
81+
let A(_) = Pin::new(&mut x).project();
82+
}
83+
84+
#[project]
85+
fn combine_stmt_project2() {
86+
let mut x = A(());
87+
#[project]
88+
#[project_replace] //~ ERROR are mutually exclusive
89+
let A(_) = Pin::new(&mut x).project();
90+
}
91+
92+
#[project]
93+
fn combine_stmt_project3() {
94+
let mut x = A(());
95+
#[project_ref]
96+
#[project_replace] //~ ERROR are mutually exclusive
97+
let A(_) = Pin::new(&mut x).project();
98+
}
99+
100+
#[project_ref]
101+
fn combine_stmt_project_ref1() {
102+
let mut x = A(());
103+
#[project]
104+
#[project_ref] //~ ERROR are mutually exclusive
105+
let A(_) = Pin::new(&mut x).project();
106+
}
107+
108+
#[project_ref]
109+
fn combine_stmt_project_ref2() {
110+
let mut x = A(());
111+
#[project]
112+
#[project_replace] //~ ERROR are mutually exclusive
113+
let A(_) = Pin::new(&mut x).project();
114+
}
115+
116+
#[project_ref]
117+
fn combine_stmt_project_ref3() {
118+
let mut x = A(());
119+
#[project_ref]
120+
#[project_replace] //~ ERROR are mutually exclusive
121+
let A(_) = Pin::new(&mut x).project();
122+
}
123+
124+
#[project_replace]
125+
fn combine_stmt_project_replace1() {
126+
let mut x = A(());
127+
#[project]
128+
#[project_ref] //~ ERROR are mutually exclusive
129+
let A(_) = Pin::new(&mut x).project();
130+
}
131+
132+
#[project_replace]
133+
fn combine_stmt_project_replace2() {
134+
let mut x = A(());
135+
#[project]
136+
#[project_replace] //~ ERROR are mutually exclusive
137+
let A(_) = Pin::new(&mut x).project();
138+
}
139+
140+
#[project_replace]
141+
fn combine_stmt_project_replace3() {
142+
let mut x = A(());
143+
#[project_ref]
144+
#[project_replace] //~ ERROR are mutually exclusive
145+
let A(_) = Pin::new(&mut x).project();
146+
}
147+
76148
#[project]
77149
#[project] //~ ERROR duplicate #[project] attribute
78150
fn duplicate_fn_project() {}

0 commit comments

Comments
 (0)