1use 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#[derive(Debug)]
20pub(crate) enum VarType {
21 Bool(bool),
23 Int(i64),
25 Float(f64),
27 Char(u8),
29 String(Vec<u8>),
31}
32
33#[derive(Debug)]
35pub(crate) struct Var {
36 name: String,
38 mangled_name: Option<String>,
40 link_name: Option<String>,
42 ty: TypeId,
44 val: Option<VarType>,
46 is_const: bool,
48}
49
50impl Var {
51 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 pub(crate) fn is_const(&self) -> bool {
73 self.is_const
74 }
75
76 pub(crate) fn val(&self) -> Option<&VarType> {
78 self.val.as_ref()
79 }
80
81 pub(crate) fn ty(&self) -> TypeId {
83 self.ty
84 }
85
86 pub(crate) fn name(&self) -> &str {
88 &self.name
89 }
90
91 pub(crate) fn mangled_name(&self) -> Option<&str> {
93 self.mangled_name.as_deref()
94 }
95
96 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
152fn handle_function_macro(
155 cursor: &clang::Cursor,
156 callbacks: &dyn crate::callbacks::ParseCallbacks,
157) {
158 let is_closing_paren = |t: &ClangToken| {
159 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 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 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 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 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 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 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 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 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 Err(ParseError::Continue)
376 }
377 }
378 }
379}
380
381fn 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 let root_children = ftu.translation_unit().cursor().collect_children();
403 let main_func = root_children.last()?;
406 let all_stmts = main_func.collect_children();
408 let macro_stmt = all_stmts.first()?;
411 let paren_exprs = macro_stmt.collect_children();
413 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
423fn 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 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 #[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}