Skip to content

Commit 1dfb2a3

Browse files
committed
Handle all project* attributes in that scope with one wrapper attribute
1 parent 3795d2d commit 1dfb2a3

File tree

4 files changed

+117
-27
lines changed

4 files changed

+117
-27
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

+1-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
#[rustversion::since(1.43)]
1010
include!("project_if_attr.rs.in");
1111

12-
use pin_project::{pin_project, project, project_ref, project_replace};
12+
use pin_project::{pin_project, project};
1313
use std::pin::Pin;
1414

1515
#[project] // Nightly does not need a dummy attribute to the function.
@@ -240,8 +240,6 @@ fn issue_206() {
240240
}
241241

242242
#[project]
243-
#[project_ref]
244-
#[project_replace]
245243
#[test]
246244
fn combine() {
247245
#[pin_project(Replace)]

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 combine1() {
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 combine1() {
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+
}

0 commit comments

Comments
 (0)