bindgen/ir/
var.rs

1//! Intermediate representation of variables.
2
3use super::super::codegen::MacroTypeVariation;
4use super::context::{BindgenContext, TypeId};
5use super::dot::DotAttributes;
6use super::function::cursor_mangling;
7use super::int::IntKind;
8use super::item::Item;
9use super::ty::{FloatKind, TypeKind};
10use crate::callbacks::{ItemInfo, ItemKind, MacroParsingBehavior};
11use crate::clang;
12use crate::clang::ClangToken;
13use crate::parse::{ClangSubItemParser, ParseError, ParseResult};
14
15use std::io;
16use std::num::Wrapping;
17
18/// The type for a constant variable.
19#[derive(Debug)]
20pub(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)]
35pub(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
50impl 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
102impl 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) = self.mangled_name {
116            writeln!(out, "<tr><td>mangled name</td><td>{mangled}</td></tr>")?;
117        }
118
119        Ok(())
120    }
121}
122
123fn default_macro_constant_type(ctx: &BindgenContext, value: i64) -> IntKind {
124    if value < 0 ||
125        ctx.options().default_macro_constant_type ==
126            MacroTypeVariation::Signed
127    {
128        if value < i64::from(i32::MIN) || value > i64::from(i32::MAX) {
129            IntKind::I64
130        } else if !ctx.options().fit_macro_constants ||
131            value < i64::from(i16::MIN) ||
132            value > i64::from(i16::MAX)
133        {
134            IntKind::I32
135        } else if value < i64::from(i8::MIN) || value > i64::from(i8::MAX) {
136            IntKind::I16
137        } else {
138            IntKind::I8
139        }
140    } else if value > i64::from(u32::MAX) {
141        IntKind::U64
142    } else if !ctx.options().fit_macro_constants || value > i64::from(u16::MAX)
143    {
144        IntKind::U32
145    } else if value > i64::from(u8::MAX) {
146        IntKind::U16
147    } else {
148        IntKind::U8
149    }
150}
151
152/// Parses tokens from a `CXCursor_MacroDefinition` pointing into a function-like
153/// macro, and calls the `func_macro` callback.
154fn handle_function_macro(
155    cursor: &clang::Cursor,
156    callbacks: &dyn crate::callbacks::ParseCallbacks,
157) {
158    let is_closing_paren = |t: &ClangToken| {
159        // Test cheap token kind before comparing exact spellings.
160        t.kind == clang_sys::CXToken_Punctuation && t.spelling() == b")"
161    };
162    let tokens: Vec<_> = cursor.tokens().iter().collect();
163    if let Some(boundary) = tokens.iter().position(is_closing_paren) {
164        let mut spelled = tokens.iter().map(ClangToken::spelling);
165        // Add 1, to convert index to length.
166        let left = spelled.by_ref().take(boundary + 1);
167        let left = left.collect::<Vec<_>>().concat();
168        if let Ok(left) = String::from_utf8(left) {
169            let right: Vec<_> = spelled.collect();
170            callbacks.func_macro(&left, &right);
171        }
172    }
173}
174
175impl ClangSubItemParser for Var {
176    fn parse(
177        cursor: clang::Cursor,
178        ctx: &mut BindgenContext,
179    ) -> Result<ParseResult<Self>, ParseError> {
180        use cexpr::expr::EvalResult;
181        use cexpr::literal::CChar;
182        use clang_sys::*;
183        match cursor.kind() {
184            CXCursor_MacroDefinition => {
185                for callbacks in &ctx.options().parse_callbacks {
186                    match callbacks.will_parse_macro(&cursor.spelling()) {
187                        MacroParsingBehavior::Ignore => {
188                            return Err(ParseError::Continue);
189                        }
190                        MacroParsingBehavior::Default => {}
191                    }
192
193                    if cursor.is_macro_function_like() {
194                        handle_function_macro(&cursor, callbacks.as_ref());
195                        // We handled the macro, skip macro processing below.
196                        return Err(ParseError::Continue);
197                    }
198                }
199
200                let value = parse_macro(ctx, &cursor);
201
202                let Some((id, value)) = value else {
203                    return Err(ParseError::Continue);
204                };
205
206                assert!(!id.is_empty(), "Empty macro name?");
207
208                let previously_defined = ctx.parsed_macro(&id);
209
210                // NB: It's important to "note" the macro even if the result is
211                // not an integer, otherwise we might loose other kind of
212                // derived macros.
213                ctx.note_parsed_macro(id.clone(), value.clone());
214
215                if previously_defined {
216                    let name = String::from_utf8(id).unwrap();
217                    duplicated_macro_diagnostic(&name, cursor.location(), ctx);
218                    return Err(ParseError::Continue);
219                }
220
221                // NOTE: Unwrapping, here and above, is safe, because the
222                // identifier of a token comes straight from clang, and we
223                // enforce utf8 there, so we should have already panicked at
224                // this point.
225                let name = String::from_utf8(id).unwrap();
226                let (type_kind, val) = match value {
227                    EvalResult::Invalid => return Err(ParseError::Continue),
228                    EvalResult::Float(f) => {
229                        (TypeKind::Float(FloatKind::Double), VarType::Float(f))
230                    }
231                    EvalResult::Char(c) => {
232                        let c = match c {
233                            CChar::Char(c) => {
234                                assert_eq!(c.len_utf8(), 1);
235                                c as u8
236                            }
237                            CChar::Raw(c) => u8::try_from(c).unwrap(),
238                        };
239
240                        (TypeKind::Int(IntKind::U8), VarType::Char(c))
241                    }
242                    EvalResult::Str(val) => {
243                        let char_ty = Item::builtin_type(
244                            TypeKind::Int(IntKind::U8),
245                            true,
246                            ctx,
247                        );
248                        for callbacks in &ctx.options().parse_callbacks {
249                            callbacks.str_macro(&name, &val);
250                        }
251                        (TypeKind::Pointer(char_ty), VarType::String(val))
252                    }
253                    EvalResult::Int(Wrapping(value)) => {
254                        let kind = ctx
255                            .options()
256                            .last_callback(|c| c.int_macro(&name, value))
257                            .unwrap_or_else(|| {
258                                default_macro_constant_type(ctx, value)
259                            });
260
261                        (TypeKind::Int(kind), VarType::Int(value))
262                    }
263                };
264
265                let ty = Item::builtin_type(type_kind, true, ctx);
266
267                Ok(ParseResult::New(
268                    Var::new(name, None, None, ty, Some(val), true),
269                    Some(cursor),
270                ))
271            }
272            CXCursor_VarDecl => {
273                let mut name = cursor.spelling();
274                if cursor.linkage() == CXLinkage_External {
275                    if let Some(nm) = ctx.options().last_callback(|callbacks| {
276                        callbacks.generated_name_override(ItemInfo {
277                            name: name.as_str(),
278                            kind: ItemKind::Var,
279                        })
280                    }) {
281                        name = nm;
282                    }
283                }
284                // No more changes to name
285                let name = name;
286
287                if name.is_empty() {
288                    warn!("Empty constant name?");
289                    return Err(ParseError::Continue);
290                }
291
292                let link_name = ctx.options().last_callback(|callbacks| {
293                    callbacks.generated_link_name_override(ItemInfo {
294                        name: name.as_str(),
295                        kind: ItemKind::Var,
296                    })
297                });
298
299                let ty = cursor.cur_type();
300
301                // TODO(emilio): do we have to special-case constant arrays in
302                // some other places?
303                let is_const = ty.is_const() ||
304                    ([CXType_ConstantArray, CXType_IncompleteArray]
305                        .contains(&ty.kind()) &&
306                        ty.elem_type()
307                            .is_some_and(|element| element.is_const()));
308
309                let ty = match Item::from_ty(&ty, cursor, None, ctx) {
310                    Ok(ty) => ty,
311                    Err(e) => {
312                        assert!(
313                            matches!(ty.kind(), CXType_Auto | CXType_Unexposed),
314                            "Couldn't resolve constant type, and it \
315                             wasn't an nondeductible auto type or unexposed \
316                             type: {ty:?}"
317                        );
318                        return Err(e);
319                    }
320                };
321
322                // Note: Ty might not be totally resolved yet, see
323                // tests/headers/inner_const.hpp
324                //
325                // That's fine because in that case we know it's not a literal.
326                let canonical_ty = ctx
327                    .safe_resolve_type(ty)
328                    .and_then(|t| t.safe_canonical_type(ctx));
329
330                let is_integer = canonical_ty.is_some_and(|t| t.is_integer());
331                let is_float = canonical_ty.is_some_and(|t| t.is_float());
332
333                // TODO: We could handle `char` more gracefully.
334                // TODO: Strings, though the lookup is a bit more hard (we need
335                // to look at the canonical type of the pointee too, and check
336                // is char, u8, or i8 I guess).
337                let value = if is_integer {
338                    let TypeKind::Int(kind) = *canonical_ty.unwrap().kind()
339                    else {
340                        unreachable!()
341                    };
342
343                    let mut val = cursor.evaluate().and_then(|v| v.as_int());
344                    if val.is_none() || !kind.signedness_matches(val.unwrap()) {
345                        val = get_integer_literal_from_cursor(&cursor);
346                    }
347
348                    val.map(|val| {
349                        if kind == IntKind::Bool {
350                            VarType::Bool(val != 0)
351                        } else {
352                            VarType::Int(val)
353                        }
354                    })
355                } else if is_float {
356                    cursor
357                        .evaluate()
358                        .and_then(|v| v.as_double())
359                        .map(VarType::Float)
360                } else {
361                    cursor
362                        .evaluate()
363                        .and_then(|v| v.as_literal_string())
364                        .map(VarType::String)
365                };
366
367                let mangling = cursor_mangling(ctx, &cursor);
368                let var =
369                    Var::new(name, mangling, link_name, ty, value, is_const);
370
371                Ok(ParseResult::New(var, Some(cursor)))
372            }
373            _ => {
374                /* TODO */
375                Err(ParseError::Continue)
376            }
377        }
378    }
379}
380
381/// This function uses a [`FallbackTranslationUnit`][clang::FallbackTranslationUnit] to parse each
382/// macro that cannot be parsed by the normal bindgen process for `#define`s.
383///
384/// To construct the [`FallbackTranslationUnit`][clang::FallbackTranslationUnit], first precompiled
385/// headers are generated for all input headers. An empty temporary `.c` file is generated to pass
386/// to the translation unit. On the evaluation of each macro, a [`String`] is generated with the
387/// new contents of the empty file and passed in for reparsing. The precompiled headers and
388/// preservation of the [`FallbackTranslationUnit`][clang::FallbackTranslationUnit] across macro
389/// evaluations are both optimizations that have significantly improved the performance.
390fn parse_macro_clang_fallback(
391    ctx: &mut BindgenContext,
392    cursor: &clang::Cursor,
393) -> Option<(Vec<u8>, cexpr::expr::EvalResult)> {
394    if !ctx.options().clang_macro_fallback {
395        return None;
396    }
397
398    let ftu = ctx.try_ensure_fallback_translation_unit()?;
399    let contents = format!("int main() {{ {}; }}", cursor.spelling());
400    ftu.reparse(&contents).ok()?;
401    // Children of root node of AST
402    let root_children = ftu.translation_unit().cursor().collect_children();
403    // Last child in root is function declaration
404    // Should be FunctionDecl
405    let main_func = root_children.last()?;
406    // Children should all be statements in function declaration
407    let all_stmts = main_func.collect_children();
408    // First child in all_stmts should be the statement containing the macro to evaluate
409    // Should be CompoundStmt
410    let macro_stmt = all_stmts.first()?;
411    // Children should all be expressions from the compound statement
412    let paren_exprs = macro_stmt.collect_children();
413    // First child in all_exprs is the expression utilizing the given macro to be evaluated
414    // Should  be ParenExpr
415    let paren = paren_exprs.first()?;
416
417    Some((
418        cursor.spelling().into_bytes(),
419        cexpr::expr::EvalResult::Int(Wrapping(paren.evaluate()?.as_int()?)),
420    ))
421}
422
423/// Try and parse a macro using all the macros parsed until now.
424fn parse_macro(
425    ctx: &mut BindgenContext,
426    cursor: &clang::Cursor,
427) -> Option<(Vec<u8>, cexpr::expr::EvalResult)> {
428    use cexpr::expr;
429
430    let mut cexpr_tokens = cursor.cexpr_tokens();
431
432    for callbacks in &ctx.options().parse_callbacks {
433        callbacks.modify_macro(&cursor.spelling(), &mut cexpr_tokens);
434    }
435
436    let parser = expr::IdentifierParser::new(ctx.parsed_macros());
437
438    match parser.macro_definition(&cexpr_tokens) {
439        Ok((_, (id, val))) => Some((id.into(), val)),
440        _ => parse_macro_clang_fallback(ctx, cursor),
441    }
442}
443
444fn parse_int_literal_tokens(cursor: &clang::Cursor) -> Option<i64> {
445    use cexpr::expr;
446    use cexpr::expr::EvalResult;
447
448    let cexpr_tokens = cursor.cexpr_tokens();
449
450    // TODO(emilio): We can try to parse other kinds of literals.
451    match expr::expr(&cexpr_tokens) {
452        Ok((_, EvalResult::Int(Wrapping(val)))) => Some(val),
453        _ => None,
454    }
455}
456
457fn get_integer_literal_from_cursor(cursor: &clang::Cursor) -> Option<i64> {
458    use clang_sys::*;
459    let mut value = None;
460    cursor.visit(|c| {
461        match c.kind() {
462            CXCursor_IntegerLiteral | CXCursor_UnaryOperator => {
463                value = parse_int_literal_tokens(&c);
464            }
465            CXCursor_UnexposedExpr => {
466                value = get_integer_literal_from_cursor(&c);
467            }
468            _ => (),
469        }
470        if value.is_some() {
471            CXChildVisit_Break
472        } else {
473            CXChildVisit_Continue
474        }
475    });
476    value
477}
478
479fn duplicated_macro_diagnostic(
480    macro_name: &str,
481    _location: clang::SourceLocation,
482    _ctx: &BindgenContext,
483) {
484    warn!("Duplicated macro definition: {macro_name}");
485
486    #[cfg(feature = "experimental")]
487    // FIXME (pvdrz & amanjeev): This diagnostic message shows way too often to be actually
488    // useful. We have to change the logic where this function is called to be able to emit this
489    // message only when the duplication is an actual issue.
490    //
491    // If I understood correctly, `bindgen` ignores all `#undef` directives. Meaning that this:
492    // ```c
493    // #define FOO 1
494    // #undef FOO
495    // #define FOO 2
496    // ```
497    //
498    // Will trigger this message even though there's nothing wrong with it.
499    #[allow(clippy::overly_complex_bool_expr)]
500    if false && _ctx.options().emit_diagnostics {
501        use crate::diagnostics::{get_line, Diagnostic, Level, Slice};
502        use std::borrow::Cow;
503
504        let mut slice = Slice::default();
505        let mut source = Cow::from(macro_name);
506
507        let (file, line, col, _) = _location.location();
508        if let Some(filename) = file.name() {
509            if let Ok(Some(code)) = get_line(&filename, line) {
510                source = code.into();
511            }
512            slice.with_location(filename, line, col);
513        }
514
515        slice.with_source(source);
516
517        Diagnostic::default()
518            .with_title("Duplicated macro definition.", Level::Warning)
519            .add_slice(slice)
520            .add_annotation("This macro had a duplicate.", Level::Note)
521            .display();
522    }
523}