Skip to content

Commit 7012dae

Browse files
bors[bot]taiki-e
andauthored
Merge #197
197: Fix #[project] attribute on non-statement expressions r=taiki-e a=taiki-e Fixes #189 Co-authored-by: Taiki Endo <te316e89@gmail.com>
2 parents 7d46ff1 + 9be39c4 commit 7012dae

File tree

4 files changed

+168
-115
lines changed

4 files changed

+168
-115
lines changed

pin-project-internal/src/project.rs

+108-89
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ pub(crate) fn attribute(args: &TokenStream, input: Stmt, mutability: Mutability)
1313
.unwrap_or_else(|e| e.to_compile_error())
1414
}
1515

16-
fn replace_stmt(stmt: &mut Stmt, mutability: Mutability) -> Result<()> {
17-
match stmt {
18-
Stmt::Expr(Expr::Match(expr)) | Stmt::Semi(Expr::Match(expr), _) => {
16+
fn replace_expr(expr: &mut Expr, mutability: Mutability) {
17+
match expr {
18+
Expr::Match(expr) => {
1919
Context::new(mutability).replace_expr_match(expr);
2020
}
21-
Stmt::Expr(Expr::If(expr_if)) => {
21+
Expr::If(expr_if) => {
2222
let mut expr_if = expr_if;
2323
while let Expr::Let(ref mut expr) = &mut *expr_if.cond {
2424
Context::new(mutability).replace_expr_let(expr);
@@ -31,15 +31,14 @@ fn replace_stmt(stmt: &mut Stmt, mutability: Mutability) -> Result<()> {
3131
break;
3232
}
3333
}
34-
Stmt::Local(local) => Context::new(mutability).replace_local(local)?,
3534
_ => {}
3635
}
37-
Ok(())
3836
}
3937

4038
fn parse(mut stmt: Stmt, mutability: Mutability) -> Result<TokenStream> {
41-
replace_stmt(&mut stmt, mutability)?;
4239
match &mut stmt {
40+
Stmt::Expr(expr) | Stmt::Semi(expr, _) => replace_expr(expr, mutability),
41+
Stmt::Local(local) => Context::new(mutability).replace_local(local)?,
4342
Stmt::Item(Item::Fn(item)) => replace_item_fn(item, mutability)?,
4443
Stmt::Item(Item::Impl(item)) => replace_item_impl(item, mutability),
4544
Stmt::Item(Item::Use(item)) => replace_item_use(item, mutability)?,
@@ -68,7 +67,7 @@ impl Context {
6867

6968
fn compare_paths(&self, ident: &Ident, len: usize) -> bool {
7069
match &self.register {
71-
Some((i, l)) => *l == len && ident == i,
70+
Some((i, l)) => *l == len && i == ident,
7271
None => false,
7372
}
7473
}
@@ -99,7 +98,7 @@ impl Context {
9998
}
10099

101100
fn replace_expr_match(&mut self, expr: &mut ExprMatch) {
102-
expr.arms.iter_mut().for_each(|Arm { pat, .. }| self.replace_pat(pat, true))
101+
expr.arms.iter_mut().for_each(|arm| self.replace_pat(&mut arm.pat, true))
103102
}
104103

105104
fn replace_pat(&mut self, pat: &mut Pat, allow_pat_path: bool) {
@@ -155,6 +154,10 @@ fn is_replaceable(pat: &Pat, allow_pat_path: bool) -> bool {
155154
}
156155
}
157156

157+
fn replace_ident(ident: &mut Ident, mutability: Mutability) {
158+
*ident = proj_ident(ident, mutability);
159+
}
160+
158161
fn replace_item_impl(item: &mut ItemImpl, mutability: Mutability) {
159162
let PathSegment { ident, arguments } = match &mut *item.self_ty {
160163
Type::Path(TypePath { qself: None, path }) => path.segments.last_mut().unwrap(),
@@ -185,106 +188,122 @@ fn replace_item_impl(item: &mut ItemImpl, mutability: Mutability) {
185188
}
186189

187190
fn replace_item_fn(item: &mut ItemFn, mutability: Mutability) -> Result<()> {
188-
let mut visitor = FnVisitor { res: Ok(()), mutability };
189-
visitor.visit_block_mut(&mut item.block);
190-
visitor.res
191-
}
192-
193-
fn replace_item_use(item: &mut ItemUse, mutability: Mutability) -> Result<()> {
194-
let mut visitor = UseTreeVisitor { res: Ok(()), mutability };
195-
visitor.visit_item_use_mut(item);
196-
visitor.res
197-
}
198-
199-
fn replace_ident(ident: &mut Ident, mutability: Mutability) {
200-
*ident = proj_ident(ident, mutability);
201-
}
202-
203-
// =================================================================================================
204-
// visitors
205-
206-
struct FnVisitor {
207-
res: Result<()>,
208-
mutability: Mutability,
209-
}
191+
struct FnVisitor {
192+
res: Result<()>,
193+
mutability: Mutability,
194+
}
210195

211-
impl FnVisitor {
212-
/// Returns the attribute name.
213-
fn name(&self) -> &str {
214-
match self.mutability {
215-
Mutable => "project",
216-
Immutable => "project_ref",
217-
Owned => "project_replace",
196+
impl FnVisitor {
197+
/// Returns the attribute name.
198+
fn name(&self) -> &str {
199+
match self.mutability {
200+
Mutable => "project",
201+
Immutable => "project_ref",
202+
Owned => "project_replace",
203+
}
218204
}
219-
}
220205

221-
fn visit_stmt(&mut self, node: &mut Stmt) -> Result<()> {
222-
let attr = match node {
223-
Stmt::Expr(Expr::Match(expr)) | Stmt::Semi(Expr::Match(expr), _) => {
224-
expr.attrs.find_remove(self.name())?
206+
fn visit_stmt(&mut self, node: &mut Stmt) -> Result<()> {
207+
match node {
208+
Stmt::Expr(expr) | Stmt::Semi(expr, _) => {
209+
visit_mut::visit_expr_mut(self, expr);
210+
self.visit_expr(expr)
211+
}
212+
Stmt::Local(local) => {
213+
visit_mut::visit_local_mut(self, local);
214+
if let Some(attr) = local.attrs.find_remove(self.name())? {
215+
parse_as_empty(&attr.tokens)?;
216+
Context::new(self.mutability).replace_local(local)?;
217+
}
218+
Ok(())
219+
}
220+
// Do not recurse into nested items.
221+
Stmt::Item(_) => Ok(()),
225222
}
226-
Stmt::Local(local) => local.attrs.find_remove(self.name())?,
227-
Stmt::Expr(Expr::If(expr_if)) => {
228-
if let Expr::Let(_) = &*expr_if.cond {
229-
expr_if.attrs.find_remove(self.name())?
230-
} else {
231-
None
223+
}
224+
225+
fn visit_expr(&mut self, node: &mut Expr) -> Result<()> {
226+
let attr = match node {
227+
Expr::Match(expr) => expr.attrs.find_remove(self.name())?,
228+
Expr::If(expr_if) => {
229+
if let Expr::Let(_) = &*expr_if.cond {
230+
expr_if.attrs.find_remove(self.name())?
231+
} else {
232+
None
233+
}
232234
}
235+
_ => return Ok(()),
236+
};
237+
if let Some(attr) = attr {
238+
parse_as_empty(&attr.tokens)?;
239+
replace_expr(node, self.mutability);
233240
}
234-
_ => return Ok(()),
235-
};
236-
if let Some(attr) = attr {
237-
parse_as_empty(&attr.tokens)?;
238-
replace_stmt(node, self.mutability)?;
241+
Ok(())
239242
}
240-
Ok(())
241243
}
242-
}
243244

244-
impl VisitMut for FnVisitor {
245-
fn visit_stmt_mut(&mut self, node: &mut Stmt) {
246-
if self.res.is_err() {
247-
return;
245+
impl VisitMut for FnVisitor {
246+
fn visit_stmt_mut(&mut self, node: &mut Stmt) {
247+
if self.res.is_err() {
248+
return;
249+
}
250+
if let Err(e) = self.visit_stmt(node) {
251+
self.res = Err(e)
252+
}
248253
}
249254

250-
visit_mut::visit_stmt_mut(self, node);
251-
252-
if let Err(e) = self.visit_stmt(node) {
253-
self.res = Err(e)
255+
fn visit_expr_mut(&mut self, node: &mut Expr) {
256+
if self.res.is_err() {
257+
return;
258+
}
259+
if let Err(e) = self.visit_expr(node) {
260+
self.res = Err(e)
261+
}
254262
}
255-
}
256263

257-
fn visit_item_mut(&mut self, _: &mut Item) {
258-
// Do not recurse into nested items.
264+
fn visit_item_mut(&mut self, _: &mut Item) {
265+
// Do not recurse into nested items.
266+
}
259267
}
260-
}
261268

262-
struct UseTreeVisitor {
263-
res: Result<()>,
264-
mutability: Mutability,
269+
let mut visitor = FnVisitor { res: Ok(()), mutability };
270+
visitor.visit_block_mut(&mut item.block);
271+
visitor.res
265272
}
266273

267-
impl VisitMut for UseTreeVisitor {
268-
fn visit_use_tree_mut(&mut self, node: &mut UseTree) {
269-
if self.res.is_err() {
270-
return;
271-
}
274+
fn replace_item_use(item: &mut ItemUse, mutability: Mutability) -> Result<()> {
275+
struct UseTreeVisitor {
276+
res: Result<()>,
277+
mutability: Mutability,
278+
}
272279

273-
match node {
274-
// Desugar `use tree::<name>` into `tree::__<name>Projection`.
275-
UseTree::Name(name) => replace_ident(&mut name.ident, self.mutability),
276-
UseTree::Glob(glob) => {
277-
self.res =
278-
Err(error!(glob, "#[project] attribute may not be used on glob imports"));
280+
impl VisitMut for UseTreeVisitor {
281+
fn visit_use_tree_mut(&mut self, node: &mut UseTree) {
282+
if self.res.is_err() {
283+
return;
279284
}
280-
UseTree::Rename(rename) => {
281-
// TODO: Consider allowing the projected type to be renamed by `#[project] use Foo as Bar`.
282-
self.res =
283-
Err(error!(rename, "#[project] attribute may not be used on renamed imports"));
284-
}
285-
node @ UseTree::Path(_) | node @ UseTree::Group(_) => {
286-
visit_mut::visit_use_tree_mut(self, node)
285+
286+
match node {
287+
// Desugar `use tree::<name>` into `tree::__<name>Projection`.
288+
UseTree::Name(name) => replace_ident(&mut name.ident, self.mutability),
289+
UseTree::Glob(glob) => {
290+
self.res =
291+
Err(error!(glob, "#[project] attribute may not be used on glob imports"));
292+
}
293+
UseTree::Rename(rename) => {
294+
self.res = Err(error!(
295+
rename,
296+
"#[project] attribute may not be used on renamed imports"
297+
));
298+
}
299+
node @ UseTree::Path(_) | node @ UseTree::Group(_) => {
300+
visit_mut::visit_use_tree_mut(self, node)
301+
}
287302
}
288303
}
289304
}
305+
306+
let mut visitor = UseTreeVisitor { res: Ok(()), mutability };
307+
visitor.visit_item_use_mut(item);
308+
visitor.res
290309
}

tests/pin_project.rs

+24
Original file line numberDiff line numberDiff line change
@@ -556,3 +556,27 @@ fn self_in_where_clause() {
556556
type Foo = Struct1<T>;
557557
}
558558
}
559+
560+
#[test]
561+
fn where_clause() {
562+
#[pin_project]
563+
struct StructWhereClause<T>
564+
where
565+
T: Copy,
566+
{
567+
field: T,
568+
}
569+
570+
#[pin_project]
571+
struct TupleStructWhereClause<T>(T)
572+
where
573+
T: Copy;
574+
575+
#[pin_project]
576+
enum EnumWhereClause<T>
577+
where
578+
T: Copy,
579+
{
580+
Variant(T),
581+
}
582+
}

tests/project.rs

+19-23
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
#![warn(rust_2018_idioms, single_use_lifetimes)]
22
#![allow(dead_code)]
33

4-
// This hack is needed until https://github.com/rust-lang/rust/pull/69201
5-
// makes it way into stable.
6-
// Ceurrently, `#[attr] if true {}` doesn't even *parse* on stable,
7-
// which means that it will error even behind a `#[rustversion::nightly]`
4+
// Ceurrently, `#[attr] if true {}` doesn't even *parse* on MSRV,
5+
// which means that it will error even behind a `#[rustversion::since(..)]`
86
//
97
// This trick makes sure that we don't even attempt to parse
10-
// the `#[project] if let _` test on stable.
11-
#[rustversion::nightly]
8+
// the `#[project] if let _` test on MSRV.
9+
#[rustversion::since(1.43)]
1210
include!("project_if_attr.rs.in");
1311

1412
use pin_project::{pin_project, project};
@@ -194,23 +192,21 @@ mod project_use_2 {
194192
}
195193
}
196194

197-
#[pin_project]
198-
struct StructWhereClause<T>
199-
where
200-
T: Copy,
201-
{
202-
field: T,
203-
}
195+
#[test]
196+
#[project]
197+
fn non_stmt_expr_match() {
198+
#[pin_project]
199+
enum Enum<A> {
200+
Variant(#[pin] A),
201+
}
204202

205-
#[pin_project]
206-
struct TupleStructWhereClause<T>(T)
207-
where
208-
T: Copy;
203+
let mut x = Enum::Variant(1);
204+
let x = Pin::new(&mut x).project();
209205

210-
#[pin_project]
211-
enum EnumWhereClause<T>
212-
where
213-
T: Copy,
214-
{
215-
Variant(T),
206+
Some(
207+
#[project]
208+
match x {
209+
Enum::Variant(_x) => {}
210+
},
211+
);
216212
}

tests/project_if_attr.rs.in

+17-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
// FIXME: Once https://github.com/rust-lang/rust/pull/69201 makes its
2-
// way into stable, move this back into `project.rs
3-
41
#[test]
52
#[project]
63
fn project_if_let() {
@@ -27,3 +24,20 @@ fn project_if_let() {
2724
}
2825
}
2926

27+
#[test]
28+
#[project]
29+
fn non_stmt_expr_if_let() {
30+
#[pin_project]
31+
enum Enum<A> {
32+
Variant(#[pin] A),
33+
}
34+
35+
let mut x = Enum::Variant(1);
36+
let x = Pin::new(&mut x).project();
37+
38+
#[allow(irrefutable_let_patterns)]
39+
Some(
40+
#[project]
41+
if let Enum::Variant(_x) = x {},
42+
);
43+
}

0 commit comments

Comments
 (0)