• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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`.
new( name: String, mangled_name: Option<String>, link_name: Option<String>, ty: TypeId, val: Option<VarType>, is_const: bool, ) -> Var52     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?
is_const(&self) -> bool72     pub(crate) fn is_const(&self) -> bool {
73         self.is_const
74     }
75 
76     /// The value of this constant variable, if any.
val(&self) -> Option<&VarType>77     pub(crate) fn val(&self) -> Option<&VarType> {
78         self.val.as_ref()
79     }
80 
81     /// Get this variable's type.
ty(&self) -> TypeId82     pub(crate) fn ty(&self) -> TypeId {
83         self.ty
84     }
85 
86     /// Get this variable's name.
name(&self) -> &str87     pub(crate) fn name(&self) -> &str {
88         &self.name
89     }
90 
91     /// Get this variable's mangled name.
mangled_name(&self) -> Option<&str>92     pub(crate) fn mangled_name(&self) -> Option<&str> {
93         self.mangled_name.as_deref()
94     }
95 
96     /// Get this variable's link name.
link_name(&self) -> Option<&str>97     pub fn link_name(&self) -> Option<&str> {
98         self.link_name.as_deref()
99     }
100 }
101 
102 impl DotAttributes for Var {
dot_attributes<W>( &self, _ctx: &BindgenContext, out: &mut W, ) -> io::Result<()> where W: io::Write,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) = 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 
default_macro_constant_type(ctx: &BindgenContext, value: i64) -> IntKind127 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.
handle_function_macro( cursor: &clang::Cursor, callbacks: &dyn crate::callbacks::ParseCallbacks, )161 fn handle_function_macro(
162     cursor: &clang::Cursor,
163     callbacks: &dyn crate::callbacks::ParseCallbacks,
164 ) {
165     let is_closing_paren = |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) = tokens.iter().position(is_closing_paren) {
171         let mut spelled = tokens.iter().map(ClangToken::spelling);
172         // Add 1, to convert index to length.
173         let left = spelled.by_ref().take(boundary + 1);
174         let left = left.collect::<Vec<_>>().concat();
175         if let Ok(left) = String::from_utf8(left) {
176             let right: Vec<_> = spelled.collect();
177             callbacks.func_macro(&left, &right);
178         }
179     }
180 }
181 
182 impl ClangSubItemParser for Var {
parse( cursor: clang::Cursor, ctx: &mut BindgenContext, ) -> Result<ParseResult<Self>, ParseError>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.
parse_macro( ctx: &BindgenContext, cursor: &clang::Cursor, ) -> Option<(Vec<u8>, cexpr::expr::EvalResult)>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 = cursor.cexpr_tokens();
400 
401     let parser = expr::IdentifierParser::new(ctx.parsed_macros());
402 
403     match parser.macro_definition(&cexpr_tokens) {
404         Ok((_, (id, val))) => Some((id.into(), val)),
405         _ => None,
406     }
407 }
408 
parse_int_literal_tokens(cursor: &clang::Cursor) -> Option<i64>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 = 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)))) => Some(val),
418         _ => None,
419     }
420 }
421 
get_integer_literal_from_cursor(cursor: &clang::Cursor) -> Option<i64>422 fn get_integer_literal_from_cursor(cursor: &clang::Cursor) -> Option<i64> {
423     use clang_sys::*;
424     let mut value = None;
425     cursor.visit(|c| {
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 
duplicated_macro_diagnostic( macro_name: &str, _location: crate::clang::SourceLocation, _ctx: &BindgenContext, )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