use ast::HasName;
use cfg::CfgExpr;
-use either::Either;
-use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, InFile, Semantics};
+use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics};
use ide_assists::utils::test_related_attribute;
use ide_db::{
base_db::{FilePosition, FileRange},
+ defs::Definition,
helpers::visit_file_defs,
search::SearchScope,
- RootDatabase, SymbolKind,
+ FxHashMap, FxHashSet, RootDatabase, SymbolKind,
};
use itertools::Itertools;
-use rustc_hash::{FxHashMap, FxHashSet};
use stdx::{always, format_to};
-use syntax::ast::{self, AstNode, HasAttrs as _};
-
-use crate::{
- display::{ToNav, TryToNav},
- references, FileId, NavigationTarget,
+use syntax::{
+ ast::{self, AstNode, HasAttrs as _},
+ SmolStr, SyntaxNode,
};
+use crate::{references, FileId, NavigationTarget, ToNav, TryToNav};
+
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Runnable {
pub use_name_in_title: bool,
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub enum TestId {
- Name(String),
+ Name(SmolStr),
Path(String),
}
impl fmt::Display for TestId {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
- TestId::Name(name) => write!(f, "{}", name),
- TestId::Path(path) => write!(f, "{}", path),
+ TestId::Name(name) => name.fmt(f),
+ TestId::Path(path) => path.fmt(f),
}
}
}
}) {
if let Some(def) = def {
let file_id = match def {
- hir::ModuleDef::Module(it) => it.declaration_source(db).map(|src| src.file_id),
- hir::ModuleDef::Function(it) => it.source(db).map(|src| src.file_id),
+ Definition::Module(it) => it.declaration_source(db).map(|src| src.file_id),
+ Definition::Function(it) => it.source(db).map(|src| src.file_id),
_ => None,
};
if let Some(file_id) = file_id.filter(|file| file.call_node(db).is_some()) {
res.push(runnable);
}
};
- visit_file_defs(&sema, file_id, &mut |def| match def {
- Either::Left(def) => {
- let runnable = match def {
- hir::ModuleDef::Module(it) => runnable_mod(&sema, it),
- hir::ModuleDef::Function(it) => runnable_fn(&sema, it),
- _ => None,
- };
- add_opt(runnable.or_else(|| module_def_doctest(sema.db, def)), Some(def));
- }
- Either::Right(impl_) => {
- add_opt(runnable_impl(&sema, &impl_), None);
- impl_
- .items(db)
- .into_iter()
- .map(|assoc| {
- (
- match assoc {
- hir::AssocItem::Function(it) => runnable_fn(&sema, it)
- .or_else(|| module_def_doctest(sema.db, it.into())),
- hir::AssocItem::Const(it) => module_def_doctest(sema.db, it.into()),
- hir::AssocItem::TypeAlias(it) => module_def_doctest(sema.db, it.into()),
- },
- assoc,
- )
- })
- .for_each(|(r, assoc)| add_opt(r, Some(assoc.into())));
+ visit_file_defs(&sema, file_id, &mut |def| {
+ let runnable = match def {
+ Definition::Module(it) => runnable_mod(&sema, it),
+ Definition::Function(it) => runnable_fn(&sema, it),
+ Definition::SelfType(impl_) => runnable_impl(&sema, &impl_),
+ _ => None,
+ };
+ add_opt(
+ runnable
+ .or_else(|| module_def_doctest(sema.db, def))
+ // #[macro_export] mbe macros are declared in the root, while their definition may reside in a different module
+ .filter(|it| it.nav.file_id == file_id),
+ Some(def),
+ );
+ if let Definition::SelfType(impl_) = def {
+ impl_.items(db).into_iter().for_each(|assoc| {
+ let runnable = match assoc {
+ hir::AssocItem::Function(it) => {
+ runnable_fn(&sema, it).or_else(|| module_def_doctest(sema.db, it.into()))
+ }
+ hir::AssocItem::Const(it) => module_def_doctest(sema.db, it.into()),
+ hir::AssocItem::TypeAlias(it) => module_def_doctest(sema.db, it.into()),
+ };
+ add_opt(runnable, Some(assoc.into()))
+ });
}
});
) -> Vec<Runnable> {
let sema = Semantics::new(db);
let mut res: FxHashSet<Runnable> = FxHashSet::default();
+ let syntax = sema.parse(position.file_id).syntax().clone();
- find_related_tests(&sema, position, search_scope, &mut res);
+ find_related_tests(&sema, &syntax, position, search_scope, &mut res);
- res.into_iter().collect_vec()
+ res.into_iter().collect()
}
fn find_related_tests(
- sema: &Semantics<RootDatabase>,
+ sema: &Semantics<'_, RootDatabase>,
+ syntax: &SyntaxNode,
position: FilePosition,
search_scope: Option<SearchScope>,
tests: &mut FxHashSet<Runnable>,
) {
- if let Some(refs) = references::find_all_refs(sema, position, search_scope) {
- for (file_id, refs) in refs.into_iter().flat_map(|refs| refs.references) {
- let file = sema.parse(file_id);
- let file = file.syntax();
-
- // create flattened vec of tokens
- let tokens = refs.iter().flat_map(|(range, _)| {
- match file.token_at_offset(range.start()).next() {
- Some(token) => sema.descend_into_macros(token),
- None => Default::default(),
- }
- });
-
- // find first suitable ancestor
- let functions = tokens
- .filter_map(|token| token.ancestors().find_map(ast::Fn::cast))
- .map(|f| hir::InFile::new(sema.hir_file_for(f.syntax()), f));
-
- for fn_def in functions {
- // #[test/bench] expands to just the item causing us to lose the attribute, so recover them by going out of the attribute
- let InFile { value: fn_def, .. } = &fn_def.node_with_attributes(sema.db);
- if let Some(runnable) = as_test_runnable(sema, fn_def) {
+ // FIXME: why is this using references::find_defs, this should use ide_db::search
+ let defs = match references::find_defs(sema, syntax, position.offset) {
+ Some(defs) => defs,
+ None => return,
+ };
+ for def in defs {
+ let defs = def
+ .usages(sema)
+ .set_scope(search_scope.clone())
+ .all()
+ .references
+ .into_values()
+ .flatten();
+ for ref_ in defs {
+ let name_ref = match ref_.name {
+ ast::NameLike::NameRef(name_ref) => name_ref,
+ _ => continue,
+ };
+ if let Some(fn_def) =
+ sema.ancestors_with_macros(name_ref.syntax().clone()).find_map(ast::Fn::cast)
+ {
+ if let Some(runnable) = as_test_runnable(sema, &fn_def) {
// direct test
tests.insert(runnable);
- } else if let Some(module) = parent_test_module(sema, fn_def) {
+ } else if let Some(module) = parent_test_module(sema, &fn_def) {
// indirect test
- find_related_tests_in_module(sema, fn_def, &module, tests);
+ find_related_tests_in_module(sema, syntax, &fn_def, &module, tests);
}
}
}
}
}
+
fn find_related_tests_in_module(
- sema: &Semantics<RootDatabase>,
+ sema: &Semantics<'_, RootDatabase>,
+ syntax: &SyntaxNode,
fn_def: &ast::Fn,
parent_module: &hir::Module,
tests: &mut FxHashSet<Runnable>,
) {
- if let Some(fn_name) = fn_def.name() {
- let mod_source = parent_module.definition_source(sema.db);
- let range = match mod_source.value {
- hir::ModuleSource::Module(m) => m.syntax().text_range(),
- hir::ModuleSource::BlockExpr(b) => b.syntax().text_range(),
- hir::ModuleSource::SourceFile(f) => f.syntax().text_range(),
- };
+ let fn_name = match fn_def.name() {
+ Some(it) => it,
+ _ => return,
+ };
+ let mod_source = parent_module.definition_source(sema.db);
+ let range = match &mod_source.value {
+ hir::ModuleSource::Module(m) => m.syntax().text_range(),
+ hir::ModuleSource::BlockExpr(b) => b.syntax().text_range(),
+ hir::ModuleSource::SourceFile(f) => f.syntax().text_range(),
+ };
- let file_id = mod_source.file_id.original_file(sema.db);
- let mod_scope = SearchScope::file_range(FileRange { file_id, range });
- let fn_pos = FilePosition { file_id, offset: fn_name.syntax().text_range().start() };
- find_related_tests(sema, fn_pos, Some(mod_scope), tests)
- }
+ let file_id = mod_source.file_id.original_file(sema.db);
+ let mod_scope = SearchScope::file_range(FileRange { file_id, range });
+ let fn_pos = FilePosition { file_id, offset: fn_name.syntax().text_range().start() };
+ find_related_tests(sema, syntax, fn_pos, Some(mod_scope), tests)
}
-fn as_test_runnable(sema: &Semantics<RootDatabase>, fn_def: &ast::Fn) -> Option<Runnable> {
+fn as_test_runnable(sema: &Semantics<'_, RootDatabase>, fn_def: &ast::Fn) -> Option<Runnable> {
if test_related_attribute(fn_def).is_some() {
let function = sema.to_def(fn_def)?;
runnable_fn(sema, function)
}
}
-fn parent_test_module(sema: &Semantics<RootDatabase>, fn_def: &ast::Fn) -> Option<hir::Module> {
+fn parent_test_module(sema: &Semantics<'_, RootDatabase>, fn_def: &ast::Fn) -> Option<hir::Module> {
fn_def.syntax().ancestors().find_map(|node| {
let module = ast::Module::cast(node)?;
let module = sema.to_def(&module)?;
})
}
-pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> {
- // #[test/bench] expands to just the item causing us to lose the attribute, so recover them by going out of the attribute
- let func = def.source(sema.db)?.node_with_attributes(sema.db);
- let name_string = def.name(sema.db).to_string();
+pub(crate) fn runnable_fn(
+ sema: &Semantics<'_, RootDatabase>,
+ def: hir::Function,
+) -> Option<Runnable> {
+ let func = def.source(sema.db)?;
+ let name = def.name(sema.db).to_smol_str();
let root = def.module(sema.db).krate().root_module(sema.db);
- let kind = if name_string == "main" && def.module(sema.db) == root {
+ let kind = if name == "main" && def.module(sema.db) == root {
RunnableKind::Bin
} else {
- let canonical_path = {
- let def: hir::ModuleDef = def.into();
- def.canonical_path(sema.db)
+ let test_id = || {
+ let canonical_path = {
+ let def: hir::ModuleDef = def.into();
+ def.canonical_path(sema.db)
+ };
+ canonical_path.map(TestId::Path).unwrap_or(TestId::Name(name))
};
- let test_id = canonical_path.map(TestId::Path).unwrap_or(TestId::Name(name_string));
if test_related_attribute(&func.value).is_some() {
let attr = TestAttr::from_fn(&func.value);
- RunnableKind::Test { test_id, attr }
+ RunnableKind::Test { test_id: test_id(), attr }
} else if func.value.has_atom_attr("bench") {
- RunnableKind::Bench { test_id }
+ RunnableKind::Bench { test_id: test_id() }
} else {
return None;
}
Some(Runnable { use_name_in_title: false, nav, kind, cfg })
}
-pub(crate) fn runnable_mod(sema: &Semantics<RootDatabase>, def: hir::Module) -> Option<Runnable> {
+pub(crate) fn runnable_mod(
+ sema: &Semantics<'_, RootDatabase>,
+ def: hir::Module,
+) -> Option<Runnable> {
if !has_test_function_or_multiple_test_submodules(sema, &def) {
return None;
}
Some(Runnable { use_name_in_title: false, nav, kind: RunnableKind::TestMod { path }, cfg })
}
-pub(crate) fn runnable_impl(sema: &Semantics<RootDatabase>, def: &hir::Impl) -> Option<Runnable> {
+pub(crate) fn runnable_impl(
+ sema: &Semantics<'_, RootDatabase>,
+ def: &hir::Impl,
+) -> Option<Runnable> {
let attrs = def.attrs(sema.db);
if !has_runnable_doc_test(&attrs) {
return None;
/// Creates a test mod runnable for outline modules at the top of their definition.
fn runnable_mod_outline_definition(
- sema: &Semantics<RootDatabase>,
+ sema: &Semantics<'_, RootDatabase>,
def: hir::Module,
) -> Option<Runnable> {
if !has_test_function_or_multiple_test_submodules(sema, &def) {
}
}
-fn module_def_doctest(db: &RootDatabase, def: hir::ModuleDef) -> Option<Runnable> {
+fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option<Runnable> {
let attrs = match def {
- hir::ModuleDef::Module(it) => it.attrs(db),
- hir::ModuleDef::Function(it) => it.attrs(db),
- hir::ModuleDef::Adt(it) => it.attrs(db),
- hir::ModuleDef::Variant(it) => it.attrs(db),
- hir::ModuleDef::Const(it) => it.attrs(db),
- hir::ModuleDef::Static(it) => it.attrs(db),
- hir::ModuleDef::Trait(it) => it.attrs(db),
- hir::ModuleDef::TypeAlias(it) => it.attrs(db),
- hir::ModuleDef::BuiltinType(_) => return None,
+ Definition::Module(it) => it.attrs(db),
+ Definition::Function(it) => it.attrs(db),
+ Definition::Adt(it) => it.attrs(db),
+ Definition::Variant(it) => it.attrs(db),
+ Definition::Const(it) => it.attrs(db),
+ Definition::Static(it) => it.attrs(db),
+ Definition::Trait(it) => it.attrs(db),
+ Definition::TypeAlias(it) => it.attrs(db),
+ Definition::Macro(it) => it.attrs(db),
+ Definition::SelfType(it) => it.attrs(db),
+ _ => return None,
};
if !has_runnable_doc_test(&attrs) {
return None;
Some(path)
})();
- let test_id = path.map_or_else(|| TestId::Name(def_name.to_string()), TestId::Path);
+ let test_id = path.map_or_else(|| TestId::Name(def_name.to_smol_str()), TestId::Path);
let mut nav = match def {
- hir::ModuleDef::Module(def) => NavigationTarget::from_module_to_decl(db, def),
+ Definition::Module(def) => NavigationTarget::from_module_to_decl(db, def),
def => def.try_to_nav(db)?,
};
nav.focus_range = None;
}
}
-const RUSTDOC_FENCE: &str = "```";
+const RUSTDOC_FENCES: [&str; 2] = ["```", "~~~"];
const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] =
&["", "rust", "should_panic", "edition2015", "edition2018", "edition2021"];
let mut in_code_block = false;
for line in String::from(doc).lines() {
- if let Some(header) = line.strip_prefix(RUSTDOC_FENCE) {
+ if let Some(header) =
+ RUSTDOC_FENCES.into_iter().find_map(|fence| line.strip_prefix(fence))
+ {
in_code_block = !in_code_block;
if in_code_block
// We could create runnables for modules with number_of_test_submodules > 0,
// but that bloats the runnables for no real benefit, since all tests can be run by the submodule already
fn has_test_function_or_multiple_test_submodules(
- sema: &Semantics<RootDatabase>,
+ sema: &Semantics<'_, RootDatabase>,
module: &hir::Module,
) -> bool {
let mut number_of_test_submodules = 0;
match item {
hir::ModuleDef::Function(f) => {
if let Some(it) = f.source(sema.db) {
- // #[test/bench] expands to just the item causing us to lose the attribute, so recover them by going out of the attribute
- let it = it.node_with_attributes(sema.db);
if test_related_attribute(&it.value).is_some() {
return true;
}
expect: Expect,
) {
let (analysis, position) = fixture::position(ra_fixture);
- let runnables = analysis.runnables(position.file_id).unwrap();
+ let mut runnables = analysis.runnables(position.file_id).unwrap();
+ runnables.sort_by_key(|it| (it.nav.full_range.start(), it.nav.name.clone()));
expect.assert_debug_eq(&runnables);
assert_eq!(
actions,
fn main() {}
}
"#,
- &[Bin, Test, Test, Bench, TestMod],
+ &[TestMod, Bin, Test, Test, Bench],
expect![[r#"
[
+ Runnable {
+ use_name_in_title: false,
+ nav: NavigationTarget {
+ file_id: FileId(
+ 0,
+ ),
+ full_range: 0..137,
+ name: "",
+ kind: Module,
+ },
+ kind: TestMod {
+ path: "",
+ },
+ cfg: None,
+ },
Runnable {
use_name_in_title: false,
nav: NavigationTarget {
},
cfg: None,
},
- Runnable {
- use_name_in_title: false,
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 0..137,
- name: "",
- kind: Module,
- },
- kind: TestMod {
- path: "",
- },
- cfg: None,
- },
]
"#]],
);
mod nested_tests_4 {}
}
"#,
- &[TestMod, TestMod, TestMod, Test, Test, Test],
+ &[TestMod, TestMod, Test, Test, TestMod, Test],
expect![[r#"
[
Runnable {
},
cfg: None,
},
- Runnable {
- use_name_in_title: false,
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 202..286,
- focus_range: 206..220,
- name: "nested_tests_2",
- kind: Module,
- description: "mod nested_tests_2",
- },
- kind: TestMod {
- path: "root_tests::nested_tests_0::nested_tests_2",
- },
- cfg: None,
- },
Runnable {
use_name_in_title: false,
nav: NavigationTarget {
},
cfg: None,
},
+ Runnable {
+ use_name_in_title: false,
+ nav: NavigationTarget {
+ file_id: FileId(
+ 0,
+ ),
+ full_range: 202..286,
+ focus_range: 206..220,
+ name: "nested_tests_2",
+ kind: Module,
+ description: "mod nested_tests_2",
+ },
+ kind: TestMod {
+ path: "root_tests::nested_tests_0::nested_tests_2",
+ },
+ cfg: None,
+ },
Runnable {
use_name_in_title: false,
nav: NavigationTarget {
#[cfg(feature = "foo")]
fn test_foo1() {}
"#,
- &[Test, TestMod],
+ &[TestMod, Test],
expect![[r#"
[
+ Runnable {
+ use_name_in_title: false,
+ nav: NavigationTarget {
+ file_id: FileId(
+ 0,
+ ),
+ full_range: 0..51,
+ name: "",
+ kind: Module,
+ },
+ kind: TestMod {
+ path: "",
+ },
+ cfg: None,
+ },
Runnable {
use_name_in_title: false,
nav: NavigationTarget {
),
),
},
- Runnable {
- use_name_in_title: false,
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 0..51,
- name: "",
- kind: Module,
- },
- kind: TestMod {
- path: "",
- },
- cfg: None,
- },
]
"#]],
);
#[cfg(all(feature = "foo", feature = "bar"))]
fn test_foo1() {}
"#,
- &[Test, TestMod],
+ &[TestMod, Test],
expect![[r#"
[
+ Runnable {
+ use_name_in_title: false,
+ nav: NavigationTarget {
+ file_id: FileId(
+ 0,
+ ),
+ full_range: 0..73,
+ name: "",
+ kind: Module,
+ },
+ kind: TestMod {
+ path: "",
+ },
+ cfg: None,
+ },
Runnable {
use_name_in_title: false,
nav: NavigationTarget {
),
),
},
- Runnable {
- use_name_in_title: false,
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 0..73,
- name: "",
- kind: Module,
- },
- kind: TestMod {
- path: "",
- },
- cfg: None,
- },
]
"#]],
);
}
gen2!();
"#,
- &[TestMod, TestMod, TestMod, Test, Test],
+ &[TestMod, TestMod, Test, Test, TestMod],
expect![[r#"
[
Runnable {
file_id: FileId(
0,
),
- full_range: 202..227,
- focus_range: 206..211,
- name: "tests",
+ full_range: 0..237,
+ name: "",
kind: Module,
- description: "mod tests",
},
kind: TestMod {
- path: "tests",
+ path: "",
},
cfg: None,
},
file_id: FileId(
0,
),
- full_range: 0..237,
- name: "",
+ full_range: 202..227,
+ focus_range: 206..211,
+ name: "tests",
kind: Module,
+ description: "mod tests",
},
kind: TestMod {
- path: "",
+ path: "tests",
},
cfg: None,
},
Runnable {
- use_name_in_title: true,
+ use_name_in_title: false,
nav: NavigationTarget {
file_id: FileId(
0,
),
- full_range: 228..236,
- focus_range: 228..236,
- name: "tests2",
- kind: Module,
- description: "mod tests2",
+ full_range: 218..225,
+ name: "foo_test",
+ kind: Function,
},
- kind: TestMod {
- path: "tests2",
+ kind: Test {
+ test_id: Path(
+ "tests::foo_test",
+ ),
+ attr: TestAttr {
+ ignore: false,
+ },
},
cfg: None,
},
cfg: None,
},
Runnable {
- use_name_in_title: false,
+ use_name_in_title: true,
nav: NavigationTarget {
file_id: FileId(
0,
),
- full_range: 218..225,
- name: "foo_test",
- kind: Function,
+ full_range: 228..236,
+ name: "tests2",
+ kind: Module,
+ description: "mod tests2",
},
- kind: Test {
- test_id: Path(
- "tests::foo_test",
- ),
- attr: TestAttr {
- ignore: false,
- },
+ kind: TestMod {
+ path: "tests2",
},
cfg: None,
},
}
foo!();
"#,
- &[TestMod, Test, Test, Test],
+ &[Test, Test, Test, TestMod],
expect![[r#"
[
- Runnable {
- use_name_in_title: true,
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 210..217,
- focus_range: 210..217,
- name: "foo_tests",
- kind: Module,
- description: "mod foo_tests",
- },
- kind: TestMod {
- path: "foo_tests",
- },
- cfg: None,
- },
Runnable {
use_name_in_title: true,
nav: NavigationTarget {
},
cfg: None,
},
+ Runnable {
+ use_name_in_title: true,
+ nav: NavigationTarget {
+ file_id: FileId(
+ 0,
+ ),
+ full_range: 210..217,
+ name: "foo_tests",
+ kind: Module,
+ description: "mod foo_tests",
+ },
+ kind: TestMod {
+ path: "foo_tests",
+ },
+ cfg: None,
+ },
]
"#]],
);
#[test]
fn t1() {}
"#,
- &[Test, Test, TestMod],
+ &[TestMod, Test, Test],
expect![[r#"
[
+ Runnable {
+ use_name_in_title: false,
+ nav: NavigationTarget {
+ file_id: FileId(
+ 1,
+ ),
+ full_range: 0..39,
+ name: "m",
+ kind: Module,
+ },
+ kind: TestMod {
+ path: "m",
+ },
+ cfg: None,
+ },
Runnable {
use_name_in_title: false,
nav: NavigationTarget {
},
cfg: None,
},
- Runnable {
- use_name_in_title: false,
- nav: NavigationTarget {
- file_id: FileId(
- 1,
- ),
- full_range: 0..39,
- name: "m",
- kind: Module,
- },
- kind: TestMod {
- path: "m",
- },
- cfg: None,
- },
]
"#]],
);
"#]],
);
}
+
+ #[test]
+ fn doc_test_macro_export_mbe() {
+ check(
+ r#"
+//- /lib.rs
+$0
+mod foo;
+
+//- /foo.rs
+/// ```
+/// fn foo() {
+/// }
+/// ```
+#[macro_export]
+macro_rules! foo {
+ () => {
+
+ };
+}
+"#,
+ &[],
+ expect![[r#"
+ []
+ "#]],
+ );
+ check(
+ r#"
+//- /lib.rs
+$0
+/// ```
+/// fn foo() {
+/// }
+/// ```
+#[macro_export]
+macro_rules! foo {
+ () => {
+
+ };
+}
+"#,
+ &[DocTest],
+ expect![[r#"
+ [
+ Runnable {
+ use_name_in_title: false,
+ nav: NavigationTarget {
+ file_id: FileId(
+ 0,
+ ),
+ full_range: 1..94,
+ name: "foo",
+ },
+ kind: DocTest {
+ test_id: Path(
+ "foo",
+ ),
+ },
+ cfg: None,
+ },
+ ]
+ "#]],
+ );
+ }
}