blob: 1322ff68efcb20dc3a1d72cdf5f282bf466f8c72 [file] [log] [blame]
Lukasz Anforowiczccf55cb2022-10-05 06:00:57 -07001// Part of the Crubit project, under the Apache License v2.0 with LLVM
2// Exceptions. See /LICENSE for license information.
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4
Lukasz Anforowicz55616892022-10-06 09:16:57 -07005use anyhow::{anyhow, ensure, Result};
Lukasz Anforowicz20a3c692022-10-06 08:48:15 -07006use once_cell::sync::Lazy;
Lukasz Anforowicze1aff8c2022-11-15 08:42:31 -08007use proc_macro2::{Ident, TokenStream};
8use quote::{format_ident, quote, ToTokens};
Lukasz Anforowicz434c4692022-11-01 14:05:24 -07009use std::collections::{BTreeSet, HashSet};
10use std::rc::Rc;
Lukasz Anforowiczccf55cb2022-10-05 06:00:57 -070011
12// TODO(lukasza): Consider adding more items into `code_gen_utils` (this crate).
13// For example, the following items from `src_code_gen.rs` will be most likely
14// reused from `cc_bindings_from_rs`:
15// - `make_rs_ident`
16// - `NamespaceQualifier`
17
Lukasz Anforowicze1aff8c2022-11-15 08:42:31 -080018/// Formats a C++ identifier. Returns an error when `ident` is a C++ reserved
19/// keyword or is an invalid identifier.
Lukasz Anforowicz55616892022-10-06 09:16:57 -070020pub fn format_cc_ident(ident: &str) -> Result<TokenStream> {
Lukasz Anforowiczc51aeb12022-11-07 10:56:18 -080021 ensure!(!ident.is_empty(), "Empty string is not a valid C++ identifier");
22
Lukasz Anforowicz20a3c692022-10-06 08:48:15 -070023 // C++ doesn't have an equivalent of
24 // https://doc.rust-lang.org/rust-by-example/compatibility/raw_identifiers.html and therefore
Lukasz Anforowicz55616892022-10-06 09:16:57 -070025 // an error is returned when `ident` is a C++ reserved keyword.
26 ensure!(
Lukasz Anforowicz20a3c692022-10-06 08:48:15 -070027 !RESERVED_CC_KEYWORDS.contains(ident),
Lukasz Anforowicze4333062022-10-17 14:47:53 -070028 "`{}` is a C++ reserved keyword and can't be used as a C++ identifier",
Lukasz Anforowicz20a3c692022-10-06 08:48:15 -070029 ident
30 );
31
Lukasz Anforowicz55616892022-10-06 09:16:57 -070032 ident.parse().map_err(
33 // Explicitly mapping the error via `anyhow!`, because `LexError` is not `Sync`
34 // (required for `anyhow::Error` to implement `From<LexError>`) and
35 // therefore we can't just use `?`.
36 |lex_error| anyhow!("Can't format `{ident}` as a C++ identifier: {lex_error}"),
37 )
Lukasz Anforowiczccf55cb2022-10-05 06:00:57 -070038}
39
Lukasz Anforowicze1aff8c2022-11-15 08:42:31 -080040/// Makes an 'Ident' to be used in the Rust source code. Escapes Rust keywords.
41/// Panics if `ident` is empty or is otherwise an invalid identifier.
42pub fn make_rs_ident(ident: &str) -> Ident {
43 // TODO(https://github.com/dtolnay/syn/pull/1098): Remove the hardcoded list once syn recognizes
44 // 2018 and 2021 keywords.
45 if ["async", "await", "try", "dyn"].contains(&ident) {
46 return format_ident!("r#{}", ident);
47 }
48 match syn::parse_str::<syn::Ident>(ident) {
49 Ok(_) => format_ident!("{}", ident),
50 Err(_) => format_ident!("r#{}", ident),
51 }
52}
53
Lukasz Anforowicz35ba2fe2022-11-23 15:56:19 -080054/// Representation of `foo::bar::baz::` where each component is either the name
55/// of a C++ namespace, or the name of a Rust module.
56#[derive(Debug, PartialEq, Eq, Clone, Hash, PartialOrd, Ord)]
57// TODO(b/258265044): Make the `Vec<String>` payload private + guarantee
58// additional invariants in an explicit, public `new` method. This will help to
59// catch some error conditions early (e.g. an empty path component may trigger a
60// panic in `make_rs_ident`; a reserved C++ keyword might trigger a late error
61// in `format_for_cc` / `format_cc_ident`).
Lukasz Anforowicz8c1a6c42022-11-23 16:18:09 -080062pub struct NamespaceQualifier(pub Vec<Rc<str>>);
Lukasz Anforowicz35ba2fe2022-11-23 15:56:19 -080063
64impl NamespaceQualifier {
65 pub fn format_for_rs(&self) -> TokenStream {
66 let namespace_rs_idents = self.0.iter().map(|ns| make_rs_ident(ns));
67 quote! { #(#namespace_rs_idents::)* }
68 }
69
70 pub fn format_for_cc(&self) -> Result<TokenStream> {
Lukasz Anforowicza577d822022-12-12 15:00:46 -080071 let namespace_cc_idents = self.cc_idents()?;
Lukasz Anforowicz35ba2fe2022-11-23 15:56:19 -080072 Ok(quote! { #(#namespace_cc_idents::)* })
73 }
Lukasz Anforowicza577d822022-12-12 15:00:46 -080074
Lukasz Anforowicz54efc162022-12-16 15:58:44 -080075 fn format_with_cc_body(&self, body: TokenStream) -> Result<TokenStream> {
Lukasz Anforowicza577d822022-12-12 15:00:46 -080076 if self.0.is_empty() {
77 Ok(body)
78 } else {
79 let namespace_cc_idents = self.cc_idents()?;
80 Ok(quote! {
81 namespace #(#namespace_cc_idents)::* {
82 #body
83 }
84 })
85 }
86 }
87
88 fn cc_idents(&self) -> Result<Vec<TokenStream>> {
89 self.0.iter().map(|ns| format_cc_ident(ns)).collect()
90 }
Lukasz Anforowicz35ba2fe2022-11-23 15:56:19 -080091}
92
Lukasz Anforowicz54efc162022-12-16 15:58:44 -080093/// `format_namespace_bound_cc_tokens` formats a sequence of namespace-bound
94/// snippets. For example, `[(ns, tokens)]` will be formatted as:
95///
96/// ```
97/// namespace ns {
98/// #tokens
99/// }
100/// ```
101///
102/// `format_namespace_bound_cc_tokens` tries to give a nice-looking output - for
103/// example it combines consecutive items that belong to the same namespace,
104/// when given `[(ns, tokens1), (ns, tokens2)]` as input:
105///
106/// ```
107/// namespace ns {
108/// #tokens1
109/// #tokens2
110/// }
111/// ```
112///
113/// `format_namespace_bound_cc_tokens` also knows that top-level items (e.g.
114/// ones where `NamespaceQualifier` doesn't contain any namespace names) should
115/// be emitted at the top-level (not nesting them under a `namespace` keyword).
116/// For example, `[(toplevel_ns, tokens)]` will be formatted as just:
117///
118/// ```
119/// #tokens
120/// ```
121pub fn format_namespace_bound_cc_tokens(
122 iter: impl IntoIterator<Item = (NamespaceQualifier, TokenStream)>,
123) -> Result<TokenStream> {
124 let mut iter = iter.into_iter().peekable();
125 let mut tokens_in_curr_ns = Vec::with_capacity(iter.size_hint().0);
126 let mut result = TokenStream::default();
127 while let Some((curr_ns, tokens)) = iter.next() {
128 tokens_in_curr_ns.push(tokens);
129
130 // Flush `tokens_in_curr_ns` when at the end of the current namespace.
131 let next_ns = iter.peek().map(|(next_ns, _)| next_ns);
132 if next_ns != Some(&curr_ns) {
133 let tokens_in_curr_ns = tokens_in_curr_ns.drain(..);
134 result.extend(curr_ns.format_with_cc_body(quote! { #(#tokens_in_curr_ns)* })?);
135 }
136
137 // Separate namespaces with a single empty line.
138 if iter.peek().is_some() {
139 result.extend(quote! { __NEWLINE__ __NEWLINE__ });
140 }
141 }
142 Ok(result)
143}
144
Lukasz Anforowicz434c4692022-11-01 14:05:24 -0700145/// `CcInclude` represents a single `#include ...` directive in C++.
146#[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
147pub enum CcInclude {
148 SystemHeader(&'static str),
149 UserHeader(Rc<str>),
150}
151
152impl CcInclude {
153 /// Creates a `CcInclude` that represents `#include <cstddef>` and provides
154 /// C++ types like `std::size_t` or `std::ptrdiff_t`. See also
155 /// https://en.cppreference.com/w/cpp/header/cstddef
156 pub fn cstddef() -> Self {
157 Self::SystemHeader("cstddef")
158 }
159
Lukasz Anforowiczed17d052022-11-02 12:07:28 -0700160 /// Creates a `CcInclude` that represents `#include <cstdint>` and provides
161 /// C++ types like `std::int16_t` or `std::uint32_t`. See also
162 /// https://en.cppreference.com/w/cpp/header/cstdint
163 pub fn cstdint() -> Self {
164 Self::SystemHeader("cstdint")
165 }
166
Lukasz Anforowicz434c4692022-11-01 14:05:24 -0700167 /// Creates a `CcInclude` that represents `#include <memory>`.
168 /// See also https://en.cppreference.com/w/cpp/header/memory
169 pub fn memory() -> Self {
170 Self::SystemHeader("memory")
171 }
172
Lukasz Anforowiczd16b6bf2022-11-22 18:35:08 -0800173 /// Creates a `CcInclude` that represents `#include <utility>` and provides
174 /// C++ functions like `std::move` and C++ types like `std::tuple`.
175 /// See also https://en.cppreference.com/w/cpp/header/utility
176 pub fn utility() -> Self {
177 Self::SystemHeader("utility")
178 }
179
Lukasz Anforowicz434c4692022-11-01 14:05:24 -0700180 /// Creates a user include: `#include "some/path/to/header.h"`.
181 pub fn user_header(path: Rc<str>) -> Self {
182 Self::UserHeader(path)
183 }
184}
185
186impl ToTokens for CcInclude {
187 fn to_tokens(&self, tokens: &mut TokenStream) {
188 match self {
189 Self::SystemHeader(path) => {
190 let path: TokenStream = path
191 .parse()
192 .expect("`pub` API of `CcInclude` guarantees validity of system includes");
193 quote! { __HASH_TOKEN__ include < #path > __NEWLINE__ }.to_tokens(tokens)
194 }
195 Self::UserHeader(path) => {
196 quote! { __HASH_TOKEN__ include #path __NEWLINE__ }.to_tokens(tokens)
197 }
198 }
199 }
200}
201
202/// Formats a set of `CcInclude`s, trying to follow the guidance from
203/// [the Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html#Names_and_Order_of_Includes).
204pub fn format_cc_includes(set_of_includes: &BTreeSet<CcInclude>) -> TokenStream {
205 let mut tokens = TokenStream::default();
206 let mut iter = set_of_includes.iter().peekable();
207 while let Some(include) = iter.next() {
208 include.to_tokens(&mut tokens);
209
210 // Add an empty line between system headers and user headers.
211 if let (CcInclude::SystemHeader(_), Some(CcInclude::UserHeader(_))) = (include, iter.peek())
212 {
213 quote! { __NEWLINE__ }.to_tokens(&mut tokens)
214 }
215 }
216 tokens
217}
218
Lukasz Anforowicz20a3c692022-10-06 08:48:15 -0700219static RESERVED_CC_KEYWORDS: Lazy<HashSet<&'static str>> = Lazy::new(|| {
220 // `RESERVED_CC_KEYWORDS` are based on https://en.cppreference.com/w/cpp/keyword
221 [
222 "alignas",
223 "alignof",
224 "and",
225 "and_eq",
226 "asm",
227 "atomic_cancel",
228 "atomic_commit",
229 "atomic_noexcept",
230 "auto",
231 "bitand",
232 "bitor",
233 "bool",
234 "break",
235 "case",
236 "catch",
237 "char",
238 "char8_t",
239 "char16_t",
240 "char32_t",
241 "class",
242 "compl",
243 "concept",
244 "const",
245 "consteval",
246 "constexpr",
247 "constinit",
248 "const_cast",
249 "continue",
250 "co_await",
251 "co_return",
252 "co_yield",
253 "decltype",
254 "default",
255 "delete",
256 "do",
257 "double",
258 "dynamic_cast",
259 "else",
260 "enum",
261 "explicit",
262 "export",
263 "extern",
264 "false",
265 "float",
266 "for",
267 "friend",
268 "goto",
269 "if",
270 "inline",
271 "int",
272 "long",
273 "mutable",
274 "namespace",
275 "new",
276 "noexcept",
277 "not",
278 "not_eq",
279 "nullptr",
280 "operator",
281 "or",
282 "or_eq",
283 "private",
284 "protected",
285 "public",
286 "reflexpr",
287 "register",
288 "reinterpret_cast",
289 "requires",
290 "return",
291 "short",
292 "signed",
293 "sizeof",
294 "static",
295 "static_assert",
296 "static_cast",
297 "struct",
298 "switch",
299 "synchronized",
300 "template",
301 "this",
302 "thread_local",
303 "throw",
304 "true",
305 "try",
306 "typedef",
307 "typeid",
308 "typename",
309 "union",
310 "unsigned",
311 "using",
312 "virtual",
313 "void",
314 "volatile",
315 "wchar_t",
316 "while",
317 "xor",
318 "xor_eq",
319 ]
320 .into_iter()
321 .collect()
322});
323
Lukasz Anforowiczccf55cb2022-10-05 06:00:57 -0700324#[cfg(test)]
325pub mod tests {
326 use super::*;
327 use quote::quote;
Lukasz Anforowicze1aff8c2022-11-15 08:42:31 -0800328 use token_stream_matchers::{assert_cc_matches, assert_rs_matches};
Lukasz Anforowicz5bf49432022-12-12 12:17:24 -0800329 use token_stream_printer::cc_tokens_to_formatted_string_for_tests;
Lukasz Anforowiczccf55cb2022-10-05 06:00:57 -0700330
331 #[test]
332 fn test_format_cc_ident_basic() {
Lukasz Anforowicz55616892022-10-06 09:16:57 -0700333 assert_cc_matches!(
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -0800334 format_cc_ident("foo").unwrap(),
Lukasz Anforowicz55616892022-10-06 09:16:57 -0700335 quote! { foo }
336 );
Lukasz Anforowiczccf55cb2022-10-05 06:00:57 -0700337 }
338
339 #[test]
340 fn test_format_cc_ident_reserved_rust_keyword() {
Lukasz Anforowicz55616892022-10-06 09:16:57 -0700341 assert_cc_matches!(
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -0800342 format_cc_ident("impl").unwrap(),
Lukasz Anforowicz55616892022-10-06 09:16:57 -0700343 quote! { impl }
344 );
Lukasz Anforowiczccf55cb2022-10-05 06:00:57 -0700345 }
346
347 #[test]
348 fn test_format_cc_ident_reserved_cc_keyword() {
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -0800349 let err = format_cc_ident("reinterpret_cast").unwrap_err();
Lukasz Anforowicz55616892022-10-06 09:16:57 -0700350 let msg = err.to_string();
351 assert!(msg.contains("`reinterpret_cast`"));
352 assert!(msg.contains("C++ reserved keyword"));
353 }
354
355 #[test]
356 fn test_format_cc_ident_unfinished_group() {
357 let err = format_cc_ident("(foo") // No closing `)`.
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -0800358 .unwrap_err();
Lukasz Anforowicz55616892022-10-06 09:16:57 -0700359 let msg = err.to_string();
360 assert!(msg.contains("Can't format `(foo` as a C++ identifier"));
361 assert!(msg.contains("cannot parse"));
362 }
363
364 #[test]
365 fn test_format_cc_ident_unqualified_identifiers() {
366 // https://en.cppreference.com/w/cpp/language/identifiers#Unqualified_identifiers
367
368 // These may appear in `IR::Func::name`.
369 assert_cc_matches!(
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -0800370 format_cc_ident("operator==").unwrap(),
Lukasz Anforowicz55616892022-10-06 09:16:57 -0700371 quote! { operator== }
372 );
373 assert_cc_matches!(
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -0800374 format_cc_ident("operator new").unwrap(),
Lukasz Anforowicz55616892022-10-06 09:16:57 -0700375 quote! { operator new }
376 );
377
378 // This may appear in `IR::Record::cc_name` (although in practice these will
379 // be namespace-qualified most of the time).
380 assert_cc_matches!(
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -0800381 format_cc_ident("MyTemplate<int>").unwrap(),
Lukasz Anforowicz55616892022-10-06 09:16:57 -0700382 quote! { MyTemplate<int> }
383 );
384
385 // These forms of unqualified identifiers are not used by Crubit in practice,
386 assert_cc_matches!(
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -0800387 format_cc_ident("~MyClass").unwrap(),
Lukasz Anforowicz55616892022-10-06 09:16:57 -0700388 quote! { ~MyClass }
389 );
390 assert_cc_matches!(
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -0800391 format_cc_ident(r#" operator "" _km "#).unwrap(),
Lukasz Anforowicz55616892022-10-06 09:16:57 -0700392 quote! { operator "" _km }
393 );
394 }
395
396 #[test]
Lukasz Anforowiczc51aeb12022-11-07 10:56:18 -0800397 fn test_format_cc_ident_empty() {
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -0800398 let err = format_cc_ident("").unwrap_err();
Lukasz Anforowiczc51aeb12022-11-07 10:56:18 -0800399 let msg = err.to_string();
400 assert_eq!(msg, "Empty string is not a valid C++ identifier");
401 }
402
403 #[test]
Lukasz Anforowicz55616892022-10-06 09:16:57 -0700404 fn test_format_cc_ident_qualified_identifiers() {
405 // https://en.cppreference.com/w/cpp/language/identifiers#Qualified_identifiers
406
407 // This may appear in `IR::Record::cc_name`.
408 assert_cc_matches!(
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -0800409 format_cc_ident("std::vector<int>").unwrap(),
Lukasz Anforowicz55616892022-10-06 09:16:57 -0700410 quote! { std::vector<int> }
411 );
Lukasz Anforowiczccf55cb2022-10-05 06:00:57 -0700412 }
Lukasz Anforowicz434c4692022-11-01 14:05:24 -0700413
414 #[test]
Lukasz Anforowicze1aff8c2022-11-15 08:42:31 -0800415 fn test_make_rs_ident_basic() {
416 let id = make_rs_ident("foo");
417 assert_rs_matches!(quote! { #id }, quote! { foo });
418 }
419
420 #[test]
421 fn test_make_rs_ident_reserved_cc_keyword() {
422 let id = make_rs_ident("reinterpret_cast");
423 assert_rs_matches!(quote! { #id }, quote! { reinterpret_cast });
424 }
425
426 #[test]
427 fn test_make_rs_ident_reserved_rust_keyword() {
428 let id = make_rs_ident("impl");
429 assert_rs_matches!(quote! { #id }, quote! { r#impl });
430 }
431
432 #[test]
433 #[should_panic]
434 fn test_make_rs_ident_unfinished_group() {
435 make_rs_ident("(foo"); // No closing `)`.
436 }
437
438 #[test]
439 #[should_panic]
440 fn test_make_rs_ident_empty() {
441 make_rs_ident("");
442 }
443
444 #[test]
Lukasz Anforowicz434c4692022-11-01 14:05:24 -0700445 fn test_cc_include_to_tokens_for_system_header() {
446 let include = CcInclude::cstddef();
447 assert_cc_matches!(
448 quote! { #include },
449 quote! {
450 __HASH_TOKEN__ include <cstddef>
451 }
452 );
453 }
454
455 #[test]
456 fn test_cc_include_to_tokens_for_user_header() {
457 let include = CcInclude::user_header("some/path/to/header.h".into());
458 assert_cc_matches!(
459 quote! { #include },
460 quote! {
461 __HASH_TOKEN__ include "some/path/to/header.h"
462 }
463 );
464 }
465
466 #[test]
467 fn test_cc_include_ord() {
468 let cstddef = CcInclude::cstddef();
469 let memory = CcInclude::memory();
470 let a = CcInclude::user_header("a.h".into());
471 let b = CcInclude::user_header("b.h".into());
472 assert!(cstddef < memory);
473 assert!(cstddef < a);
474 assert!(cstddef < b);
475 assert!(memory < a);
476 assert!(memory < b);
477 assert!(a < b);
478 }
479
480 #[test]
481 fn test_format_cc_includes() {
482 let includes = [
483 CcInclude::cstddef(),
484 CcInclude::memory(),
485 CcInclude::user_header("a.h".into()),
486 CcInclude::user_header("b.h".into()),
487 ]
488 .into_iter()
489 .collect::<BTreeSet<_>>();
490
491 let tokens = format_cc_includes(&includes);
Lukasz Anforowicz5bf49432022-12-12 12:17:24 -0800492 let actual =
493 cc_tokens_to_formatted_string_for_tests(quote! { __NEWLINE__ #tokens }).unwrap();
Lukasz Anforowicz434c4692022-11-01 14:05:24 -0700494 assert_eq!(
495 actual,
496 r#"
497#include <cstddef>
498#include <memory>
499
500#include "a.h"
501#include "b.h"
502"#
503 );
504 }
Lukasz Anforowicz35ba2fe2022-11-23 15:56:19 -0800505
506 fn create_namespace_qualifier_for_tests(input: &[&str]) -> NamespaceQualifier {
Lukasz Anforowicz8c1a6c42022-11-23 16:18:09 -0800507 NamespaceQualifier(input.into_iter().map(|&s| s.into()).collect())
Lukasz Anforowicz35ba2fe2022-11-23 15:56:19 -0800508 }
509
510 #[test]
511 fn test_namespace_qualifier_empty() {
512 let ns = create_namespace_qualifier_for_tests(&[]);
513 let actual_rs = ns.format_for_rs();
514 assert!(actual_rs.is_empty());
515 let actual_cc = ns.format_for_cc().unwrap();
516 assert!(actual_cc.is_empty());
517 }
518
519 #[test]
520 fn test_namespace_qualifier_basic() {
521 let ns = create_namespace_qualifier_for_tests(&["foo", "bar"]);
522 let actual_rs = ns.format_for_rs();
523 assert_rs_matches!(actual_rs, quote! { foo::bar:: });
524 let actual_cc = ns.format_for_cc().unwrap();
525 assert_cc_matches!(actual_cc, quote! { foo::bar:: });
526 }
527
528 #[test]
529 fn test_namespace_qualifier_reserved_cc_keyword() {
530 let ns = create_namespace_qualifier_for_tests(&["foo", "impl", "bar"]);
531 let actual_rs = ns.format_for_rs();
532 assert_rs_matches!(actual_rs, quote! { foo :: r#impl :: bar :: });
533 let actual_cc = ns.format_for_cc().unwrap();
534 assert_cc_matches!(actual_cc, quote! { foo::impl::bar:: });
535 }
536
537 #[test]
538 fn test_namespace_qualifier_reserved_rust_keyword() {
539 let ns = create_namespace_qualifier_for_tests(&["foo", "reinterpret_cast", "bar"]);
540 let actual_rs = ns.format_for_rs();
541 assert_rs_matches!(actual_rs, quote! { foo :: reinterpret_cast :: bar :: });
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -0800542 let cc_error = ns.format_for_cc().unwrap_err();
Lukasz Anforowicz35ba2fe2022-11-23 15:56:19 -0800543 let msg = cc_error.to_string();
544 assert!(msg.contains("`reinterpret_cast`"));
545 assert!(msg.contains("C++ reserved keyword"));
546 }
Lukasz Anforowicza577d822022-12-12 15:00:46 -0800547
548 #[test]
549 fn test_namespace_qualifier_format_with_cc_body_top_level_namespace() {
550 let ns = create_namespace_qualifier_for_tests(&[]);
551 assert_cc_matches!(
552 ns.format_with_cc_body(quote! { cc body goes here }).unwrap(),
553 quote! { cc body goes here },
554 );
555 }
556
557 #[test]
558 fn test_namespace_qualifier_format_with_cc_body_nested_namespace() {
559 let ns = create_namespace_qualifier_for_tests(&["foo", "bar", "baz"]);
560 assert_cc_matches!(
561 ns.format_with_cc_body(quote! { cc body goes here }).unwrap(),
562 quote! {
563 namespace foo::bar::baz {
564 cc body goes here
565 } // namespace foo::bar::baz
566 },
567 );
568 }
Lukasz Anforowicz54efc162022-12-16 15:58:44 -0800569
570 #[test]
571 fn test_format_namespace_bound_cc_tokens() {
572 let top_level = create_namespace_qualifier_for_tests(&[]);
573 let m1 = create_namespace_qualifier_for_tests(&["m1"]);
574 let m2 = create_namespace_qualifier_for_tests(&["m2"]);
575 let input = vec![
576 (top_level.clone(), quote! { void f0a(); }),
577 (m1.clone(), quote! { void f1a(); }),
578 (m1.clone(), quote! { void f1b(); }),
579 (top_level.clone(), quote! { void f0b(); }),
580 (top_level.clone(), quote! { void f0c(); }),
581 (m2.clone(), quote! { void f2a(); }),
582 (m1.clone(), quote! { void f1c(); }),
583 (m1.clone(), quote! { void f1d(); }),
584 ];
585 assert_cc_matches!(
586 format_namespace_bound_cc_tokens(input).unwrap(),
587 quote! {
588 void f0a();
589
590 namespace m1 {
591 void f1a();
592 void f1b();
593 } // namespace m1
594
595 void f0b();
596 void f0c();
597
598 namespace m2 {
599 void f2a();
600 }
601
602 namespace m1 {
603 void f1c();
604 void f1d();
605 } // namespace m1
606 },
607 );
608 }
Lukasz Anforowiczccf55cb2022-10-05 06:00:57 -0700609}