Move derive macro expansion into the MacroExpander · rust-lang/rust@b117bee (original) (raw)

``

1

`+

// Copyright 2012-2017 The Rust Project Developers. See the COPYRIGHT

`

``

2

`+

// file at the top-level directory of this distribution and at

`

``

3

`+

// http://rust-lang.org/COPYRIGHT.

`

``

4

`+

//

`

``

5

`+

// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or

`

``

6

`+

// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license

`

``

7

`+

// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your

`

``

8

`+

// option. This file may not be copied, modified, or distributed

`

``

9

`+

// except according to those terms.

`

``

10

+

``

11

`+

use ast::Name;

`

``

12

`+

use attr;

`

``

13

`+

use ast::{self, NestedMetaItem}; use ext::base::{ExtCtxt, SyntaxExtension};

`

``

14

`+

use codemap;

`

``

15

`+

use ext::build::AstBuilder;

`

``

16

`+

use feature_gate;

`

``

17

`+

use symbol::Symbol;

`

``

18

`+

use syntax_pos::Span;

`

``

19

+

``

20

`+

pub fn derive_attr_trait<'a>(cx: &mut ExtCtxt, attr: &'a ast::Attribute)

`

``

21

`+

-> Option<&'a NestedMetaItem> {

`

``

22

`+

if attr.name() != "derive" {

`

``

23

`+

return None;

`

``

24

`+

}

`

``

25

`+

if attr.value_str().is_some() {

`

``

26

`` +

cx.span_err(attr.span, "unexpected value in derive");

``

``

27

`+

return None;

`

``

28

`+

}

`

``

29

+

``

30

`+

let traits = attr.meta_item_list().unwrap_or(&[]);

`

``

31

+

``

32

`+

if traits.is_empty() {

`

``

33

`` +

cx.span_warn(attr.span, "empty trait list in derive");

``

``

34

`+

return None;

`

``

35

`+

}

`

``

36

+

``

37

`+

return traits.get(0);

`

``

38

`+

}

`

``

39

+

``

40

`+

pub fn verify_derive_attrs(cx: &mut ExtCtxt, attrs: &[ast::Attribute]) {

`

``

41

`+

for attr in attrs {

`

``

42

`+

if attr.name() != "derive" {

`

``

43

`+

continue;

`

``

44

`+

}

`

``

45

+

``

46

`+

if attr.value_str().is_some() {

`

``

47

`` +

cx.span_err(attr.span, "unexpected value in derive");

``

``

48

`+

}

`

``

49

+

``

50

`+

let traits = attr.meta_item_list().unwrap_or(&[]).to_owned();

`

``

51

+

``

52

`+

if traits.is_empty() {

`

``

53

`` +

cx.span_warn(attr.span, "empty trait list in derive");

``

``

54

`+

attr::mark_used(&attr);

`

``

55

`+

continue;

`

``

56

`+

}

`

``

57

`+

for titem in traits {

`

``

58

`+

if titem.word().is_none() {

`

``

59

`` +

cx.span_err(titem.span, "malformed derive entry");

``

``

60

`+

}

`

``

61

`+

}

`

``

62

`+

}

`

``

63

`+

}

`

``

64

+

``

65

`+

#[derive(PartialEq, Debug, Clone, Copy)]

`

``

66

`+

pub enum DeriveType {

`

``

67

`+

Legacy,

`

``

68

`+

ProcMacro,

`

``

69

`+

Builtin

`

``

70

`+

}

`

``

71

+

``

72

`+

impl DeriveType {

`

``

73

`+

// Classify a derive trait name by resolving the macro.

`

``

74

`+

pub fn classify(cx: &mut ExtCtxt, tname: Name) -> DeriveType {

`

``

75

`+

let legacy_derive_name = Symbol::intern(&format!("derive_{}", tname));

`

``

76

+

``

77

`+

if let Ok(_) = cx.resolver.resolve_builtin_macro(legacy_derive_name) {

`

``

78

`+

return DeriveType::Legacy;

`

``

79

`+

}

`

``

80

+

``

81

`+

match cx.resolver.resolve_builtin_macro(tname) {

`

``

82

`+

Ok(ext) => match *ext {

`

``

83

`+

SyntaxExtension::BuiltinDerive(..) => DeriveType::Builtin,

`

``

84

`+

_ => DeriveType::ProcMacro,

`

``

85

`+

},

`

``

86

`+

Err(_) => DeriveType::ProcMacro,

`

``

87

`+

}

`

``

88

`+

}

`

``

89

`+

}

`

``

90

+

``

91

`+

pub fn get_derive_attr(cx: &mut ExtCtxt, attrs: &mut Vecast::Attribute,

`

``

92

`+

derive_type: DeriveType) -> Optionast::Attribute {

`

``

93

`+

for i in 0..attrs.len() {

`

``

94

`+

if attrs[i].name() != "derive" {

`

``

95

`+

continue;

`

``

96

`+

}

`

``

97

+

``

98

`+

if attrs[i].value_str().is_some() {

`

``

99

`+

continue;

`

``

100

`+

}

`

``

101

+

``

102

`+

let mut traits = attrs[i].meta_item_list().unwrap_or(&[]).to_owned();

`

``

103

+

``

104

`+

// First, weed out malformed #[derive]

`

``

105

`+

traits.retain(|titem| titem.word().is_some());

`

``

106

+

``

107

`+

let mut titem = None;

`

``

108

+

``

109

`+

// See if we can find a matching trait.

`

``

110

`+

for j in 0..traits.len() {

`

``

111

`+

let tname = match traits[j].name() {

`

``

112

`+

Some(tname) => tname,

`

``

113

`+

_ => continue,

`

``

114

`+

};

`

``

115

+

``

116

`+

if DeriveType::classify(cx, tname) == derive_type {

`

``

117

`+

titem = Some(traits.remove(j));

`

``

118

`+

break;

`

``

119

`+

}

`

``

120

`+

}

`

``

121

+

``

122

`+

// If we find a trait, remove the trait from the attribute.

`

``

123

`+

if let Some(titem) = titem {

`

``

124

`+

if traits.len() == 0 {

`

``

125

`+

attrs.remove(i);

`

``

126

`+

} else {

`

``

127

`+

let derive = Symbol::intern("derive");

`

``

128

`+

let mitem = cx.meta_list(titem.span, derive, traits);

`

``

129

`+

attrs[i] = cx.attribute(titem.span, mitem);

`

``

130

`+

}

`

``

131

`+

let derive = Symbol::intern("derive");

`

``

132

`+

let mitem = cx.meta_list(titem.span, derive, vec![titem]);

`

``

133

`+

return Some(cx.attribute(mitem.span, mitem));

`

``

134

`+

}

`

``

135

`+

}

`

``

136

`+

return None;

`

``

137

`+

}

`

``

138

+

``

139

`+

fn allow_unstable(cx: &mut ExtCtxt, span: Span, attr_name: &str) -> Span {

`

``

140

`+

Span {

`

``

141

`+

expn_id: cx.codemap().record_expansion(codemap::ExpnInfo {

`

``

142

`+

call_site: span,

`

``

143

`+

callee: codemap::NameAndSpan {

`

``

144

`+

format: codemap::MacroAttribute(Symbol::intern(attr_name)),

`

``

145

`+

span: Some(span),

`

``

146

`+

allow_internal_unstable: true,

`

``

147

`+

},

`

``

148

`+

}),

`

``

149

`+

..span

`

``

150

`+

}

`

``

151

`+

}

`

``

152

+

``

153

`+

pub fn add_derived_markers(cx: &mut ExtCtxt, attrs: &mut Vecast::Attribute) {

`

``

154

`+

if attrs.is_empty() {

`

``

155

`+

return;

`

``

156

`+

}

`

``

157

+

``

158

`+

let titems = attrs.iter().filter(|a| {

`

``

159

`+

a.name() == "derive"

`

``

160

`+

}).flat_map(|a| {

`

``

161

`+

a.meta_item_list().unwrap_or(&[]).iter()

`

``

162

`+

}).filter_map(|titem| {

`

``

163

`+

titem.name()

`

``

164

`+

}).collect::<Vec<_>>();

`

``

165

+

``

166

`+

let span = attrs[0].span;

`

``

167

+

``

168

`+

if !attrs.iter().any(|a| a.name() == "structural_match") &&

`

``

169

`+

titems.iter().any(|t| *t == "PartialEq") && titems.iter().any(|t| *t == "Eq") {

`

``

170

`+

let structural_match = Symbol::intern("structural_match");

`

``

171

`+

let span = allow_unstable(cx, span, "derive(PartialEq, Eq)");

`

``

172

`+

let meta = cx.meta_word(span, structural_match);

`

``

173

`+

attrs.push(cx.attribute(span, meta));

`

``

174

`+

}

`

``

175

+

``

176

`+

if !attrs.iter().any(|a| a.name() == "rustc_copy_clone_marker") &&

`

``

177

`+

titems.iter().any(|t| *t == "Copy") && titems.iter().any(|t| *t == "Clone") {

`

``

178

`+

let structural_match = Symbol::intern("rustc_copy_clone_marker");

`

``

179

`+

let span = allow_unstable(cx, span, "derive(Copy, Clone)");

`

``

180

`+

let meta = cx.meta_word(span, structural_match);

`

``

181

`+

attrs.push(cx.attribute(span, meta));

`

``

182

`+

}

`

``

183

`+

}

`

``

184

+

``

185

`+

pub fn find_derive_attr(cx: &mut ExtCtxt, attrs: &mut Vecast::Attribute)

`

``

186

`+

-> Optionast::Attribute {

`

``

187

`+

verify_derive_attrs(cx, attrs);

`

``

188

`+

get_derive_attr(cx, attrs, DeriveType::Legacy).and_then(|a| {

`

``

189

`+

let titem = derive_attr_trait(cx, &a);

`

``

190

`+

titem.and_then(|titem| {

`

``

191

`+

let tword = titem.word().unwrap();

`

``

192

`+

let tname = tword.name();

`

``

193

`+

if !cx.ecfg.enable_custom_derive() {

`

``

194

`+

feature_gate::emit_feature_err(

`

``

195

`+

&cx.parse_sess,

`

``

196

`+

"custom_derive",

`

``

197

`+

titem.span,

`

``

198

`+

feature_gate::GateIssue::Language,

`

``

199

`+

feature_gate::EXPLAIN_CUSTOM_DERIVE

`

``

200

`+

);

`

``

201

`+

None

`

``

202

`+

} else {

`

``

203

`+

let name = Symbol::intern(&format!("derive_{}", tname));

`

``

204

`+

if !cx.resolver.is_whitelisted_legacy_custom_derive(name) {

`

``

205

`+

cx.span_warn(titem.span,

`

``

206

`+

feature_gate::EXPLAIN_DEPR_CUSTOM_DERIVE);

`

``

207

`+

}

`

``

208

`+

let mitem = cx.meta_word(titem.span, name);

`

``

209

`+

Some(cx.attribute(mitem.span, mitem))

`

``

210

`+

}

`

``

211

`+

})

`

``

212

`+

}).or_else(|| {

`

``

213

`+

get_derive_attr(cx, attrs, DeriveType::ProcMacro)

`

``

214

`+

}).or_else(|| {

`

``

215

`+

add_derived_markers(cx, attrs);

`

``

216

`+

get_derive_attr(cx, attrs, DeriveType::Builtin)

`

``

217

`+

})

`

``

218

`+

}

`