1 | //! Intermediate representation of variables. |
2 | |
3 | use super::super::codegen::MacroTypeVariation; |
4 | use super::context::{BindgenContext, TypeId}; |
5 | use super::dot::DotAttributes; |
6 | use super::function::cursor_mangling; |
7 | use super::int::IntKind; |
8 | use super::item::Item; |
9 | use super::ty::{FloatKind, TypeKind}; |
10 | use crate::callbacks::{ItemInfo, ItemKind, MacroParsingBehavior}; |
11 | use crate::clang; |
12 | use crate::clang::ClangToken; |
13 | use crate::parse::{ClangSubItemParser, ParseError, ParseResult}; |
14 | |
15 | use std::io; |
16 | use std::num::Wrapping; |
17 | |
18 | /// The type for a constant variable. |
19 | #[derive (Debug)] |
20 | pub(crate) enum VarType { |
21 | /// A boolean. |
22 | Bool(bool), |
23 | /// An integer. |
24 | Int(i64), |
25 | /// A floating point number. |
26 | Float(f64), |
27 | /// A character. |
28 | Char(u8), |
29 | /// A string, not necessarily well-formed utf-8. |
30 | String(Vec<u8>), |
31 | } |
32 | |
33 | /// A `Var` is our intermediate representation of a variable. |
34 | #[derive (Debug)] |
35 | pub(crate) struct Var { |
36 | /// The name of the variable. |
37 | name: String, |
38 | /// The mangled name of the variable. |
39 | mangled_name: Option<String>, |
40 | /// The link name of the variable. |
41 | link_name: Option<String>, |
42 | /// The type of the variable. |
43 | ty: TypeId, |
44 | /// The value of the variable, that needs to be suitable for `ty`. |
45 | val: Option<VarType>, |
46 | /// Whether this variable is const. |
47 | is_const: bool, |
48 | } |
49 | |
50 | impl Var { |
51 | /// Construct a new `Var`. |
52 | pub(crate) fn new( |
53 | name: String, |
54 | mangled_name: Option<String>, |
55 | link_name: Option<String>, |
56 | ty: TypeId, |
57 | val: Option<VarType>, |
58 | is_const: bool, |
59 | ) -> Var { |
60 | assert!(!name.is_empty()); |
61 | Var { |
62 | name, |
63 | mangled_name, |
64 | link_name, |
65 | ty, |
66 | val, |
67 | is_const, |
68 | } |
69 | } |
70 | |
71 | /// Is this variable `const` qualified? |
72 | pub(crate) fn is_const(&self) -> bool { |
73 | self.is_const |
74 | } |
75 | |
76 | /// The value of this constant variable, if any. |
77 | pub(crate) fn val(&self) -> Option<&VarType> { |
78 | self.val.as_ref() |
79 | } |
80 | |
81 | /// Get this variable's type. |
82 | pub(crate) fn ty(&self) -> TypeId { |
83 | self.ty |
84 | } |
85 | |
86 | /// Get this variable's name. |
87 | pub(crate) fn name(&self) -> &str { |
88 | &self.name |
89 | } |
90 | |
91 | /// Get this variable's mangled name. |
92 | pub(crate) fn mangled_name(&self) -> Option<&str> { |
93 | self.mangled_name.as_deref() |
94 | } |
95 | |
96 | /// Get this variable's link name. |
97 | pub fn link_name(&self) -> Option<&str> { |
98 | self.link_name.as_deref() |
99 | } |
100 | } |
101 | |
102 | impl DotAttributes for Var { |
103 | fn dot_attributes<W>( |
104 | &self, |
105 | _ctx: &BindgenContext, |
106 | out: &mut W, |
107 | ) -> io::Result<()> |
108 | where |
109 | W: io::Write, |
110 | { |
111 | if self.is_const { |
112 | writeln!(out, "<tr><td>const</td><td>true</td></tr>" )?; |
113 | } |
114 | |
115 | if let Some(ref mangled: &String) = self.mangled_name { |
116 | writeln!( |
117 | out, |
118 | "<tr><td>mangled name</td><td> {}</td></tr>" , |
119 | mangled |
120 | )?; |
121 | } |
122 | |
123 | Ok(()) |
124 | } |
125 | } |
126 | |
127 | fn default_macro_constant_type(ctx: &BindgenContext, value: i64) -> IntKind { |
128 | if value < 0 || |
129 | ctx.options().default_macro_constant_type == |
130 | MacroTypeVariation::Signed |
131 | { |
132 | if value < i32::min_value() as i64 || value > i32::max_value() as i64 { |
133 | IntKind::I64 |
134 | } else if !ctx.options().fit_macro_constants || |
135 | value < i16::min_value() as i64 || |
136 | value > i16::max_value() as i64 |
137 | { |
138 | IntKind::I32 |
139 | } else if value < i8::min_value() as i64 || |
140 | value > i8::max_value() as i64 |
141 | { |
142 | IntKind::I16 |
143 | } else { |
144 | IntKind::I8 |
145 | } |
146 | } else if value > u32::max_value() as i64 { |
147 | IntKind::U64 |
148 | } else if !ctx.options().fit_macro_constants || |
149 | value > u16::max_value() as i64 |
150 | { |
151 | IntKind::U32 |
152 | } else if value > u8::max_value() as i64 { |
153 | IntKind::U16 |
154 | } else { |
155 | IntKind::U8 |
156 | } |
157 | } |
158 | |
159 | /// Parses tokens from a CXCursor_MacroDefinition pointing into a function-like |
160 | /// macro, and calls the func_macro callback. |
161 | fn handle_function_macro( |
162 | cursor: &clang::Cursor, |
163 | callbacks: &dyn crate::callbacks::ParseCallbacks, |
164 | ) { |
165 | let is_closing_paren: impl Fn(&ClangToken) -> bool = |t: &ClangToken| { |
166 | // Test cheap token kind before comparing exact spellings. |
167 | t.kind == clang_sys::CXToken_Punctuation && t.spelling() == b")" |
168 | }; |
169 | let tokens: Vec<_> = cursor.tokens().iter().collect(); |
170 | if let Some(boundary: usize) = tokens.iter().position(is_closing_paren) { |
171 | let mut spelled: impl Iterator = tokens.iter().map(ClangToken::spelling); |
172 | // Add 1, to convert index to length. |
173 | let left: impl Iterator = spelled.by_ref().take(boundary + 1); |
174 | let left: Vec = left.collect::<Vec<_>>().concat(); |
175 | if let Ok(left: String) = String::from_utf8(vec:left) { |
176 | let right: Vec<_> = spelled.collect(); |
177 | callbacks.func_macro(&left, &right); |
178 | } |
179 | } |
180 | } |
181 | |
182 | impl ClangSubItemParser for Var { |
183 | fn parse( |
184 | cursor: clang::Cursor, |
185 | ctx: &mut BindgenContext, |
186 | ) -> Result<ParseResult<Self>, ParseError> { |
187 | use cexpr::expr::EvalResult; |
188 | use cexpr::literal::CChar; |
189 | use clang_sys::*; |
190 | match cursor.kind() { |
191 | CXCursor_MacroDefinition => { |
192 | for callbacks in &ctx.options().parse_callbacks { |
193 | match callbacks.will_parse_macro(&cursor.spelling()) { |
194 | MacroParsingBehavior::Ignore => { |
195 | return Err(ParseError::Continue); |
196 | } |
197 | MacroParsingBehavior::Default => {} |
198 | } |
199 | |
200 | if cursor.is_macro_function_like() { |
201 | handle_function_macro(&cursor, callbacks.as_ref()); |
202 | // We handled the macro, skip macro processing below. |
203 | return Err(ParseError::Continue); |
204 | } |
205 | } |
206 | |
207 | let value = parse_macro(ctx, &cursor); |
208 | |
209 | let (id, value) = match value { |
210 | Some(v) => v, |
211 | None => return Err(ParseError::Continue), |
212 | }; |
213 | |
214 | assert!(!id.is_empty(), "Empty macro name?" ); |
215 | |
216 | let previously_defined = ctx.parsed_macro(&id); |
217 | |
218 | // NB: It's important to "note" the macro even if the result is |
219 | // not an integer, otherwise we might loose other kind of |
220 | // derived macros. |
221 | ctx.note_parsed_macro(id.clone(), value.clone()); |
222 | |
223 | if previously_defined { |
224 | let name = String::from_utf8(id).unwrap(); |
225 | duplicated_macro_diagnostic(&name, cursor.location(), ctx); |
226 | return Err(ParseError::Continue); |
227 | } |
228 | |
229 | // NOTE: Unwrapping, here and above, is safe, because the |
230 | // identifier of a token comes straight from clang, and we |
231 | // enforce utf8 there, so we should have already panicked at |
232 | // this point. |
233 | let name = String::from_utf8(id).unwrap(); |
234 | let (type_kind, val) = match value { |
235 | EvalResult::Invalid => return Err(ParseError::Continue), |
236 | EvalResult::Float(f) => { |
237 | (TypeKind::Float(FloatKind::Double), VarType::Float(f)) |
238 | } |
239 | EvalResult::Char(c) => { |
240 | let c = match c { |
241 | CChar::Char(c) => { |
242 | assert_eq!(c.len_utf8(), 1); |
243 | c as u8 |
244 | } |
245 | CChar::Raw(c) => { |
246 | assert!(c <= ::std::u8::MAX as u64); |
247 | c as u8 |
248 | } |
249 | }; |
250 | |
251 | (TypeKind::Int(IntKind::U8), VarType::Char(c)) |
252 | } |
253 | EvalResult::Str(val) => { |
254 | let char_ty = Item::builtin_type( |
255 | TypeKind::Int(IntKind::U8), |
256 | true, |
257 | ctx, |
258 | ); |
259 | for callbacks in &ctx.options().parse_callbacks { |
260 | callbacks.str_macro(&name, &val); |
261 | } |
262 | (TypeKind::Pointer(char_ty), VarType::String(val)) |
263 | } |
264 | EvalResult::Int(Wrapping(value)) => { |
265 | let kind = ctx |
266 | .options() |
267 | .last_callback(|c| c.int_macro(&name, value)) |
268 | .unwrap_or_else(|| { |
269 | default_macro_constant_type(ctx, value) |
270 | }); |
271 | |
272 | (TypeKind::Int(kind), VarType::Int(value)) |
273 | } |
274 | }; |
275 | |
276 | let ty = Item::builtin_type(type_kind, true, ctx); |
277 | |
278 | Ok(ParseResult::New( |
279 | Var::new(name, None, None, ty, Some(val), true), |
280 | Some(cursor), |
281 | )) |
282 | } |
283 | CXCursor_VarDecl => { |
284 | let mut name = cursor.spelling(); |
285 | if cursor.linkage() == CXLinkage_External { |
286 | if let Some(nm) = ctx.options().last_callback(|callbacks| { |
287 | callbacks.generated_name_override(ItemInfo { |
288 | name: name.as_str(), |
289 | kind: ItemKind::Var, |
290 | }) |
291 | }) { |
292 | name = nm; |
293 | } |
294 | } |
295 | // No more changes to name |
296 | let name = name; |
297 | |
298 | if name.is_empty() { |
299 | warn!("Empty constant name?" ); |
300 | return Err(ParseError::Continue); |
301 | } |
302 | |
303 | let link_name = ctx.options().last_callback(|callbacks| { |
304 | callbacks.generated_link_name_override(ItemInfo { |
305 | name: name.as_str(), |
306 | kind: ItemKind::Var, |
307 | }) |
308 | }); |
309 | |
310 | let ty = cursor.cur_type(); |
311 | |
312 | // TODO(emilio): do we have to special-case constant arrays in |
313 | // some other places? |
314 | let is_const = ty.is_const() || |
315 | ([CXType_ConstantArray, CXType_IncompleteArray] |
316 | .contains(&ty.kind()) && |
317 | ty.elem_type() |
318 | .map_or(false, |element| element.is_const())); |
319 | |
320 | let ty = match Item::from_ty(&ty, cursor, None, ctx) { |
321 | Ok(ty) => ty, |
322 | Err(e) => { |
323 | assert!( |
324 | matches!(ty.kind(), CXType_Auto | CXType_Unexposed), |
325 | "Couldn't resolve constant type, and it \ |
326 | wasn't an nondeductible auto type or unexposed \ |
327 | type!" |
328 | ); |
329 | return Err(e); |
330 | } |
331 | }; |
332 | |
333 | // Note: Ty might not be totally resolved yet, see |
334 | // tests/headers/inner_const.hpp |
335 | // |
336 | // That's fine because in that case we know it's not a literal. |
337 | let canonical_ty = ctx |
338 | .safe_resolve_type(ty) |
339 | .and_then(|t| t.safe_canonical_type(ctx)); |
340 | |
341 | let is_integer = canonical_ty.map_or(false, |t| t.is_integer()); |
342 | let is_float = canonical_ty.map_or(false, |t| t.is_float()); |
343 | |
344 | // TODO: We could handle `char` more gracefully. |
345 | // TODO: Strings, though the lookup is a bit more hard (we need |
346 | // to look at the canonical type of the pointee too, and check |
347 | // is char, u8, or i8 I guess). |
348 | let value = if is_integer { |
349 | let kind = match *canonical_ty.unwrap().kind() { |
350 | TypeKind::Int(kind) => kind, |
351 | _ => unreachable!(), |
352 | }; |
353 | |
354 | let mut val = cursor.evaluate().and_then(|v| v.as_int()); |
355 | if val.is_none() || !kind.signedness_matches(val.unwrap()) { |
356 | val = get_integer_literal_from_cursor(&cursor); |
357 | } |
358 | |
359 | val.map(|val| { |
360 | if kind == IntKind::Bool { |
361 | VarType::Bool(val != 0) |
362 | } else { |
363 | VarType::Int(val) |
364 | } |
365 | }) |
366 | } else if is_float { |
367 | cursor |
368 | .evaluate() |
369 | .and_then(|v| v.as_double()) |
370 | .map(VarType::Float) |
371 | } else { |
372 | cursor |
373 | .evaluate() |
374 | .and_then(|v| v.as_literal_string()) |
375 | .map(VarType::String) |
376 | }; |
377 | |
378 | let mangling = cursor_mangling(ctx, &cursor); |
379 | let var = |
380 | Var::new(name, mangling, link_name, ty, value, is_const); |
381 | |
382 | Ok(ParseResult::New(var, Some(cursor))) |
383 | } |
384 | _ => { |
385 | /* TODO */ |
386 | Err(ParseError::Continue) |
387 | } |
388 | } |
389 | } |
390 | } |
391 | |
392 | /// Try and parse a macro using all the macros parsed until now. |
393 | fn parse_macro( |
394 | ctx: &BindgenContext, |
395 | cursor: &clang::Cursor, |
396 | ) -> Option<(Vec<u8>, cexpr::expr::EvalResult)> { |
397 | use cexpr::expr; |
398 | |
399 | let cexpr_tokens: Vec = cursor.cexpr_tokens(); |
400 | |
401 | let parser: IdentifierParser<'_> = expr::IdentifierParser::new(identifiers:ctx.parsed_macros()); |
402 | |
403 | match parser.macro_definition(&cexpr_tokens) { |
404 | Ok((_, (id: &[u8], val: EvalResult))) => Some((id.into(), val)), |
405 | _ => None, |
406 | } |
407 | } |
408 | |
409 | fn parse_int_literal_tokens(cursor: &clang::Cursor) -> Option<i64> { |
410 | use cexpr::expr; |
411 | use cexpr::expr::EvalResult; |
412 | |
413 | let cexpr_tokens: Vec = cursor.cexpr_tokens(); |
414 | |
415 | // TODO(emilio): We can try to parse other kinds of literals. |
416 | match expr::expr(&cexpr_tokens) { |
417 | Ok((_, EvalResult::Int(Wrapping(val: i64)))) => Some(val), |
418 | _ => None, |
419 | } |
420 | } |
421 | |
422 | fn get_integer_literal_from_cursor(cursor: &clang::Cursor) -> Option<i64> { |
423 | use clang_sys::*; |
424 | let mut value: Option = None; |
425 | cursor.visit(|c: Cursor| { |
426 | match c.kind() { |
427 | CXCursor_IntegerLiteral | CXCursor_UnaryOperator => { |
428 | value = parse_int_literal_tokens(&c); |
429 | } |
430 | CXCursor_UnexposedExpr => { |
431 | value = get_integer_literal_from_cursor(&c); |
432 | } |
433 | _ => (), |
434 | } |
435 | if value.is_some() { |
436 | CXChildVisit_Break |
437 | } else { |
438 | CXChildVisit_Continue |
439 | } |
440 | }); |
441 | value |
442 | } |
443 | |
444 | fn duplicated_macro_diagnostic( |
445 | macro_name: &str, |
446 | _location: crate::clang::SourceLocation, |
447 | _ctx: &BindgenContext, |
448 | ) { |
449 | warn!("Duplicated macro definition: {}" , macro_name); |
450 | |
451 | #[cfg (feature = "experimental" )] |
452 | // FIXME (pvdrz & amanjeev): This diagnostic message shows way too often to be actually |
453 | // useful. We have to change the logic where this function is called to be able to emit this |
454 | // message only when the duplication is an actuall issue. |
455 | // |
456 | // If I understood correctly, `bindgen` ignores all `#undef` directives. Meaning that this: |
457 | // ```c |
458 | // #define FOO 1 |
459 | // #undef FOO |
460 | // #define FOO 2 |
461 | // ``` |
462 | // |
463 | // Will trigger this message even though there's nothing wrong with it. |
464 | #[allow (clippy::overly_complex_bool_expr)] |
465 | if false && _ctx.options().emit_diagnostics { |
466 | use crate::diagnostics::{get_line, Diagnostic, Level, Slice}; |
467 | use std::borrow::Cow; |
468 | |
469 | let mut slice = Slice::default(); |
470 | let mut source = Cow::from(macro_name); |
471 | |
472 | let (file, line, col, _) = _location.location(); |
473 | if let Some(filename) = file.name() { |
474 | if let Ok(Some(code)) = get_line(&filename, line) { |
475 | source = code.into(); |
476 | } |
477 | slice.with_location(filename, line, col); |
478 | } |
479 | |
480 | slice.with_source(source); |
481 | |
482 | Diagnostic::default() |
483 | .with_title("Duplicated macro definition." , Level::Warn) |
484 | .add_slice(slice) |
485 | .add_annotation("This macro had a duplicate." , Level::Note) |
486 | .display(); |
487 | } |
488 | } |
489 | |