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: &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
127fn 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.
161fn 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
182impl 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.
393fn 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
409fn 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
422fn 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
444fn 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