diff --git a/Cargo.toml b/Cargo.toml index 433aead0d..be7b44afe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,8 @@ clang_7_0 = ["clang-sys/clang_7_0", "clang_6_0"] clang_8_0 = ["clang-sys/clang_8_0", "clang_7_0"] clang_9_0 = ["clang-sys/clang_9_0", "clang_8_0"] clang_10_0 = ["clang-sys/clang_10_0", "clang_9_0"] +clang_16_0 = ["clang-sys/clang_16_0", "clang_10_0"] +clang_17_0 = ["clang-sys/clang_17_0", "clang_16_0"] runtime = ["clang-sys/runtime"] static = ["clang-sys/static"] @@ -45,4 +47,4 @@ harness = true [package.metadata.docs.rs] -features = ["clang_10_0"] +features = ["clang_17_0"] diff --git a/README.md b/README.md index 4ff6e1f99..f0d57cb18 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ To target a version of `libclang`, enable one of the following Cargo features: * `clang_8_0` - requires `libclang` 8.0 or later * `clang_9_0` - requires `libclang` 9.0 or later * `clang_10_0` - requires `libclang` 10.0 or later +* `clang_16_0` - requires `libclang` 16.0 or later If you do not enable one of these features, the API provided by `libclang` 3.5 will be available by default. diff --git a/src/lib.rs b/src/lib.rs index dd37c7c14..ac41279ba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,6 +49,9 @@ use clang_sys::*; use libc::{c_int, c_uint, c_ulong}; +#[cfg(feature="clang_17_0")] +use libc::c_uchar; + use completion::{Completer, CompletionString}; use diagnostic::{Diagnostic}; use documentation::{Comment}; @@ -111,6 +114,102 @@ impl Availability { } } +// BinaryOperatorKind ____________________________ + +/// Indicates the kind of binary operators. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[repr(C)] +#[cfg(feature = "clang_17_0")] +pub enum BinaryOperatorKind { + /// The cursor is not a BinaryOperator. + Invalid = 0, + /// A C++ pointer to member operator. + PtrMemD = 1, + /// A C++ pointer to member operator. + PtrMemI = 2, + /// The multiplication operator. + Mul = 3, + /// The division operator. + Div = 4, + /// The remainder (modulo) operator. + Rem = 5, + /// The addition operator. + Add = 6, + /// The subtraction operator. + Sub = 7, + /// The bitwise left shift operator. + Shl = 8, + /// The bitwise right shift operator. + Shr = 9, + /// The C++ three-way comparison (spaceship) operator. + Cmp = 10, + /// The less than operator. + LT = 11, + /// The greater than operator. + GT = 12, + /// The less than or equal operator. + LE = 13, + /// The greater than or equal operator. + GE = 14, + /// The equal comparison operator. + EQ = 15, + /// The not equal operator. + NE = 16, + /// The bitwise AND operator. + And = 17, + /// The bitwise XOR operator. + Xor = 18, + /// The bitwise OR operator. + Or = 19, + /// The logical (comparison) AND operator. + LAnd = 20, + /// The logical (comparison) OR operator. + LOr = 21, + /// The assignment operator. + Assign = 22, + /// The multiplication assignment operator. + MulAssign = 23, + /// The division assignment operator. + DivAssign = 24, + /// The remainder (modulo) assignment operator. + RemAssign = 25, + /// The addition assignment operator. + AddAssign = 26, + /// The subtraction assignment operator. + SubAssign = 27, + /// The bitwise left shift assignment operator. + ShlAssign = 28, + /// The bitwise right shift assignment operator. + ShrAssign = 29, + /// The bitwise AND assignment operator. + AndAssign = 30, + /// The bitwise XOR assignment operator. + XorAssign = 31, + /// The bitwise OR assignment operator. + OrAssign = 32, + /// The comma operator. + Comma = 33, +} + +#[cfg(feature = "clang_17_0")] +impl BinaryOperatorKind { + fn from_raw(raw: c_int) -> Option { + match raw { + 0..=33 => Some(unsafe { mem::transmute(raw) }), + _ => None, + } + } + + fn from_raw_infallible(raw: c_int) -> Self { + Self::from_raw(raw).unwrap_or(BinaryOperatorKind::Invalid) + } + + /// Returns the name of this operator, if any. + pub fn get_name(&self) -> Option { + unsafe { utility::to_string_option(clang_getBinaryOperatorKindSpelling(*self as CXBinaryOperatorKind)) } + } +} + // CallingConvention _____________________________ /// Indicates the calling convention specified for a function type. @@ -171,6 +270,21 @@ impl CallingConvention { } } +// Choice ________________________________________ + +/// Indicates if an option should be enabled or disabled. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[repr(C)] +#[cfg(feature = "clang_17_0")] +pub enum Choice { + /// Use the default value of an option. + Default = 0, + /// Enable the option. + Enabled = 1, + /// Disable the option. + Disabled = 2, +} + // EntityKind ____________________________________ /// Indicates the categorization of an AST entity. @@ -644,7 +758,7 @@ pub enum EntityKind { /// Only produced by `libclang` 10.0 and later. OmpParallelMasterDirective = 285, /// The top-level AST entity which acts as the root for the other entitys. - TranslationUnit = 300, + TranslationUnit = if cfg!(feature="clang_16_0") { 350 } else { 300 }, /// An attribute whose specific kind is not exposed via this interface. UnexposedAttr = 400, /// An attribute applied to an Objective-C IBAction. @@ -809,7 +923,7 @@ pub enum EntityKind { impl EntityKind { fn from_raw(raw: c_int) -> Option { match raw { - 1..=50 | 70..=73 | 100..=149 | 200..=280 | 300 | 400..=441 | 500..=503 | 600..=603 + 1..=50 | 70..=73 | 100..=149 | 200..=280 | 300 | 350 | 400..=441 | 500..=503 | 600..=603 | 700 => { Some(unsafe { mem::transmute(raw) }) } @@ -1583,6 +1697,64 @@ impl TypeKind { } } +// UnaryOperatorKind _____________________________ + +/// Indicates the kind of unary operators. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[repr(C)] +#[cfg(feature = "clang_17_0")] +pub enum UnaryOperatorKind { + /// The cursor is not a UnaryOperator. + Invalid = 0, + /// The postfix increment operator. + PostInc = 1, + /// The postfix decrement operator. + PostDec = 2, + /// The prefix increment operator. + PreInc = 3, + /// The prefix decrement operator. + PreDec = 4, + /// The address of (reference) operator. + AddrOf = 5, + /// The dereference operator. + Deref = 6, + /// The plus operator. + Plus = 7, + /// The minus operator. + Minus = 8, + /// The bitwise NOT operator. + Not = 9, + /// The logical NOT operator. + LNot = 10, + /// The `__real expr` operator. + Real = 11, + /// The `__imag expr` operator. + Imag = 12, + /// The extension marker operator. + Extension = 13, + /// The C++ `co_await` operator + Coawait = 14, +} + +#[cfg(feature = "clang_17_0")] +impl UnaryOperatorKind { + fn from_raw(raw: c_int) -> Option { + match raw { + 0..=14 => Some(unsafe { mem::transmute(raw) }), + _ => None, + } + } + + fn from_raw_infallible(raw: c_int) -> Self { + Self::from_raw(raw).unwrap_or(UnaryOperatorKind::Invalid) + } + + /// Returns the name of this operator, if any. + pub fn get_name(&self) -> Option { + unsafe { utility::to_string_option(clang_getUnaryOperatorKindSpelling(*self as CXUnaryOperatorKind)) } + } +} + // Visibility ____________________________________ /// Indicates the linker visibility of an AST element. @@ -1905,6 +2077,12 @@ impl<'tu> Entity<'tu> { pub fn get_availability(&self) -> Availability { Availability::from_raw(unsafe {clang_getCursorAvailability(self.raw) }).unwrap() } + + /// Returns the binary operator kind of this binary operator. + #[cfg(feature = "clang_17_0")] + pub fn get_binary_operator_kind(&self) -> BinaryOperatorKind { + BinaryOperatorKind::from_raw_infallible(unsafe { clang_getCursorBinaryOperatorKind(self.raw) }) + } /// Returns the width of this bit field, if applicable. pub fn get_bit_field_width(&self) -> Option { @@ -2322,6 +2500,12 @@ impl<'tu> Entity<'tu> { unsafe { clang_getTypedefDeclUnderlyingType(self.raw).map(|t| Type::from_raw(t, self.tu)) } } + /// Returns the unary operator kind of this unary operator. + #[cfg(feature = "clang_17_0")] + pub fn get_unary_operator_kind(&self) -> UnaryOperatorKind { + UnaryOperatorKind::from_raw_infallible(unsafe { clang_getCursorUnaryOperatorKind(self.raw) }) + } + /// Returns the USR for this AST entity, if any. pub fn get_usr(&self) -> Option { unsafe { utility::to_string_option(clang_getCursorUSR(self.raw)).map(Usr) } @@ -2417,6 +2601,12 @@ impl<'tu> Entity<'tu> { unsafe { clang_CXXMethod_isDefaulted(self.raw) != 0 } } + /// Returns whether this AST entity is a C++ deleted method. + #[cfg(feature="clang_16_0")] + pub fn is_deleted(&self) -> bool { + unsafe { clang_CXXMethod_isDeleted(self.raw) != 0 } + } + /// Returns whether this AST entity is a declaration and also the definition of that /// declaration. pub fn is_definition(&self) -> bool { @@ -2431,6 +2621,12 @@ impl<'tu> Entity<'tu> { unsafe { clang_Cursor_isDynamicCall(self.raw) != 0 } } + /// Returns whether this AST entity is a constructor or conversion method declared explicit. + #[cfg(feature="clang_17_0")] + pub fn is_explicit(&self) -> bool { + unsafe { clang_CXXMethod_isExplicit(self.raw) != 0 } + } + /// Returns whether this AST entity is a function-like macro. #[cfg(feature="clang_3_9")] pub fn is_function_like_macro(&self) -> bool { @@ -2498,6 +2694,18 @@ impl<'tu> Entity<'tu> { unsafe { clang_CXXMethod_isVirtual(self.raw) != 0 } } + /// Returns whether this AST entity is a copy-assignment operator. + #[cfg(feature="clang_16_0")] + pub fn is_copy_assignment_operator(&self) -> bool { + unsafe { clang_CXXMethod_isCopyAssignmentOperator(self.raw) != 0 } + } + + /// Returns whether this AST entity is a move-assignment operator. + #[cfg(feature="clang_16_0")] + pub fn is_move_assignment_operator(&self) -> bool { + unsafe { clang_CXXMethod_isMoveAssignmentOperator(self.raw) != 0 } + } + /// Visits the children of this AST entity recursively and returns whether visitation was ended /// by the callback returning `EntityVisitResult::Break`. /// @@ -2652,6 +2860,14 @@ impl<'c> Index<'c> { unsafe { Index::from_ptr(clang_createIndex(exclude as c_int, diagnostics as c_int)) } } + /// Constructs a new `Index` with options. + /// + /// `options` determines the options associated with the newly created `Index`. + #[cfg(feature="clang_17_0")] + pub fn new_with_options(_: &'c Clang, options: &IndexOptions) -> Index<'c> { + unsafe { Index::from_ptr(clang_createIndexWithOptions(options.as_raw())) } + } + //- Accessors -------------------------------- /// Returns a parser for the supplied file. @@ -2693,6 +2909,82 @@ impl<'c> fmt::Debug for Index<'c> { } } +// IndexOptions __________________________________ + +/// Options to explicitly initialize an `Index`. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[cfg(feature="clang_17_0")] +pub struct IndexOptions { + /// An enumerator indicating the indexing priority policy. + pub thread_background_priority_for_indexing: Choice, + /// An enumerator indicating the editing priority policy. + pub thread_background_priority_for_editing: Choice, + /// Specifies if declarations in precompiled headers should be ignored. + pub exclude_declarations_from_pch: bool, + /// Specifies if diagnostics should be displayed. + pub display_diagnostics: bool, + /// Specifies if precompiled headers should be stored in memory. + pub store_preambles_in_memory: bool, + /// Path to directory where temporary precompiled headers should be stored. + /// Ignored if `store_preambles_in_memory` is `true`. + pub preamble_storage_path: Option, + /// Path to a directory where certain `libclang` invocations will place logs. + pub invocation_emission_path: Option, +} + +#[cfg(feature="clang_17_0")] +impl IndexOptions { + //- Constructors ----------------------------- + + /// Constructs a new `IndexOptions`. + pub fn new( + thread_background_priority_for_indexing: Choice, + thread_background_priority_for_editing: Choice, + exclude_declarations_from_pch: bool, + display_diagnostics: bool, + store_preambles_in_memory: bool, + preamble_storage_path: Option<&Path>, + invocation_emission_path: Option<&Path> + ) -> IndexOptions { + IndexOptions { + thread_background_priority_for_indexing, + thread_background_priority_for_editing, + exclude_declarations_from_pch, + display_diagnostics, + store_preambles_in_memory, + preamble_storage_path: preamble_storage_path.map(utility::from_path), + invocation_emission_path: invocation_emission_path.map(utility::from_path) + } + } + + //- Accessors -------------------------------- + + fn as_raw(&self) -> CXIndexOptions { + let mut flags = 0; + + if self.exclude_declarations_from_pch { + flags |= CXIndexOptions_ExcludeDeclarationsFromPCH; + } + + if self.display_diagnostics { + flags |= CXIndexOptions_DisplayDiagnostics; + } + + if self.store_preambles_in_memory { + flags |= CXIndexOptions_StorePreamblesInMemory; + } + + CXIndexOptions { + Size: mem::size_of::() as c_uint, + ThreadBackgroundPriorityForIndexing: self.thread_background_priority_for_indexing as c_uchar, + ThreadBackgroundPriorityForEditing: self.thread_background_priority_for_editing as c_uchar, + flags, + PreambleStoragePath: self.preamble_storage_path.as_ref().map_or_else(ptr::null, |s| s.as_ptr()), + InvocationEmissionPath: self.invocation_emission_path.as_ref().map_or_else(ptr::null, |s| s.as_ptr()), + } + } +} + // ObjCAttributes ________________________________ options! { @@ -3339,6 +3631,20 @@ impl<'tu> Type<'tu> { unsafe { clang_getPointeeType(self.raw).map(|t| Type::from_raw(t, self.tu)) } } + /// Returns the unqualified variant of this type, removing as little sugar as possible, or, if + /// this is type is not qualified, returns this type. + #[cfg(feature="clang_16_0")] + pub fn get_unqualified_type(&self) -> Type<'tu> { + unsafe { Type::from_raw(clang_getUnqualifiedType(self.raw), self.tu) } + } + + /// Returns the type that this reference refers to, or, if this is not a reference type, returns + /// this type. + #[cfg(feature="clang_16_0")] + pub fn get_non_reference_type(&self) -> Type<'tu> { + unsafe { Type::from_raw(clang_getNonReferenceType(self.raw), self.tu) } + } + /// Returns the ref qualifier for this C++ function or method type, if applicable. pub fn get_ref_qualifier(&self) -> Option { unsafe { diff --git a/tests/diagnostic.rs b/tests/diagnostic.rs index a2bfcb516..ae7cbd0b7 100644 --- a/tests/diagnostic.rs +++ b/tests/diagnostic.rs @@ -40,10 +40,13 @@ pub fn test(clang: &Clang) { ], &[ ]); - let text = "missing 'typename' prior to dependent type name 'T::U'"; - assert_diagnostic_eq!(diagnostics[1], Severity::Error, text, file.get_location(3, 50), &[ - range!(file, 3, 50, 3, 54) - ], &[ + let (severity, text, range) = if cfg!(feature="clang_16_0") { + (Severity::Warning, "missing 'typename' prior to dependent type name T::U; implicit 'typename' is a C++20 extension", Vec::new()) + } else { + (Severity::Error, "missing 'typename' prior to dependent type name 'T::U'", vec![range!(file, 3, 50, 3, 54)]) + }; + + assert_diagnostic_eq!(diagnostics[1], severity, text, file.get_location(3, 50), range, &[ FixIt::Insertion(file.get_location(3, 50), "typename ".into()) ]); diff --git a/tests/sonar.rs b/tests/sonar.rs index ef7a5b317..2cee2f5df 100644 --- a/tests/sonar.rs +++ b/tests/sonar.rs @@ -31,7 +31,7 @@ pub fn test(clang: &Clang) { let tu = index.parser(f).detailed_preprocessing_record(true).parse().unwrap(); let definitions = sonar::find_definitions(tu.get_entity().get_children()).filter(|d| { - !d.entity.is_in_system_header() + d.entity.is_in_main_file() }).collect::>(); assert_eq!(definitions.len(), 4); @@ -90,7 +90,7 @@ pub fn test(clang: &Clang) { assert_declaration_eq!(&enums[0], "A", SAME); assert_declaration_eq!(&enums[1], "B", SAME); - assert_declaration_eq!(&enums[2], "C", DIFFERENT); + assert_declaration_eq!(&enums[2], "C", SAME); assert_declaration_eq!(&enums[3], "D", SAME); }); @@ -151,7 +151,7 @@ pub fn test(clang: &Clang) { assert_declaration_eq!(&structs[0], "A", SAME); assert_declaration_eq!(&structs[1], "B", SAME); - assert_declaration_eq!(&structs[2], "C", DIFFERENT); + assert_declaration_eq!(&structs[2], "C", SAME); assert_declaration_eq!(&structs[3], "D", SAME); }); diff --git a/tests/tests.rs b/tests/tests.rs index bc4744c0e..57caa5f6c 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -337,8 +337,8 @@ fn test() { assert_eq!(children.len(), 2); assert_eq!(children[0].get_bit_field_width(), None); - assert_eq!(children[0].get_name(), None); - assert_eq!(children[0].get_display_name(), None); + assert!(children[0].get_name().is_some_and(|n| n.starts_with("(anonymous struct at "))); + assert!(children[0].get_display_name().is_some_and(|n| n.starts_with("(anonymous struct at "))); assert!(!children[0].is_bit_field()); if !cfg!(target_os="windows") { @@ -439,6 +439,40 @@ fn test() { assert_eq!(tu.get_file(&fs[1]).unwrap().get_includes(), &[last]); }); + with_temporary_files(files, |_, fs| { + + #[cfg(feature="clang_17_0")] + fn test_index_with_options(clang: &Clang, fs: &[PathBuf]) { + let options = IndexOptions::new( + Choice::Enabled, + Choice::Disabled, + true, + false, + true, + None, + None + ); + + let index = Index::new_with_options(&clang, &options); + + assert!(index.get_thread_options().indexing); + assert!(!index.get_thread_options().editing); + + let tu = index.parser(&fs[1]).detailed_preprocessing_record(true).parse().unwrap(); + + let last = tu.get_entity().get_children().iter().last().unwrap().clone(); + assert_eq!(last.get_kind(), EntityKind::InclusionDirective); + assert_eq!(last.get_file(), tu.get_file(&fs[0])); + + assert_eq!(tu.get_file(&fs[1]).unwrap().get_includes(), &[last]); + } + + #[cfg(not(feature="clang_17_0"))] + fn test_index_with_options(_clang: &Clang, _fs: &[PathBuf]) {} + + test_index_with_options(&clang, &fs[..]); + }); + let source = " void a() { } class B { void b() { } }; @@ -721,6 +755,66 @@ fn test() { test_constructors(&children); }); + let source = " + struct A { + void a(); + void b() = delete; + }; + "; + + with_entity(&clang, source, |e| { + #[cfg(feature="clang_16_0")] + fn test_is_deleted<'tu>(children: &[Entity<'tu>]) { + assert!(!children[0].is_deleted()); + assert!(children[1].is_deleted()); + } + + #[cfg(not(feature="clang_16_0"))] + fn test_is_deleted<'tu>(_: &[Entity<'tu>]) { } + + let children = e.get_children()[0].get_children(); + assert_eq!(children.len(), 2); + + test_is_deleted(&children); + }); + + let source = " + struct A { + A& operator=(A other) { return *this; } + A& operator=(A& other) { return *this; } + A& operator=(const A& other) { return *this; } + A& operator=(volatile A& other) { return *this; } + A& operator=(const volatile A& other) { return *this; } + + A& operator=(const A&& other) { return *this; } + A& operator=(volatile A&& other) { return *this; } + A& operator=(const volatile A&& other) { return *this; } + }; + "; + + with_entity(&clang, source, |e| { + #[cfg(feature="clang_16_0")] + fn test_assignment_operators<'tu>(children: &[Entity<'tu>]) { + for (i, child) in children.iter().enumerate() { + if i < 5 { + assert!(child.is_copy_assignment_operator()); + assert!(!child.is_move_assignment_operator()); + } else { + assert!(child.is_move_assignment_operator()); + assert!(!child.is_copy_assignment_operator()); + } + } + } + + #[cfg(not(feature="clang_16_0"))] + fn test_assignment_operators<'tu>(_: &[Entity<'tu>]) { } + + let children = e.get_children()[0].get_children(); + assert_eq!(children.len(), 8); + + test_assignment_operators(&children); + }); + let source = " struct A { void a() { } @@ -742,6 +836,34 @@ fn test() { assert!(children[2].is_dynamic_call()); }); + let source = " + class A { + A(int b); + operator int(); + + explicit A(float b); + explicit operator float(); + } + "; + + with_entity(&clang, source, |e| { + #[cfg(feature="clang_17_0")] + fn test_is_explicit(children: &[Entity]) { + assert!(!children[0].is_explicit()); + assert!(!children[1].is_explicit()); + assert!(children[2].is_explicit()); + assert!(children[3].is_explicit()); + } + + #[cfg(not(feature="clang_17_0"))] + fn test_is_explicit(_: &[Entity]) { } + + let children = e.get_children()[0].get_children(); + assert_eq!(children.len(), 4); + + test_is_explicit(&children[..]); + }); + let source = r#" void a(); void b() @@ -795,6 +917,62 @@ fn test() { test_is_mutable(&children); }); + let source = " + int main() { + int a, b; + a = 322; + b = a + 322; + b <<= 9; + return 0; + } + "; + + with_entity(&clang, source, |e| { + #[cfg(feature="clang_17_0")] + fn test_binary_operator_kind<'tu>(children: &[Entity<'tu>]) { + assert_eq!(children[1].get_binary_operator_kind(), BinaryOperatorKind::Assign); + assert_eq!(children[2].get_binary_operator_kind(), BinaryOperatorKind::Assign); + assert_eq!(children[2].get_children()[1].get_binary_operator_kind(), BinaryOperatorKind::Add); + assert_eq!(children[3].get_binary_operator_kind(), BinaryOperatorKind::ShlAssign); + assert_eq!(children[4].get_binary_operator_kind(), BinaryOperatorKind::Invalid); + } + + #[cfg(not(feature="clang_17_0"))] + fn test_binary_operator_kind<'tu>(_: &[Entity<'tu>]) { } + + let children = e.get_children()[0].get_children()[0].get_children(); + assert_eq!(children.len(), 5); + + test_binary_operator_kind(&children); + }); + + let source = " + int main() { + int a = 322; + int *b = &a; + a = -*b; + return 0; + } + "; + + with_entity(&clang, source, |e| { + #[cfg(feature="clang_17_0")] + fn test_unary_operator_kind<'tu>(children: &[Entity<'tu>]) { + assert_eq!(children[1].get_children()[0].get_children()[0].get_unary_operator_kind(), UnaryOperatorKind::AddrOf); + assert_eq!(children[2].get_children()[1].get_unary_operator_kind(), UnaryOperatorKind::Minus); + assert_eq!(children[2].get_children()[1].get_children()[0].get_children()[0].get_unary_operator_kind(), UnaryOperatorKind::Deref); + assert_eq!(children[3].get_unary_operator_kind(), UnaryOperatorKind::Invalid); + } + + #[cfg(not(feature="clang_17_0"))] + fn test_unary_operator_kind<'tu>(_: &[Entity<'tu>]) { } + + let children = e.get_children()[0].get_children()[0].get_children(); + assert_eq!(children.len(), 4); + + test_unary_operator_kind(&children); + }); + let source = " void a() { } void b(...) { } @@ -908,6 +1086,31 @@ fn test() { test_is_invalid_declaration(children[0]); }); + let source = " + int x = 0; + const int x = 0; + volatile int x = 0; + const volatile int x = 0; + "; + + with_entity(&clang, source, |e| { + let children = e.get_children(); + + #[cfg(feature="clang_16_0")] + fn test_get_unqualified_type<'tu>(children: &[Entity<'tu>]) { + let types = children.iter().map(|it| it.get_type().unwrap()).collect::>(); + assert_eq!(types[3].get_unqualified_type(), types[0]); + assert_eq!(types[2].get_unqualified_type(), types[0]); + assert_eq!(types[1].get_unqualified_type(), types[0]); + assert_eq!(types[0].get_unqualified_type(), types[0]); + } + + #[cfg(not(feature="clang_16_0"))] + fn test_get_unqualified_type<'tu>(_: &[Entity<'tu>]) {} + + test_get_unqualified_type(&children); + }); + let source = " int main() { return 0; @@ -1318,6 +1521,32 @@ fn test() { ]) }); + let source = " + int a = 322; + const b = 456; + int& c = a; + const int& d = b; + int&& e = a; + int&& f = c; + "; + + with_types(&clang, source, |ts| { + #[cfg(feature="clang_16_0")] + fn test_get_non_reference_type(ts: &[Type]) { + assert_eq!(ts[0].get_non_reference_type(), ts[0]); + assert_eq!(ts[1].get_non_reference_type(), ts[1]); + assert_eq!(ts[2].get_non_reference_type(), ts[0]); + assert_eq!(ts[3].get_non_reference_type(), ts[1]); + assert_eq!(ts[4].get_non_reference_type(), ts[0]); + assert_eq!(ts[5].get_non_reference_type(), ts[0]); + } + + #[cfg(not(feature="clang_16_0"))] + fn test_get_non_reference_type(_: &[Type]) {} + + test_get_non_reference_type(&ts[..]) + }); + // Usr _______________________________________ let class = Usr::from_objc_class("A");