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
//! Macro hygiene gate for `#[derive(obj::Document)]` (M9 #80).
//!
//! The derive must use absolute paths (`::obj::Document`,
//! `::obj::IndexSpec`, `::obj::IndexKind`, `::std::vec::Vec`,
//! `::std::string::String`) so that a user crate that locally
//! shadows any of these names still compiles. This test sets up the
//! adversarial environment — a deeply-nested module path with local
//! types that match every name the derive references — and verifies
//! the emitted impl resolves to the real items.
// The local shadows below are intentionally unused — they exist only
// to break the derive if it forgets to use absolute paths.
#![allow(dead_code)]
use obj::Document;
// Outer module so the derive lands beneath a non-empty path.
mod outer {
pub mod middle {
pub mod inner {
use serde::{Deserialize, Serialize};
// Local types that shadow every crate-level item the
// derive references. None of them are used by anything in
// this module — they exist solely to poison the namespace
// and break the derive if it ever emits a non-absolute
// path. The derive uses `::obj::IndexSpec` (absolute), so
// these shadows are inert.
#[allow(dead_code)] // intentional shadow
pub struct IndexSpec;
#[allow(dead_code)] // intentional shadow
pub struct IndexKind;
#[allow(dead_code)] // intentional shadow
pub struct Document;
#[allow(dead_code)] // intentional shadow
pub struct Vec;
#[allow(dead_code)] // intentional shadow
pub struct String;
// Apply the derive to a struct in this poisoned module.
// The struct uses fully-qualified types for its real
// fields so the shadowed names don't break the field
// resolution itself.
#[derive(Serialize, Deserialize, obj::Document)]
#[obj(version = 2, collection = "shadowed")]
#[obj(index_composite(fields = ("a", "b"), name = "by_a_b"))]
pub struct Shadowed {
#[obj(index = unique)]
pub a: ::std::string::String,
#[obj(index)]
pub b: u32,
#[obj(index = each)]
pub c: ::std::vec::Vec<u32>,
}
}
}
}
#[test]
fn derive_resolves_absolute_paths_under_local_shadows() {
type S = outer::middle::inner::Shadowed;
assert_eq!(<S as Document>::COLLECTION, "shadowed");
assert_eq!(<S as Document>::VERSION, 2);
let specs = <S as Document>::indexes();
// 3 field indexes (a / b / c) + 1 composite (by_a_b) = 4.
assert_eq!(specs.len(), 4);
assert_eq!(specs[0].name, "a");
assert_eq!(specs[1].name, "b");
assert_eq!(specs[2].name, "c");
assert_eq!(specs[3].name, "by_a_b");
}