aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2019-01-25 07:32:13 +0000
committerbors[bot] <bors[bot]@users.noreply.github.com>2019-01-25 07:32:13 +0000
commit946aea3eb34d1b7f09900e017bb94708e6299d0a (patch)
treea662d8e9c63835950af45792dd538ce7d30b9251
parent675943712ce92e0ce04e85e6952f50bcbf1ee611 (diff)
parent0707f65806961028f2be64812869c66020e2a1a6 (diff)
Merge #634
634: rename def_id -> def r=matklad a=matklad Co-authored-by: Aleksey Kladov <[email protected]>
-rw-r--r--crates/ra_arena/src/map.rs4
-rw-r--r--crates/ra_hir/src/code_model_api.rs4
-rw-r--r--crates/ra_hir/src/code_model_impl/module.rs74
-rw-r--r--crates/ra_hir/src/marks.rs7
-rw-r--r--crates/ra_hir/src/nameres.rs260
-rw-r--r--crates/ra_hir/src/nameres/tests.rs27
-rw-r--r--crates/ra_ide_api/src/completion/completion_item.rs5
-rw-r--r--crates/ra_ide_api/src/marks.rs4
-rw-r--r--crates/test_utils/src/marks.rs6
9 files changed, 186 insertions, 205 deletions
diff --git a/crates/ra_arena/src/map.rs b/crates/ra_arena/src/map.rs
index 2f09d677f..be80edaf3 100644
--- a/crates/ra_arena/src/map.rs
+++ b/crates/ra_arena/src/map.rs
@@ -29,6 +29,10 @@ impl<ID: ArenaId, T> ArenaMap<ID, T> {
29 self.v.get(Self::to_idx(id)).and_then(|it| it.as_ref()) 29 self.v.get(Self::to_idx(id)).and_then(|it| it.as_ref())
30 } 30 }
31 31
32 pub fn get_mut(&mut self, id: ID) -> Option<&mut T> {
33 self.v.get_mut(Self::to_idx(id)).and_then(|it| it.as_mut())
34 }
35
32 pub fn values(&self) -> impl Iterator<Item = &T> { 36 pub fn values(&self) -> impl Iterator<Item = &T> {
33 self.v.iter().filter_map(|o| o.as_ref()) 37 self.v.iter().filter_map(|o| o.as_ref())
34 } 38 }
diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs
index 6739627b4..a469ad477 100644
--- a/crates/ra_hir/src/code_model_api.rs
+++ b/crates/ra_hir/src/code_model_api.rs
@@ -165,11 +165,11 @@ impl Module {
165 165
166 /// Returns a `ModuleScope`: a set of items, visible in this module. 166 /// Returns a `ModuleScope`: a set of items, visible in this module.
167 pub fn scope(&self, db: &impl HirDatabase) -> ModuleScope { 167 pub fn scope(&self, db: &impl HirDatabase) -> ModuleScope {
168 self.scope_impl(db) 168 db.item_map(self.krate)[self.module_id].clone()
169 } 169 }
170 170
171 pub fn resolve_path(&self, db: &impl HirDatabase, path: &Path) -> PerNs<ModuleDef> { 171 pub fn resolve_path(&self, db: &impl HirDatabase, path: &Path) -> PerNs<ModuleDef> {
172 self.resolve_path_impl(db, path) 172 db.item_map(self.krate).resolve_path(db, *self, path)
173 } 173 }
174 174
175 pub fn problems(&self, db: &impl HirDatabase) -> Vec<(TreeArc<SyntaxNode>, Problem)> { 175 pub fn problems(&self, db: &impl HirDatabase) -> Vec<(TreeArc<SyntaxNode>, Problem)> {
diff --git a/crates/ra_hir/src/code_model_impl/module.rs b/crates/ra_hir/src/code_model_impl/module.rs
index 6419d3934..480ec27bf 100644
--- a/crates/ra_hir/src/code_model_impl/module.rs
+++ b/crates/ra_hir/src/code_model_impl/module.rs
@@ -2,10 +2,10 @@ use ra_db::FileId;
2use ra_syntax::{ast, SyntaxNode, TreeArc}; 2use ra_syntax::{ast, SyntaxNode, TreeArc};
3 3
4use crate::{ 4use crate::{
5 Module, ModuleSource, Problem, ModuleDef, 5 Module, ModuleSource, Problem,
6 Crate, Name, Path, PathKind, PerNs, 6 Crate, Name,
7 module_tree::ModuleId, 7 module_tree::ModuleId,
8 nameres::{ModuleScope, lower::ImportId}, 8 nameres::{lower::ImportId},
9 db::HirDatabase, 9 db::HirDatabase,
10}; 10};
11 11
@@ -90,74 +90,6 @@ impl Module {
90 Some(self.with_module_id(parent_id)) 90 Some(self.with_module_id(parent_id))
91 } 91 }
92 92
93 /// Returns a `ModuleScope`: a set of items, visible in this module.
94 pub(crate) fn scope_impl(&self, db: &impl HirDatabase) -> ModuleScope {
95 let item_map = db.item_map(self.krate);
96 item_map.per_module[&self.module_id].clone()
97 }
98
99 pub(crate) fn resolve_path_impl(&self, db: &impl HirDatabase, path: &Path) -> PerNs<ModuleDef> {
100 let mut curr_per_ns: PerNs<ModuleDef> = PerNs::types(match path.kind {
101 PathKind::Crate => self.crate_root(db).into(),
102 PathKind::Self_ | PathKind::Plain => self.clone().into(),
103 PathKind::Super => {
104 if let Some(p) = self.parent(db) {
105 p.into()
106 } else {
107 return PerNs::none();
108 }
109 }
110 PathKind::Abs => {
111 // TODO: absolute use is not supported
112 return PerNs::none();
113 }
114 });
115
116 for segment in path.segments.iter() {
117 let curr = match curr_per_ns.as_ref().take_types() {
118 Some(r) => r,
119 None => {
120 // we still have path segments left, but the path so far
121 // didn't resolve in the types namespace => no resolution
122 // (don't break here because curr_per_ns might contain
123 // something in the value namespace, and it would be wrong
124 // to return that)
125 return PerNs::none();
126 }
127 };
128 // resolve segment in curr
129
130 curr_per_ns = match curr {
131 ModuleDef::Module(m) => {
132 let scope = m.scope(db);
133 match scope.get(&segment.name) {
134 Some(r) => r.def_id.clone(),
135 None => PerNs::none(),
136 }
137 }
138 ModuleDef::Enum(e) => {
139 // enum variant
140 let matching_variant = e
141 .variants(db)
142 .into_iter()
143 .find(|(n, _variant)| n == &segment.name);
144
145 match matching_variant {
146 Some((_n, variant)) => PerNs::both(variant.into(), (*e).into()),
147 None => PerNs::none(),
148 }
149 }
150 _ => {
151 // could be an inherent method call in UFCS form
152 // (`Struct::method`), or some other kind of associated
153 // item... Which we currently don't handle (TODO)
154 PerNs::none()
155 }
156 };
157 }
158 curr_per_ns
159 }
160
161 pub(crate) fn problems_impl( 93 pub(crate) fn problems_impl(
162 &self, 94 &self,
163 db: &impl HirDatabase, 95 db: &impl HirDatabase,
diff --git a/crates/ra_hir/src/marks.rs b/crates/ra_hir/src/marks.rs
index f4d0c3e59..338ed0516 100644
--- a/crates/ra_hir/src/marks.rs
+++ b/crates/ra_hir/src/marks.rs
@@ -1,3 +1,4 @@
1use test_utils::mark; 1test_utils::marks!(
2 2 name_res_works_for_broken_modules
3mark!(name_res_works_for_broken_modules); 3 item_map_enum_importing
4);
diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs
index a3bc98958..639726b5e 100644
--- a/crates/ra_hir/src/nameres.rs
+++ b/crates/ra_hir/src/nameres.rs
@@ -19,6 +19,8 @@ pub(crate) mod lower;
19use std::sync::Arc; 19use std::sync::Arc;
20 20
21use ra_db::CrateId; 21use ra_db::CrateId;
22use ra_arena::map::ArenaMap;
23use test_utils::tested_by;
22use rustc_hash::{FxHashMap, FxHashSet}; 24use rustc_hash::{FxHashMap, FxHashSet};
23 25
24use crate::{ 26use crate::{
@@ -27,16 +29,21 @@ use crate::{
27 HirDatabase, Crate, 29 HirDatabase, Crate,
28 Name, 30 Name,
29 module_tree::{ModuleId, ModuleTree}, 31 module_tree::{ModuleId, ModuleTree},
30//FIXME: deglobify 32 nameres::lower::{ImportId, LoweredModule, ImportData},
31 nameres::lower::*,
32}; 33};
33 34
34/// `ItemMap` is the result of name resolution. It contains, for each 35/// `ItemMap` is the result of name resolution. It contains, for each
35/// module, the set of visible items. 36/// module, the set of visible items.
36// FIXME: currenty we compute item map per source-root. We should do it per crate instead.
37#[derive(Default, Debug, PartialEq, Eq)] 37#[derive(Default, Debug, PartialEq, Eq)]
38pub struct ItemMap { 38pub struct ItemMap {
39 pub per_module: FxHashMap<ModuleId, ModuleScope>, 39 per_module: ArenaMap<ModuleId, ModuleScope>,
40}
41
42impl std::ops::Index<ModuleId> for ItemMap {
43 type Output = ModuleScope;
44 fn index(&self, id: ModuleId) -> &ModuleScope {
45 &self.per_module[id]
46 }
40} 47}
41 48
42#[derive(Debug, Default, PartialEq, Eq, Clone)] 49#[derive(Debug, Default, PartialEq, Eq, Clone)]
@@ -58,7 +65,7 @@ impl ModuleScope {
58#[derive(Debug, Clone, PartialEq, Eq)] 65#[derive(Debug, Clone, PartialEq, Eq)]
59pub struct Resolution { 66pub struct Resolution {
60 /// None for unresolved 67 /// None for unresolved
61 pub def_id: PerNs<ModuleDef>, 68 pub def: PerNs<ModuleDef>,
62 /// ident by which this is imported into local scope. 69 /// ident by which this is imported into local scope.
63 pub import: Option<ImportId>, 70 pub import: Option<ImportId>,
64} 71}
@@ -210,11 +217,11 @@ where
210 let krate = Crate::new(crate_id); 217 let krate = Crate::new(crate_id);
211 for dep in krate.dependencies(self.db) { 218 for dep in krate.dependencies(self.db) {
212 if let Some(module) = dep.krate.root_module(self.db) { 219 if let Some(module) = dep.krate.root_module(self.db) {
213 let def_id = module.into(); 220 let def = module.into();
214 self.add_module_item( 221 self.add_module_item(
215 &mut module_items, 222 &mut module_items,
216 dep.name.clone(), 223 dep.name.clone(),
217 PerNs::types(def_id), 224 PerNs::types(def),
218 ); 225 );
219 } 226 }
220 } 227 }
@@ -226,7 +233,7 @@ where
226 module_items.items.insert( 233 module_items.items.insert(
227 segment.name.clone(), 234 segment.name.clone(),
228 Resolution { 235 Resolution {
229 def_id: PerNs::none(), 236 def: PerNs::none(),
230 import: Some(import_id), 237 import: Some(import_id),
231 }, 238 },
232 ); 239 );
@@ -234,11 +241,8 @@ where
234 } 241 }
235 } 242 }
236 // Populate explicitly declared items, except modules 243 // Populate explicitly declared items, except modules
237 for (name, &def_id) in input.declarations.iter() { 244 for (name, &def) in input.declarations.iter() {
238 let resolution = Resolution { 245 let resolution = Resolution { def, import: None };
239 def_id,
240 import: None,
241 };
242 module_items.items.insert(name.clone(), resolution); 246 module_items.items.insert(name.clone(), resolution);
243 } 247 }
244 248
@@ -254,16 +258,8 @@ where
254 self.result.per_module.insert(module_id, module_items); 258 self.result.per_module.insert(module_id, module_items);
255 } 259 }
256 260
257 fn add_module_item( 261 fn add_module_item(&self, module_items: &mut ModuleScope, name: Name, def: PerNs<ModuleDef>) {
258 &self, 262 let resolution = Resolution { def, import: None };
259 module_items: &mut ModuleScope,
260 name: Name,
261 def_id: PerNs<ModuleDef>,
262 ) {
263 let resolution = Resolution {
264 def_id,
265 import: None,
266 };
267 module_items.items.insert(name, resolution); 263 module_items.items.insert(name, resolution);
268 } 264 }
269 265
@@ -273,7 +269,7 @@ where
273 // already done 269 // already done
274 continue; 270 continue;
275 } 271 }
276 if self.resolve_import(module_id, import_id, import_data) { 272 if self.resolve_import(module_id, import_id, import_data) == ReachedFixedPoint::Yes {
277 log::debug!("import {:?} resolved (or definite error)", import_id); 273 log::debug!("import {:?} resolved (or definite error)", import_id);
278 self.processed_imports.insert((module_id, import_id)); 274 self.processed_imports.insert((module_id, import_id));
279 } 275 }
@@ -285,116 +281,146 @@ where
285 module_id: ModuleId, 281 module_id: ModuleId,
286 import_id: ImportId, 282 import_id: ImportId,
287 import: &ImportData, 283 import: &ImportData,
288 ) -> bool { 284 ) -> ReachedFixedPoint {
289 log::debug!("resolving import: {:?}", import); 285 log::debug!("resolving import: {:?}", import);
290 if import.is_glob { 286 if import.is_glob {
291 return false; 287 return ReachedFixedPoint::Yes;
292 }; 288 };
289 let original_module = Module {
290 krate: self.krate,
291 module_id,
292 };
293 let (def, reached_fixedpoint) =
294 self.result
295 .resolve_path_fp(self.db, original_module, &import.path);
296
297 if reached_fixedpoint == ReachedFixedPoint::Yes {
298 let last_segment = import.path.segments.last().unwrap();
299 self.update(module_id, |items| {
300 let res = Resolution {
301 def,
302 import: Some(import_id),
303 };
304 items.items.insert(last_segment.name.clone(), res);
305 });
306 log::debug!(
307 "resolved import {:?} ({:?}) cross-source root to {:?}",
308 last_segment.name,
309 import,
310 def,
311 );
312 }
313 reached_fixedpoint
314 }
315
316 fn update(&mut self, module_id: ModuleId, f: impl FnOnce(&mut ModuleScope)) {
317 let module_items = self.result.per_module.get_mut(module_id).unwrap();
318 f(module_items)
319 }
320}
321
322#[derive(Debug, Clone, Copy, PartialEq, Eq)]
323enum ReachedFixedPoint {
324 Yes,
325 No,
326}
293 327
294 let mut curr: ModuleId = match import.path.kind { 328impl ItemMap {
295 PathKind::Plain | PathKind::Self_ => module_id, 329 pub(crate) fn resolve_path(
330 &self,
331 db: &impl HirDatabase,
332 original_module: Module,
333 path: &Path,
334 ) -> PerNs<ModuleDef> {
335 self.resolve_path_fp(db, original_module, path).0
336 }
337
338 // Returns Yes if we are sure that additions to `ItemMap` wouldn't change
339 // the result.
340 fn resolve_path_fp(
341 &self,
342 db: &impl HirDatabase,
343 original_module: Module,
344 path: &Path,
345 ) -> (PerNs<ModuleDef>, ReachedFixedPoint) {
346 let mut curr_per_ns: PerNs<ModuleDef> = PerNs::types(match path.kind {
347 PathKind::Crate => original_module.crate_root(db).into(),
348 PathKind::Self_ | PathKind::Plain => original_module.into(),
296 PathKind::Super => { 349 PathKind::Super => {
297 match module_id.parent(&self.module_tree) { 350 if let Some(p) = original_module.parent(db) {
298 Some(it) => it, 351 p.into()
299 None => { 352 } else {
300 // TODO: error 353 log::debug!("super path in root module");
301 log::debug!("super path in root module"); 354 return (PerNs::none(), ReachedFixedPoint::Yes);
302 return true; // this can't suddenly resolve if we just resolve some other imports
303 }
304 } 355 }
305 } 356 }
306 PathKind::Crate => module_id.crate_root(&self.module_tree),
307 PathKind::Abs => { 357 PathKind::Abs => {
308 // TODO: absolute use is not supported for now 358 // TODO: absolute use is not supported
309 return false; 359 return (PerNs::none(), ReachedFixedPoint::Yes);
310 } 360 }
311 }; 361 });
312 362
313 for (i, segment) in import.path.segments.iter().enumerate() { 363 for (i, segment) in path.segments.iter().enumerate() {
314 let is_last = i == import.path.segments.len() - 1; 364 let curr = match curr_per_ns.as_ref().take_types() {
315 365 Some(r) => r,
316 let def_id = match self.result.per_module[&curr].items.get(&segment.name) { 366 None => {
317 Some(res) if !res.def_id.is_none() => res.def_id, 367 // we still have path segments left, but the path so far
318 _ => { 368 // didn't resolve in the types namespace => no resolution
319 log::debug!("path segment {:?} not found", segment.name); 369 // (don't break here because curr_per_ns might contain
320 return false; 370 // something in the value namespace, and it would be wrong
371 // to return that)
372 return (PerNs::none(), ReachedFixedPoint::No);
321 } 373 }
322 }; 374 };
375 // resolve segment in curr
376
377 curr_per_ns = match curr {
378 ModuleDef::Module(module) => {
379 if module.krate != original_module.krate {
380 let path = Path {
381 segments: path.segments[i..].iter().cloned().collect(),
382 kind: PathKind::Crate,
383 };
384 log::debug!("resolving {:?} in other crate", path);
385 let def = module.resolve_path(db, &path);
386 return (def, ReachedFixedPoint::Yes);
387 }
323 388
324 if !is_last { 389 match self[module.module_id].items.get(&segment.name) {
325 let type_def_id = if let Some(d) = def_id.take(Namespace::Types) { 390 Some(res) if !res.def.is_none() => res.def,
326 d 391 _ => {
327 } else { 392 log::debug!("path segment {:?} not found", segment.name);
328 log::debug!( 393 return (PerNs::none(), ReachedFixedPoint::No);
329 "path segment {:?} resolved to value only, but is not last",
330 segment.name
331 );
332 return false;
333 };
334 curr = match type_def_id {
335 ModuleDef::Module(module) => {
336 if module.krate == self.krate {
337 module.module_id
338 } else {
339 let path = Path {
340 segments: import.path.segments[i + 1..].iter().cloned().collect(),
341 kind: PathKind::Crate,
342 };
343 log::debug!("resolving {:?} in other source root", path);
344 let def_id = module.resolve_path(self.db, &path);
345 if !def_id.is_none() {
346 let last_segment = path.segments.last().unwrap();
347 self.update(module_id, |items| {
348 let res = Resolution {
349 def_id,
350 import: Some(import_id),
351 };
352 items.items.insert(last_segment.name.clone(), res);
353 });
354 log::debug!(
355 "resolved import {:?} ({:?}) cross-source root to {:?}",
356 last_segment.name,
357 import,
358 def_id,
359 );
360 return true;
361 } else {
362 log::debug!("rest of path did not resolve in other source root");
363 return true;
364 }
365 } 394 }
366 } 395 }
367 _ => { 396 }
368 log::debug!( 397 ModuleDef::Enum(e) => {
369 "path segment {:?} resolved to non-module {:?}, but is not last", 398 // enum variant
370 segment.name, 399 tested_by!(item_map_enum_importing);
371 type_def_id, 400 let matching_variant = e
372 ); 401 .variants(db)
373 return true; // this resolved to a non-module, so the path won't ever resolve 402 .into_iter()
403 .find(|(n, _variant)| n == &segment.name);
404
405 match matching_variant {
406 Some((_n, variant)) => PerNs::both(variant.into(), (*e).into()),
407 None => PerNs::none(),
374 } 408 }
375 } 409 }
376 } else { 410 _ => {
377 log::debug!( 411 // could be an inherent method call in UFCS form
378 "resolved import {:?} ({:?}) within source root to {:?}", 412 // (`Struct::method`), or some other kind of associated
379 segment.name, 413 // item... Which we currently don't handle (TODO)
380 import, 414 log::debug!(
381 def_id, 415 "path segment {:?} resolved to non-module {:?}, but is not last",
382 ); 416 segment.name,
383 self.update(module_id, |items| { 417 curr,
384 let res = Resolution { 418 );
385 def_id, 419 return (PerNs::none(), ReachedFixedPoint::Yes);
386 import: Some(import_id), 420 }
387 }; 421 };
388 items.items.insert(segment.name.clone(), res);
389 })
390 }
391 } 422 }
392 true 423 (curr_per_ns, ReachedFixedPoint::Yes)
393 }
394
395 fn update(&mut self, module_id: ModuleId, f: impl FnOnce(&mut ModuleScope)) {
396 let module_items = self.result.per_module.get_mut(&module_id).unwrap();
397 f(module_items)
398 } 424 }
399} 425}
400 426
diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs
index 9322bf08c..c033bebe8 100644
--- a/crates/ra_hir/src/nameres/tests.rs
+++ b/crates/ra_hir/src/nameres/tests.rs
@@ -20,7 +20,7 @@ fn item_map(fixture: &str) -> (Arc<ItemMap>, ModuleId) {
20} 20}
21 21
22fn check_module_item_map(map: &ItemMap, module_id: ModuleId, expected: &str) { 22fn check_module_item_map(map: &ItemMap, module_id: ModuleId, expected: &str) {
23 let mut lines = map.per_module[&module_id] 23 let mut lines = map[module_id]
24 .items 24 .items
25 .iter() 25 .iter()
26 .map(|(name, res)| format!("{}: {}", name, dump_resolution(res))) 26 .map(|(name, res)| format!("{}: {}", name, dump_resolution(res)))
@@ -37,8 +37,8 @@ fn check_module_item_map(map: &ItemMap, module_id: ModuleId, expected: &str) {
37 37
38 fn dump_resolution(resolution: &Resolution) -> &'static str { 38 fn dump_resolution(resolution: &Resolution) -> &'static str {
39 match ( 39 match (
40 resolution.def_id.types.is_some(), 40 resolution.def.types.is_some(),
41 resolution.def_id.values.is_some(), 41 resolution.def.values.is_some(),
42 ) { 42 ) {
43 (true, true) => "t v", 43 (true, true) => "t v",
44 (true, false) => "t", 44 (true, false) => "t",
@@ -216,6 +216,27 @@ fn item_map_using_self() {
216} 216}
217 217
218#[test] 218#[test]
219fn item_map_enum_importing() {
220 covers!(item_map_enum_importing);
221 let (item_map, module_id) = item_map(
222 "
223 //- /lib.rs
224 enum E { V }
225 use self::E::V;
226 <|>
227 ",
228 );
229 check_module_item_map(
230 &item_map,
231 module_id,
232 "
233 E: t
234 V: t v
235 ",
236 );
237}
238
239#[test]
219fn item_map_across_crates() { 240fn item_map_across_crates() {
220 let (mut db, sr) = MockDatabase::with_files( 241 let (mut db, sr) = MockDatabase::with_files(
221 " 242 "
diff --git a/crates/ra_ide_api/src/completion/completion_item.rs b/crates/ra_ide_api/src/completion/completion_item.rs
index 3ba6c33ee..b2b047766 100644
--- a/crates/ra_ide_api/src/completion/completion_item.rs
+++ b/crates/ra_ide_api/src/completion/completion_item.rs
@@ -209,10 +209,7 @@ impl Builder {
209 ctx: &CompletionContext, 209 ctx: &CompletionContext,
210 resolution: &hir::Resolution, 210 resolution: &hir::Resolution,
211 ) -> Builder { 211 ) -> Builder {
212 let def = resolution 212 let def = resolution.def.take_types().or(resolution.def.take_values());
213 .def_id
214 .take_types()
215 .or(resolution.def_id.take_values());
216 let def = match def { 213 let def = match def {
217 None => return self, 214 None => return self,
218 Some(it) => it, 215 Some(it) => it,
diff --git a/crates/ra_ide_api/src/marks.rs b/crates/ra_ide_api/src/marks.rs
index b4a726ef0..dc5b2702a 100644
--- a/crates/ra_ide_api/src/marks.rs
+++ b/crates/ra_ide_api/src/marks.rs
@@ -1,3 +1 @@
1use test_utils::mark; test_utils::marks!(inserts_parens_for_function_calls);
2
3mark!(inserts_parens_for_function_calls);
diff --git a/crates/test_utils/src/marks.rs b/crates/test_utils/src/marks.rs
index 79ffedf69..ee47b5219 100644
--- a/crates/test_utils/src/marks.rs
+++ b/crates/test_utils/src/marks.rs
@@ -46,11 +46,13 @@ macro_rules! covers {
46} 46}
47 47
48#[macro_export] 48#[macro_export]
49macro_rules! mark { 49macro_rules! marks {
50 ($ident:ident) => { 50 ($($ident:ident)*) => {
51 $(
51 #[allow(bad_style)] 52 #[allow(bad_style)]
52 pub(crate) static $ident: std::sync::atomic::AtomicUsize = 53 pub(crate) static $ident: std::sync::atomic::AtomicUsize =
53 std::sync::atomic::AtomicUsize::new(0); 54 std::sync::atomic::AtomicUsize::new(0);
55 )*
54 }; 56 };
55} 57}
56 58