aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock8
-rw-r--r--crates/hir/src/code_model.rs1
-rw-r--r--crates/hir_def/src/generics.rs37
-rw-r--r--crates/hir_ty/src/lower.rs18
-rw-r--r--crates/hir_ty/src/utils.rs9
-rw-r--r--crates/ide/src/goto_definition.rs28
-rw-r--r--crates/mbe/src/tests.rs12
-rw-r--r--crates/parser/src/grammar.rs2
-rw-r--r--crates/parser/src/lib.rs2
-rw-r--r--docs/user/manual.adoc42
10 files changed, 119 insertions, 40 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 1051c4ec6..fbb79e01f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1251,9 +1251,9 @@ dependencies = [
1251 1251
1252[[package]] 1252[[package]]
1253name = "quote" 1253name = "quote"
1254version = "1.0.7" 1254version = "1.0.8"
1255source = "registry+https://github.com/rust-lang/crates.io-index" 1255source = "registry+https://github.com/rust-lang/crates.io-index"
1256checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" 1256checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df"
1257dependencies = [ 1257dependencies = [
1258 "proc-macro2", 1258 "proc-macro2",
1259] 1259]
@@ -1588,9 +1588,9 @@ version = "0.0.0"
1588 1588
1589[[package]] 1589[[package]]
1590name = "syn" 1590name = "syn"
1591version = "1.0.54" 1591version = "1.0.55"
1592source = "registry+https://github.com/rust-lang/crates.io-index" 1592source = "registry+https://github.com/rust-lang/crates.io-index"
1593checksum = "9a2af957a63d6bd42255c359c93d9bfdb97076bd3b820897ce55ffbfbf107f44" 1593checksum = "a571a711dddd09019ccc628e1b17fe87c59b09d513c06c026877aa708334f37a"
1594dependencies = [ 1594dependencies = [
1595 "proc-macro2", 1595 "proc-macro2",
1596 "quote", 1596 "quote",
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs
index d6c7e71ea..73ca6ba9f 100644
--- a/crates/hir/src/code_model.rs
+++ b/crates/hir/src/code_model.rs
@@ -1268,7 +1268,6 @@ impl LifetimeParam {
1268 } 1268 }
1269} 1269}
1270 1270
1271// FIXME: rename from `ImplDef` to `Impl`
1272#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 1271#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1273pub struct Impl { 1272pub struct Impl {
1274 pub(crate) id: ImplId, 1273 pub(crate) id: ImplId,
diff --git a/crates/hir_def/src/generics.rs b/crates/hir_def/src/generics.rs
index 41134d23b..bb8fca009 100644
--- a/crates/hir_def/src/generics.rs
+++ b/crates/hir_def/src/generics.rs
@@ -62,6 +62,7 @@ pub struct GenericParams {
62pub enum WherePredicate { 62pub enum WherePredicate {
63 TypeBound { target: WherePredicateTypeTarget, bound: TypeBound }, 63 TypeBound { target: WherePredicateTypeTarget, bound: TypeBound },
64 Lifetime { target: LifetimeRef, bound: LifetimeRef }, 64 Lifetime { target: LifetimeRef, bound: LifetimeRef },
65 ForLifetime { lifetimes: Box<[Name]>, target: WherePredicateTypeTarget, bound: TypeBound },
65} 66}
66 67
67#[derive(Clone, PartialEq, Eq, Debug)] 68#[derive(Clone, PartialEq, Eq, Debug)]
@@ -69,7 +70,6 @@ pub enum WherePredicateTypeTarget {
69 TypeRef(TypeRef), 70 TypeRef(TypeRef),
70 /// For desugared where predicates that can directly refer to a type param. 71 /// For desugared where predicates that can directly refer to a type param.
71 TypeParam(LocalTypeParamId), 72 TypeParam(LocalTypeParamId),
72 // FIXME: ForLifetime(Vec<LifetimeParamId>, TypeRef)
73} 73}
74 74
75#[derive(Default)] 75#[derive(Default)]
@@ -234,7 +234,7 @@ impl GenericParams {
234 for bound in 234 for bound in
235 node.type_bound_list().iter().flat_map(|type_bound_list| type_bound_list.bounds()) 235 node.type_bound_list().iter().flat_map(|type_bound_list| type_bound_list.bounds())
236 { 236 {
237 self.add_where_predicate_from_bound(lower_ctx, bound, target.clone()); 237 self.add_where_predicate_from_bound(lower_ctx, bound, None, target.clone());
238 } 238 }
239 } 239 }
240 240
@@ -279,8 +279,25 @@ impl GenericParams {
279 } else { 279 } else {
280 continue; 280 continue;
281 }; 281 };
282
283 let lifetimes: Option<Box<_>> = pred.generic_param_list().map(|param_list| {
284 // Higher-Ranked Trait Bounds
285 param_list
286 .lifetime_params()
287 .map(|lifetime_param| {
288 lifetime_param
289 .lifetime()
290 .map_or_else(Name::missing, |lt| Name::new_lifetime(&lt))
291 })
292 .collect()
293 });
282 for bound in pred.type_bound_list().iter().flat_map(|l| l.bounds()) { 294 for bound in pred.type_bound_list().iter().flat_map(|l| l.bounds()) {
283 self.add_where_predicate_from_bound(lower_ctx, bound, target.clone()); 295 self.add_where_predicate_from_bound(
296 lower_ctx,
297 bound,
298 lifetimes.as_ref(),
299 target.clone(),
300 );
284 } 301 }
285 } 302 }
286 } 303 }
@@ -289,6 +306,7 @@ impl GenericParams {
289 &mut self, 306 &mut self,
290 lower_ctx: &LowerCtx, 307 lower_ctx: &LowerCtx,
291 bound: ast::TypeBound, 308 bound: ast::TypeBound,
309 hrtb_lifetimes: Option<&Box<[Name]>>,
292 target: Either<TypeRef, LifetimeRef>, 310 target: Either<TypeRef, LifetimeRef>,
293 ) { 311 ) {
294 if bound.question_mark_token().is_some() { 312 if bound.question_mark_token().is_some() {
@@ -297,9 +315,16 @@ impl GenericParams {
297 } 315 }
298 let bound = TypeBound::from_ast(lower_ctx, bound); 316 let bound = TypeBound::from_ast(lower_ctx, bound);
299 let predicate = match (target, bound) { 317 let predicate = match (target, bound) {
300 (Either::Left(type_ref), bound) => WherePredicate::TypeBound { 318 (Either::Left(type_ref), bound) => match hrtb_lifetimes {
301 target: WherePredicateTypeTarget::TypeRef(type_ref), 319 Some(hrtb_lifetimes) => WherePredicate::ForLifetime {
302 bound, 320 lifetimes: hrtb_lifetimes.clone(),
321 target: WherePredicateTypeTarget::TypeRef(type_ref),
322 bound,
323 },
324 None => WherePredicate::TypeBound {
325 target: WherePredicateTypeTarget::TypeRef(type_ref),
326 bound,
327 },
303 }, 328 },
304 (Either::Right(lifetime), TypeBound::Lifetime(bound)) => { 329 (Either::Right(lifetime), TypeBound::Lifetime(bound)) => {
305 WherePredicate::Lifetime { target: lifetime, bound } 330 WherePredicate::Lifetime { target: lifetime, bound }
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index 8392cb770..8da56cd11 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -675,7 +675,8 @@ impl GenericPredicate {
675 where_predicate: &'a WherePredicate, 675 where_predicate: &'a WherePredicate,
676 ) -> impl Iterator<Item = GenericPredicate> + 'a { 676 ) -> impl Iterator<Item = GenericPredicate> + 'a {
677 match where_predicate { 677 match where_predicate {
678 WherePredicate::TypeBound { target, bound } => { 678 WherePredicate::ForLifetime { target, bound, .. }
679 | WherePredicate::TypeBound { target, bound } => {
679 let self_ty = match target { 680 let self_ty = match target {
680 WherePredicateTypeTarget::TypeRef(type_ref) => Ty::from_hir(ctx, type_ref), 681 WherePredicateTypeTarget::TypeRef(type_ref) => Ty::from_hir(ctx, type_ref),
681 WherePredicateTypeTarget::TypeParam(param_id) => { 682 WherePredicateTypeTarget::TypeParam(param_id) => {
@@ -888,14 +889,13 @@ pub(crate) fn generic_predicates_for_param_query(
888 .where_predicates_in_scope() 889 .where_predicates_in_scope()
889 // we have to filter out all other predicates *first*, before attempting to lower them 890 // we have to filter out all other predicates *first*, before attempting to lower them
890 .filter(|pred| match pred { 891 .filter(|pred| match pred {
891 WherePredicate::TypeBound { 892 WherePredicate::ForLifetime { target, .. }
892 target: WherePredicateTypeTarget::TypeRef(type_ref), 893 | WherePredicate::TypeBound { target, .. } => match target {
893 .. 894 WherePredicateTypeTarget::TypeRef(type_ref) => {
894 } => Ty::from_hir_only_param(&ctx, type_ref) == Some(param_id), 895 Ty::from_hir_only_param(&ctx, type_ref) == Some(param_id)
895 WherePredicate::TypeBound { 896 }
896 target: WherePredicateTypeTarget::TypeParam(local_id), 897 WherePredicateTypeTarget::TypeParam(local_id) => *local_id == param_id.local_id,
897 .. 898 },
898 } => *local_id == param_id.local_id,
899 WherePredicate::Lifetime { .. } => false, 899 WherePredicate::Lifetime { .. } => false,
900 }) 900 })
901 .flat_map(|pred| { 901 .flat_map(|pred| {
diff --git a/crates/hir_ty/src/utils.rs b/crates/hir_ty/src/utils.rs
index af880c065..65b79df0d 100644
--- a/crates/hir_ty/src/utils.rs
+++ b/crates/hir_ty/src/utils.rs
@@ -5,7 +5,9 @@ use std::sync::Arc;
5use hir_def::{ 5use hir_def::{
6 adt::VariantData, 6 adt::VariantData,
7 db::DefDatabase, 7 db::DefDatabase,
8 generics::{GenericParams, TypeParamData, TypeParamProvenance, WherePredicateTypeTarget}, 8 generics::{
9 GenericParams, TypeParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
10 },
9 path::Path, 11 path::Path,
10 resolver::{HasResolver, TypeNs}, 12 resolver::{HasResolver, TypeNs},
11 type_ref::TypeRef, 13 type_ref::TypeRef,
@@ -27,7 +29,8 @@ fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> {
27 .where_predicates 29 .where_predicates
28 .iter() 30 .iter()
29 .filter_map(|pred| match pred { 31 .filter_map(|pred| match pred {
30 hir_def::generics::WherePredicate::TypeBound { target, bound } => match target { 32 WherePredicate::ForLifetime { target, bound, .. }
33 | WherePredicate::TypeBound { target, bound } => match target {
31 WherePredicateTypeTarget::TypeRef(TypeRef::Path(p)) 34 WherePredicateTypeTarget::TypeRef(TypeRef::Path(p))
32 if p == &Path::from(name![Self]) => 35 if p == &Path::from(name![Self]) =>
33 { 36 {
@@ -38,7 +41,7 @@ fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> {
38 } 41 }
39 _ => None, 42 _ => None,
40 }, 43 },
41 hir_def::generics::WherePredicate::Lifetime { .. } => None, 44 WherePredicate::Lifetime { .. } => None,
42 }) 45 })
43 .filter_map(|path| match resolver.resolve_path_in_type_ns_fully(db, path.mod_path()) { 46 .filter_map(|path| match resolver.resolve_path_in_type_ns_fully(db, path.mod_path()) {
44 Some(TypeNs::TraitId(t)) => Some(t), 47 Some(TypeNs::TraitId(t)) => Some(t),
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index 7a12e9965..431da5d9c 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -1077,4 +1077,32 @@ fn foo<'foobar>(_: &'foobar ()) {
1077}"#, 1077}"#,
1078 ) 1078 )
1079 } 1079 }
1080
1081 #[test]
1082 #[ignore] // requires the HIR to somehow track these hrtb lifetimes
1083 fn goto_lifetime_hrtb() {
1084 check(
1085 r#"trait Foo<T> {}
1086fn foo<T>() where for<'a> T: Foo<&'a<|> (u8, u16)>, {}
1087 //^^
1088"#,
1089 );
1090 check(
1091 r#"trait Foo<T> {}
1092fn foo<T>() where for<'a<|>> T: Foo<&'a (u8, u16)>, {}
1093 //^^
1094"#,
1095 );
1096 }
1097
1098 #[test]
1099 #[ignore] // requires ForTypes to be implemented
1100 fn goto_lifetime_hrtb_for_type() {
1101 check(
1102 r#"trait Foo<T> {}
1103fn foo<T>() where T: for<'a> Foo<&'a<|> (u8, u16)>, {}
1104 //^^
1105"#,
1106 );
1107 }
1080} 1108}
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs
index f10e7a9b6..451fa1456 100644
--- a/crates/mbe/src/tests.rs
+++ b/crates/mbe/src/tests.rs
@@ -1004,6 +1004,18 @@ fn test_underscore() {
1004} 1004}
1005 1005
1006#[test] 1006#[test]
1007fn test_vertical_bar_with_pat() {
1008 parse_macro(
1009 r#"
1010 macro_rules! foo {
1011 (| $pat:pat | ) => { 0 }
1012 }
1013 "#,
1014 )
1015 .assert_expand_items(r#"foo! { | x | }"#, r#"0"#);
1016}
1017
1018#[test]
1007fn test_lifetime() { 1019fn test_lifetime() {
1008 parse_macro( 1020 parse_macro(
1009 r#" 1021 r#"
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs
index 1a078f6b4..f08c8bab7 100644
--- a/crates/parser/src/grammar.rs
+++ b/crates/parser/src/grammar.rs
@@ -55,7 +55,7 @@ pub(crate) mod fragments {
55 use super::*; 55 use super::*;
56 56
57 pub(crate) use super::{ 57 pub(crate) use super::{
58 expressions::block_expr, paths::type_path as path, patterns::pattern, types::type_, 58 expressions::block_expr, paths::type_path as path, patterns::pattern_single, types::type_,
59 }; 59 };
60 60
61 pub(crate) fn expr(p: &mut Parser) { 61 pub(crate) fn expr(p: &mut Parser) {
diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs
index ab8e4c70e..811e740f9 100644
--- a/crates/parser/src/lib.rs
+++ b/crates/parser/src/lib.rs
@@ -112,7 +112,7 @@ pub fn parse_fragment(
112 FragmentKind::Path => grammar::fragments::path, 112 FragmentKind::Path => grammar::fragments::path,
113 FragmentKind::Expr => grammar::fragments::expr, 113 FragmentKind::Expr => grammar::fragments::expr,
114 FragmentKind::Type => grammar::fragments::type_, 114 FragmentKind::Type => grammar::fragments::type_,
115 FragmentKind::Pattern => grammar::fragments::pattern, 115 FragmentKind::Pattern => grammar::fragments::pattern_single,
116 FragmentKind::Item => grammar::fragments::item, 116 FragmentKind::Item => grammar::fragments::item,
117 FragmentKind::Block => grammar::fragments::block_expr, 117 FragmentKind::Block => grammar::fragments::block_expr,
118 FragmentKind::Visibility => grammar::fragments::opt_visibility, 118 FragmentKind::Visibility => grammar::fragments::opt_visibility,
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc
index 1ec6e81bb..40f10972f 100644
--- a/docs/user/manual.adoc
+++ b/docs/user/manual.adoc
@@ -46,8 +46,8 @@ You can install the latest release of the plugin from
46https://marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer[the marketplace]. 46https://marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer[the marketplace].
47 47
48Note that the plugin may cause conflicts with the 48Note that the plugin may cause conflicts with the
49https://marketplace.visualstudio.com/items?itemName=rust-lang.rust[official Rust plugin]. It is 49https://marketplace.visualstudio.com/items?itemName=rust-lang.rust[official Rust plugin].
50recommended to disable the Rust plugin when using the rust-analyzer extension. 50It is recommended to disable the Rust plugin when using the rust-analyzer extension.
51 51
52By default, the plugin will prompt you to download the matching version of the server as well: 52By default, the plugin will prompt you to download the matching version of the server as well:
53 53
@@ -74,18 +74,21 @@ Note that we only support two most recent versions of VS Code.
74 74
75==== Updates 75==== Updates
76 76
77The extension will be updated automatically as new versions become available. It will ask your permission to download the matching language server version binary if needed. 77The extension will be updated automatically as new versions become available.
78It will ask your permission to download the matching language server version binary if needed.
78 79
79===== Nightly 80===== Nightly
80 81
81We ship nightly releases for VS Code. To help us out with testing the newest code and follow the bleeding edge of our `master`, please use the following config: 82We ship nightly releases for VS Code.
83To help us out with testing the newest code and follow the bleeding edge of our `master`, please use the following config:
82 84
83[source,json] 85[source,json]
84---- 86----
85{ "rust-analyzer.updates.channel": "nightly" } 87{ "rust-analyzer.updates.channel": "nightly" }
86---- 88----
87 89
88You will be prompted to install the `nightly` extension version. Just click `Download now` and from that moment you will get automatic updates every 24 hours. 90You will be prompted to install the `nightly` extension version.
91Just click `Download now` and from that moment you will get automatic updates every 24 hours.
89 92
90If you don't want to be asked for `Download now` every day when the new nightly version is released add the following to your `settings.json`: 93If you don't want to be asked for `Download now` every day when the new nightly version is released add the following to your `settings.json`:
91[source,json] 94[source,json]
@@ -160,7 +163,8 @@ $ chmod +x ~/.local/bin/rust-analyzer
160 163
161Ensure `~/.local/bin` is listed in the `$PATH` variable. 164Ensure `~/.local/bin` is listed in the `$PATH` variable.
162 165
163Alternatively, you can install it from source using the following command: 166Alternatively, you can install it from source using the command below.
167You'll need the latest stable version of the Rust toolchain.
164 168
165[source,bash] 169[source,bash]
166---- 170----
@@ -198,7 +202,8 @@ Emacs support is maintained as part of the https://github.com/emacs-lsp/lsp-mode
198 202
199=== Vim/NeoVim 203=== Vim/NeoVim
200 204
201Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>. Not needed if the extension can install/update it on its own, coc-rust-analyzer is one example. 205Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>.
206Not needed if the extension can install/update it on its own, coc-rust-analyzer is one example.
202 207
203The are several LSP client implementations for vim or neovim: 208The are several LSP client implementations for vim or neovim:
204 209
@@ -270,14 +275,16 @@ Once `neovim/nvim-lspconfig` is installed, use `+lua require'nvim_lsp'.rust_anal
270 275
271Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>. 276Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>.
272 277
273You also need the `LSP` package. To install it: 278You also need the `LSP` package.
279To install it:
274 280
2751. If you've never installed a Sublime Text package, install Package Control: 2811. If you've never installed a Sublime Text package, install Package Control:
276 * Open the command palette (Win/Linux: `ctrl+shift+p`, Mac: `cmd+shift+p`) 282 * Open the command palette (Win/Linux: `ctrl+shift+p`, Mac: `cmd+shift+p`)
277 * Type `Install Package Control`, press enter 283 * Type `Install Package Control`, press enter
2782. In the command palette, run `Package control: Install package`, and in the list that pops up, type `LSP` and press enter. 2842. In the command palette, run `Package control: Install package`, and in the list that pops up, type `LSP` and press enter.
279 285
280Finally, with your Rust project open, in the command palette, run `LSP: Enable Language Server In Project` or `LSP: Enable Language Server Globally`, then select `rust-analyzer` in the list that pops up to enable the rust-analyzer LSP. The latter means that rust-analyzer is enabled by default in Rust projects. 286Finally, with your Rust project open, in the command palette, run `LSP: Enable Language Server In Project` or `LSP: Enable Language Server Globally`, then select `rust-analyzer` in the list that pops up to enable the rust-analyzer LSP.
287The latter means that rust-analyzer is enabled by default in Rust projects.
281 288
282If it worked, you should see "rust-analyzer, Line X, Column Y" on the left side of the bottom bar, and after waiting a bit, functionality like tooltips on hovering over variables should become available. 289If it worked, you should see "rust-analyzer, Line X, Column Y" on the left side of the bottom bar, and after waiting a bit, functionality like tooltips on hovering over variables should become available.
283 290
@@ -285,7 +292,8 @@ If you get an error saying `No such file or directory: 'rust-analyzer'`, see the
285 292
286=== GNOME Builder 293=== GNOME Builder
287 294
288GNOME Builder 3.37.1 and newer has native `rust-analyzer` support. If the LSP binary is not available, GNOME Builder can install it when opening a Rust file. 295GNOME Builder 3.37.1 and newer has native `rust-analyzer` support.
296If the LSP binary is not available, GNOME Builder can install it when opening a Rust file.
289 297
290== Configuration 298== Configuration
291 299
@@ -413,8 +421,8 @@ include::./generated_diagnostic.adoc[]
413 421
414==== Color configurations 422==== Color configurations
415 423
416It is possible to change the foreground/background color of inlay hints. Just add this to your 424It is possible to change the foreground/background color of inlay hints.
417`settings.json`: 425Just add this to your `settings.json`:
418 426
419[source,jsonc] 427[source,jsonc]
420---- 428----
@@ -436,7 +444,8 @@ It is possible to change the foreground/background color of inlay hints. Just ad
436 444
437==== Semantic style customizations 445==== Semantic style customizations
438 446
439You can customize the look of different semantic elements in the source code. For example, mutable bindings are underlined by default and you can override this behavior by adding the following section to your `settings.json`: 447You can customize the look of different semantic elements in the source code.
448For example, mutable bindings are underlined by default and you can override this behavior by adding the following section to your `settings.json`:
440 449
441[source,jsonc] 450[source,jsonc]
442---- 451----
@@ -452,7 +461,9 @@ You can customize the look of different semantic elements in the source code. Fo
452---- 461----
453 462
454==== Special `when` clause context for keybindings. 463==== Special `when` clause context for keybindings.
455You may use `inRustProject` context to configure keybindings for rust projects only. For example: 464You may use `inRustProject` context to configure keybindings for rust projects only.
465For example:
466
456[source,json] 467[source,json]
457---- 468----
458{ 469{
@@ -491,7 +502,8 @@ Or it is possible to specify vars more granularly:
491] 502]
492``` 503```
493 504
494You can use any valid RegExp as a mask. Also note that a full runnable name is something like *run bin_or_example_name*, *test some::mod::test_name* or *test-mod some::mod*, so it is possible to distinguish binaries, single tests, and test modules with this masks: `"^run"`, `"^test "` (the trailing space matters!), and `"^test-mod"` respectively. 505You can use any valid regular expression as a mask.
506Also note that a full runnable name is something like *run bin_or_example_name*, *test some::mod::test_name* or *test-mod some::mod*, so it is possible to distinguish binaries, single tests, and test modules with this masks: `"^run"`, `"^test "` (the trailing space matters!), and `"^test-mod"` respectively.
495 507
496==== Compiler feedback from external commands 508==== Compiler feedback from external commands
497 509