Skip to content

Commit 79eced6

Browse files
committed
Get diesel_derives2 working with hygiene turned on
When `proc_macro2` has the nightly feature enabled, a lot of the rules around hygiene change. Most of the changes needed appear to be due to bugs in Rust (e.g. to access a tuple struct field, the span of the dot has to be resolved specially, but not for a named struct field), but the span changes for table names in particular is definitely required. The change to hygiene rules randomly broke our const workaround (the ability to `use` items defined in an expression like that was potentially unintended to begin with). However, there's no reason it needs to be a const, we can just have it be a module with no additional problems. I've added a few UI tests for the failure cases that are actually affected by this change (there is more work to be done on this, of course). Note that we can't actually run those tests yet, as they require a nightly compiler, and we won't build on nightly until tomorrow. However, I've left the tests in place so that the changes in output can be seen. The error for `table_name` isn't quite there, but due to a bug in Rust it's the best we can do. Right now it looks like this: ``` 1 | #[table_name = "users"] | ^ Use of undeclared type or module `users` ``` instead of like this: ``` 1 | #[table_name = "users"] | ^^^^^^^ Use of undeclared type or module `users` ```
1 parent 11942ec commit 79eced6

14 files changed

Lines changed: 196 additions & 33 deletions

.travis.yml

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,10 @@ script:
2222
(cd diesel && travis-cargo test -- --no-default-features --features "extras with-deprecated $BACKEND") &&
2323
(cd diesel_derives && travis-cargo test -- --features "diesel/$BACKEND") &&
2424
(cd diesel_derives2 && travis-cargo test -- --features "$BACKEND") &&
25-
if [[ "$BACKEND" == postgres ]]; then
26-
(cd examples/postgres && ./test_all)
27-
fi &&
28-
if [[ "$BACKEND" == mysql ]]; then
29-
(cd examples/mysql && ./test_all)
30-
fi &&
31-
if [[ "$BACKEND" == sqlite ]]; then
32-
(cd examples/sqlite && ./test_all)
25+
if [[ "$TRAVIS_RUST_VERSION" == nightly* ]]; then
26+
(cd diesel_derives2 && travis-cargo test -- --features "nightly $BACKEND")
3327
fi &&
28+
(cd "examples/$BACKEND" && ./test_all) &&
3429
(cd diesel_cli && travis-cargo test -- --no-default-features --features "$BACKEND") &&
3530
(cd diesel_infer_schema/infer_schema_internals && travis-cargo test -- --no-default-features --features "$BACKEND") &&
3631
(cd diesel_infer_schema/infer_schema_macros && travis-cargo test -- --no-default-features --features "$BACKEND") &&

diesel/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ tempdir = "^0.3.4"
4343
[features]
4444
default = ["with-deprecated"]
4545
extras = ["chrono", "serde_json", "uuid", "deprecated-time", "network-address", "numeric", "r2d2"]
46-
unstable = []
46+
unstable = ["diesel_derives2/nightly"]
4747
lint = ["clippy"]
4848
large-tables = []
4949
huge-tables = ["large-tables"]
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#[macro_use]
2+
extern crate diesel;
3+
4+
table! {
5+
users {
6+
id -> Integer,
7+
}
8+
}
9+
10+
#[derive(AsChangeset)]
11+
#[table_name = "users"]
12+
struct UserStruct {
13+
name: String,
14+
#[column_name = "hair_color"]
15+
color_de_pelo: String,
16+
}
17+
18+
#[derive(AsChangeset)]
19+
#[table_name = "users"]
20+
struct UserTuple(#[column_name = "name"] String);
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
error[E0412]: cannot find type `name` in module `users`
2+
--> $DIR/as_changeset_bad_column_name.rs:13:5
3+
|
4+
13 | name: String,
5+
| ^^^^ not found in `users`
6+
7+
error[E0412]: cannot find type `hair_color` in module `users`
8+
--> $DIR/as_changeset_bad_column_name.rs:14:5
9+
|
10+
14 | #[column_name = "hair_color"]
11+
| ^ not found in `users`
12+
13+
error[E0425]: cannot find value `name` in module `users`
14+
--> $DIR/as_changeset_bad_column_name.rs:13:5
15+
|
16+
13 | name: String,
17+
| ^^^^ not found in `users`
18+
19+
error[E0425]: cannot find value `hair_color` in module `users`
20+
--> $DIR/as_changeset_bad_column_name.rs:14:5
21+
|
22+
14 | #[column_name = "hair_color"]
23+
| ^ not found in `users`
24+
25+
error[E0412]: cannot find type `name` in module `users`
26+
--> $DIR/as_changeset_bad_column_name.rs:20:18
27+
|
28+
20 | struct UserTuple(#[column_name = "name"] String);
29+
| ^ not found in `users`
30+
31+
error[E0425]: cannot find value `name` in module `users`
32+
--> $DIR/as_changeset_bad_column_name.rs:20:18
33+
|
34+
20 | struct UserTuple(#[column_name = "name"] String);
35+
| ^ not found in `users`
36+
37+
error[E0601]: main function not found
38+
39+
error: aborting due to 7 previous errors
40+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#[macro_use]
2+
extern crate diesel;
3+
4+
#[derive(AsChangeset)]
5+
struct User {
6+
id: i32,
7+
name: String,
8+
}
9+
10+
#[derive(AsChangeset)]
11+
#[table_name = "users"]
12+
struct UserForm {
13+
id: i32,
14+
name: String,
15+
}
16+
17+
fn main() {}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error[E0433]: failed to resolve. Use of undeclared type or module `users`
2+
--> $DIR/as_changeset_missing_table_import.rs:5:8
3+
|
4+
5 | struct User {
5+
| ^^^^ Use of undeclared type or module `users`
6+
7+
error[E0433]: failed to resolve. Use of undeclared type or module `users`
8+
--> <macro expansion>:1:1
9+
|
10+
1 | #[table_name = "users"]
11+
| ^ Use of undeclared type or module `users`
12+
13+
error: aborting due to 2 previous errors
14+
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Copyright 2015 The Rust Project Developers. See the COPYRIGHT
4+
# file at the top-level directory of this distribution and at
5+
# http://rust-lang.org/COPYRIGHT.
6+
#
7+
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
8+
# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
9+
# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
10+
# option. This file may not be copied, modified, or distributed
11+
# except according to those terms.
12+
13+
# A script to update the references for particular tests. The idea is
14+
# that you do a run, which will generate files in the build directory
15+
# containing the (normalized) actual output of the compiler. This
16+
# script will then copy that output and replace the "expected output"
17+
# files. You can then commit the changes.
18+
#
19+
# If you find yourself manually editing a foo.stderr file, you're
20+
# doing it wrong.
21+
22+
if [[ "$1" == "--help" || "$1" == "-h" || "$1" == "" || "$2" == "" ]]; then
23+
echo "usage: $0 <build-directory> <relative-path-to-rs-files>"
24+
echo ""
25+
echo "For example:"
26+
echo " $0 ../../../build/x86_64-apple-darwin/test/ui *.rs */*.rs"
27+
fi
28+
29+
MYDIR=$(dirname $0)
30+
31+
BUILD_DIR="$1"
32+
shift
33+
34+
while [[ "$1" != "" ]]; do
35+
STDERR_NAME="${1/%.rs/.stderr}"
36+
STDOUT_NAME="${1/%.rs/.stdout}"
37+
shift
38+
if [ -f $BUILD_DIR/$STDOUT_NAME ] && \
39+
! (diff $BUILD_DIR/$STDOUT_NAME $MYDIR/$STDOUT_NAME >& /dev/null); then
40+
echo updating $MYDIR/$STDOUT_NAME
41+
cp $BUILD_DIR/$STDOUT_NAME $MYDIR/$STDOUT_NAME
42+
fi
43+
if [ -f $BUILD_DIR/$STDERR_NAME ] && \
44+
! (diff $BUILD_DIR/$STDERR_NAME $MYDIR/$STDERR_NAME >& /dev/null); then
45+
echo updating $MYDIR/$STDERR_NAME
46+
cp $BUILD_DIR/$STDERR_NAME $MYDIR/$STDERR_NAME
47+
fi
48+
done

diesel_derives2/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ repository = "https://github.com/diesel-rs/diesel/tree/master/diesel_derives"
1212
syn = { version = "0.12.0", features = ["full"] }
1313
quote = "0.4"
1414
clippy = { optional = true, version = "=0.0.174" }
15+
proc-macro2 = "0.2.0"
1516

1617
[dev-dependencies]
1718
cfg-if = "0.1.0"
@@ -28,6 +29,7 @@ name = "tests"
2829
[features]
2930
default = []
3031
lint = ["clippy"]
32+
nightly = ["proc-macro2/nightly"]
3133
postgres = ["diesel/postgres", "diesel_migrations/postgres"]
3234
sqlite = ["diesel/sqlite", "diesel_migrations/sqlite"]
3335
mysql = ["diesel/mysql", "diesel_migrations/mysql"]

diesel_derives2/src/as_changeset.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,10 @@ pub fn derive(item: syn::DeriveInput) -> quote::Tokens {
4343
);
4444
}
4545

46-
wrap_in_dummy_const(
47-
model.dummy_const_name("AS_CHANGESET"),
46+
wrap_in_dummy_mod(
47+
model.dummy_mod_name("as_changeset"),
4848
quote!(
49-
use diesel::query_builder::AsChangeset;
49+
use self::diesel::query_builder::AsChangeset;
5050

5151
impl #impl_generics AsChangeset for &'update #struct_name #ty_generics
5252
#where_clause
@@ -70,7 +70,7 @@ fn field_changeset_ty(
7070
let column_name = field.column_name();
7171
if !treat_none_as_null && is_option_ty(&field.ty) {
7272
let field_ty = inner_of_option_ty(&field.ty);
73-
parse_quote!(::std::option::Option<diesel::dsl::Eq<#table_name::#column_name, &'update #field_ty>>)
73+
parse_quote!(std::option::Option<diesel::dsl::Eq<#table_name::#column_name, &'update #field_ty>>)
7474
} else {
7575
let field_ty = &field.ty;
7676
parse_quote!(diesel::dsl::Eq<#table_name::#column_name, &'update #field_ty>)
@@ -82,11 +82,11 @@ fn field_changeset_expr(
8282
table_name: syn::Ident,
8383
treat_none_as_null: bool,
8484
) -> syn::Expr {
85-
let field_name = &field.name;
85+
let field_access = &field.name;
8686
let column_name = field.column_name();
8787
if !treat_none_as_null && is_option_ty(&field.ty) {
88-
parse_quote!(self.#field_name.as_ref().map(|x| #table_name::#column_name.eq(x)))
88+
parse_quote!(self#field_access.as_ref().map(|x| #table_name::#column_name.eq(x)))
8989
} else {
90-
parse_quote!(#table_name::#column_name.eq(&self.#field_name))
90+
parse_quote!(#table_name::#column_name.eq(&self#field_access))
9191
}
9292
}

diesel_derives2/src/field.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
use syn;
1+
use proc_macro2::Span;
22
use quote;
3+
use syn;
34

45
use meta::*;
56

@@ -16,7 +17,11 @@ impl Field {
1617
.map(|m| m.expect_ident_value());
1718
let name = match field.ident {
1819
Some(x) => FieldName::Named(x),
19-
None => FieldName::Unnamed(index.into()),
20+
None => FieldName::Unnamed(syn::Index {
21+
index: index as u32,
22+
// https://github.com/rust-lang/rust/issues/47312
23+
span: Span::call_site(),
24+
}),
2025
};
2126

2227
Self {
@@ -42,6 +47,13 @@ pub enum FieldName {
4247

4348
impl quote::ToTokens for FieldName {
4449
fn to_tokens(&self, tokens: &mut quote::Tokens) {
50+
use proc_macro2::{Spacing, TokenNode, TokenTree};
51+
52+
// https://github.com/rust-lang/rust/issues/47312
53+
tokens.append(TokenTree {
54+
span: Span::call_site(),
55+
kind: TokenNode::Op('.', Spacing::Alone),
56+
});
4557
match *self {
4658
FieldName::Named(x) => x.to_tokens(tokens),
4759
FieldName::Unnamed(ref x) => x.to_tokens(tokens),

0 commit comments

Comments
 (0)