diff options
-rw-r--r-- | .vscode/tasks.json | 55 | ||||
-rw-r--r-- | crates/ra_ide_api/src/hover.rs | 10 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/init.rs | 100 | ||||
-rw-r--r-- | crates/ra_parser/src/grammar/expressions/atom.rs | 5 | ||||
-rw-r--r-- | crates/ra_parser/src/grammar/items.rs | 11 | ||||
-rw-r--r-- | crates/ra_parser/src/syntax_kind/generated.rs | 4 | ||||
-rw-r--r-- | crates/ra_syntax/src/grammar.ron | 1 | ||||
-rw-r--r-- | crates/ra_syntax/tests/data/lexer/0011_keywords.rs | 2 | ||||
-rw-r--r-- | crates/ra_syntax/tests/data/lexer/0011_keywords.txt | 2 | ||||
-rw-r--r-- | crates/ra_syntax/tests/data/parser/inline/err/0007_async_without_semicolon.rs | 1 | ||||
-rw-r--r-- | crates/ra_syntax/tests/data/parser/inline/err/0007_async_without_semicolon.txt | 31 | ||||
-rw-r--r-- | crates/ra_syntax/tests/data/parser/inline/ok/0124_async_fn.rs | 1 | ||||
-rw-r--r-- | crates/ra_syntax/tests/data/parser/inline/ok/0124_async_fn.txt | 16 | ||||
-rw-r--r-- | editors/code/package.json | 4 |
14 files changed, 174 insertions, 69 deletions
diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 045cee326..063cbd174 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json | |||
@@ -10,7 +10,10 @@ | |||
10 | "problemMatcher": { | 10 | "problemMatcher": { |
11 | "owner": "typescript", | 11 | "owner": "typescript", |
12 | "pattern": "$tsc", | 12 | "pattern": "$tsc", |
13 | "fileLocation": ["relative", "${workspaceRoot}/editors/code"] | 13 | "fileLocation": [ |
14 | "relative", | ||
15 | "${workspaceRoot}/editors/code" | ||
16 | ] | ||
14 | }, | 17 | }, |
15 | "path": "editors/code/" | 18 | "path": "editors/code/" |
16 | }, | 19 | }, |
@@ -18,30 +21,40 @@ | |||
18 | "label": "Build Lsp", | 21 | "label": "Build Lsp", |
19 | "type": "shell", | 22 | "type": "shell", |
20 | "command": "cargo build", | 23 | "command": "cargo build", |
21 | "problemMatcher": { | 24 | "problemMatcher": "$rustc" |
22 | "owner": "rust", | ||
23 | "fileLocation": ["relative", "${workspaceRoot}"], | ||
24 | "pattern": [ | ||
25 | { | ||
26 | "regexp": "^(warning|warn|error)(?:\\[(.*?)\\])?: (.*)$", | ||
27 | "severity": 1, | ||
28 | "code": 2, | ||
29 | "message": 3 | ||
30 | }, | ||
31 | { | ||
32 | "regexp": "^[\\s->=]*(.*?):(\\d*):(\\d*)\\s*$", | ||
33 | "file": 1, | ||
34 | "line": 2, | ||
35 | "column": 3 | ||
36 | } | ||
37 | ] | ||
38 | } | ||
39 | }, | 25 | }, |
40 | { | 26 | { |
41 | "label": "Build All", | 27 | "label": "Build All", |
42 | "group": "build", | 28 | "group": "build", |
43 | "dependsOn": ["Build Extension", "Build Lsp"], | 29 | "dependsOn": [ |
30 | "Build Extension", | ||
31 | "Build Lsp" | ||
32 | ], | ||
44 | "problemMatcher": [] | 33 | "problemMatcher": [] |
34 | }, | ||
35 | { | ||
36 | "label": "cargo watch", | ||
37 | "group": "build", | ||
38 | "isBackground": true, | ||
39 | "type": "shell", | ||
40 | "command": "cargo", | ||
41 | "args": [ | ||
42 | "watch" | ||
43 | ], | ||
44 | "problemMatcher": "$rustc-watch" | ||
45 | }, | ||
46 | { | ||
47 | "label": "cargo watch tests", | ||
48 | "group": "build", | ||
49 | "isBackground": true, | ||
50 | "type": "shell", | ||
51 | "command": "cargo", | ||
52 | "args": [ | ||
53 | "watch", | ||
54 | "-x", | ||
55 | "check --tests" | ||
56 | ], | ||
57 | "problemMatcher": "$rustc-watch" | ||
45 | } | 58 | } |
46 | ] | 59 | ] |
47 | } | 60 | } \ No newline at end of file |
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs index bf08f5203..66a625c6c 100644 --- a/crates/ra_ide_api/src/hover.rs +++ b/crates/ra_ide_api/src/hover.rs | |||
@@ -550,7 +550,7 @@ The Some variant | |||
550 | fn test_hover_infer_associated_method_result() { | 550 | fn test_hover_infer_associated_method_result() { |
551 | let (analysis, position) = single_file_with_position( | 551 | let (analysis, position) = single_file_with_position( |
552 | " | 552 | " |
553 | struct Thing { x: u32 }; | 553 | struct Thing { x: u32 } |
554 | 554 | ||
555 | impl Thing { | 555 | impl Thing { |
556 | fn new() -> Thing { | 556 | fn new() -> Thing { |
@@ -616,7 +616,7 @@ The Some variant | |||
616 | fn test_hover_self() { | 616 | fn test_hover_self() { |
617 | let (analysis, position) = single_file_with_position( | 617 | let (analysis, position) = single_file_with_position( |
618 | " | 618 | " |
619 | struct Thing { x: u32 }; | 619 | struct Thing { x: u32 } |
620 | impl Thing { | 620 | impl Thing { |
621 | fn new() -> Self { | 621 | fn new() -> Self { |
622 | Self<|> { x: 0 } | 622 | Self<|> { x: 0 } |
@@ -630,7 +630,7 @@ The Some variant | |||
630 | 630 | ||
631 | let (analysis, position) = single_file_with_position( | 631 | let (analysis, position) = single_file_with_position( |
632 | " | 632 | " |
633 | struct Thing { x: u32 }; | 633 | struct Thing { x: u32 } |
634 | impl Thing { | 634 | impl Thing { |
635 | fn new() -> Self<|> { | 635 | fn new() -> Self<|> { |
636 | Self { x: 0 } | 636 | Self { x: 0 } |
@@ -644,7 +644,7 @@ The Some variant | |||
644 | 644 | ||
645 | let (analysis, position) = single_file_with_position( | 645 | let (analysis, position) = single_file_with_position( |
646 | " | 646 | " |
647 | enum Thing { A }; | 647 | enum Thing { A } |
648 | impl Thing { | 648 | impl Thing { |
649 | pub fn new() -> Self<|> { | 649 | pub fn new() -> Self<|> { |
650 | Thing::A | 650 | Thing::A |
@@ -658,7 +658,7 @@ The Some variant | |||
658 | 658 | ||
659 | let (analysis, position) = single_file_with_position( | 659 | let (analysis, position) = single_file_with_position( |
660 | " | 660 | " |
661 | enum Thing { A }; | 661 | enum Thing { A } |
662 | impl Thing { | 662 | impl Thing { |
663 | pub fn thing(a: Self<|>) { | 663 | pub fn thing(a: Self<|>) { |
664 | } | 664 | } |
diff --git a/crates/ra_lsp_server/src/init.rs b/crates/ra_lsp_server/src/init.rs index 0b7a47a0b..1b77e0312 100644 --- a/crates/ra_lsp_server/src/init.rs +++ b/crates/ra_lsp_server/src/init.rs | |||
@@ -1,39 +1,61 @@ | |||
1 | use serde::{Deserialize, Deserializer}; | 1 | use serde::{Deserialize, Deserializer}; |
2 | 2 | ||
3 | /// Client provided initialization options | 3 | /// Client provided initialization options |
4 | #[derive(Deserialize, Clone, Copy, Debug)] | 4 | #[derive(Deserialize, Clone, Copy, Debug, PartialEq, Eq)] |
5 | #[serde(rename_all = "camelCase")] | 5 | #[serde(rename_all = "camelCase", default)] |
6 | pub struct InitializationOptions { | 6 | pub struct InitializationOptions { |
7 | /// Whether the client supports our custom highlighting publishing decorations. | 7 | /// Whether the client supports our custom highlighting publishing decorations. |
8 | /// This is different to the highlightingOn setting, which is whether the user | 8 | /// This is different to the highlightingOn setting, which is whether the user |
9 | /// wants our custom highlighting to be used. | 9 | /// wants our custom highlighting to be used. |
10 | /// | 10 | /// |
11 | /// Defaults to `true` | 11 | /// Defaults to `false` |
12 | #[serde(default = "bool_true", deserialize_with = "nullable_bool_true")] | 12 | #[serde(deserialize_with = "nullable_bool_false")] |
13 | pub publish_decorations: bool, | 13 | pub publish_decorations: bool, |
14 | 14 | ||
15 | /// Whether or not the workspace loaded notification should be sent | 15 | /// Whether or not the workspace loaded notification should be sent |
16 | /// | 16 | /// |
17 | /// Defaults to `true` | 17 | /// Defaults to `true` |
18 | #[serde(default = "bool_true", deserialize_with = "nullable_bool_true")] | 18 | #[serde(deserialize_with = "nullable_bool_true")] |
19 | pub show_workspace_loaded: bool, | 19 | pub show_workspace_loaded: bool, |
20 | } | 20 | } |
21 | 21 | ||
22 | impl Default for InitializationOptions { | 22 | impl Default for InitializationOptions { |
23 | fn default() -> InitializationOptions { | 23 | fn default() -> InitializationOptions { |
24 | InitializationOptions { publish_decorations: true, show_workspace_loaded: true } | 24 | InitializationOptions { publish_decorations: false, show_workspace_loaded: true } |
25 | } | 25 | } |
26 | } | 26 | } |
27 | 27 | ||
28 | fn bool_true() -> bool { | 28 | /// Deserializes a null value to a bool false by default |
29 | true | 29 | fn nullable_bool_false<'de, D>(deserializer: D) -> Result<bool, D::Error> |
30 | } | 30 | where |
31 | 31 | D: Deserializer<'de>, | |
32 | /// Deserializes a null value to a bool true by default | 32 | { |
33 | fn nullable_bool_true<'de, D>(deserializer: D) -> Result<bool, D::Error> | 33 | let opt = Option::deserialize(deserializer)?; |
34 | where | 34 | Ok(opt.unwrap_or(false)) |
35 | D: Deserializer<'de>, | 35 | } |
36 | { | 36 | |
37 | let opt = Option::deserialize(deserializer)?; | 37 | /// Deserializes a null value to a bool true by default |
38 | Ok(opt.unwrap_or(true)) | 38 | fn nullable_bool_true<'de, D>(deserializer: D) -> Result<bool, D::Error> |
39 | } | 39 | where |
40 | D: Deserializer<'de>, | ||
41 | { | ||
42 | let opt = Option::deserialize(deserializer)?; | ||
43 | Ok(opt.unwrap_or(true)) | ||
44 | } | ||
45 | |||
46 | #[cfg(test)] | ||
47 | mod test { | ||
48 | use super::*; | ||
49 | |||
50 | #[test] | ||
51 | fn deserialize_init_options_defaults() { | ||
52 | // check that null == default for both fields | ||
53 | let default = InitializationOptions::default(); | ||
54 | assert_eq!(default, serde_json::from_str(r#"{}"#).unwrap()); | ||
55 | assert_eq!( | ||
56 | default, | ||
57 | serde_json::from_str(r#"{"publishDecorations":null, "showWorkspaceLoaded":null}"#) | ||
58 | .unwrap() | ||
59 | ); | ||
60 | } | ||
61 | } | ||
diff --git a/crates/ra_parser/src/grammar/expressions/atom.rs b/crates/ra_parser/src/grammar/expressions/atom.rs index 9f282c74d..53bb26c5f 100644 --- a/crates/ra_parser/src/grammar/expressions/atom.rs +++ b/crates/ra_parser/src/grammar/expressions/atom.rs | |||
@@ -93,6 +93,11 @@ pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<(CompletedMar | |||
93 | } | 93 | } |
94 | } | 94 | } |
95 | 95 | ||
96 | ASYNC_KW if la == L_CURLY => { | ||
97 | let m = p.start(); | ||
98 | p.bump(); | ||
99 | block_expr(p, Some(m)) | ||
100 | } | ||
96 | MATCH_KW => match_expr(p), | 101 | MATCH_KW => match_expr(p), |
97 | UNSAFE_KW if la == L_CURLY => { | 102 | UNSAFE_KW if la == L_CURLY => { |
98 | let m = p.start(); | 103 | let m = p.start(); |
diff --git a/crates/ra_parser/src/grammar/items.rs b/crates/ra_parser/src/grammar/items.rs index ab9d2de90..a057c8167 100644 --- a/crates/ra_parser/src/grammar/items.rs +++ b/crates/ra_parser/src/grammar/items.rs | |||
@@ -86,9 +86,15 @@ pub(super) fn maybe_item(p: &mut Parser, flavor: ItemFlavor) -> MaybeItem { | |||
86 | } | 86 | } |
87 | 87 | ||
88 | let mut has_mods = false; | 88 | let mut has_mods = false; |
89 | |||
89 | // modifiers | 90 | // modifiers |
91 | // test_err async_without_semicolon | ||
92 | // fn foo() { let _ = async {} } | ||
90 | has_mods |= p.eat(CONST_KW); | 93 | has_mods |= p.eat(CONST_KW); |
91 | 94 | if p.at(ASYNC_KW) && p.nth(1) != L_CURLY { | |
95 | p.eat(ASYNC_KW); | ||
96 | has_mods = true; | ||
97 | } | ||
92 | // test_err unsafe_block_in_mod | 98 | // test_err unsafe_block_in_mod |
93 | // fn foo(){} unsafe { } fn bar(){} | 99 | // fn foo(){} unsafe { } fn bar(){} |
94 | if p.at(UNSAFE_KW) && p.nth(1) != L_CURLY { | 100 | if p.at(UNSAFE_KW) && p.nth(1) != L_CURLY { |
@@ -110,6 +116,9 @@ pub(super) fn maybe_item(p: &mut Parser, flavor: ItemFlavor) -> MaybeItem { | |||
110 | 116 | ||
111 | // items | 117 | // items |
112 | let kind = match p.current() { | 118 | let kind = match p.current() { |
119 | // test async_fn | ||
120 | // async fn foo() {} | ||
121 | |||
113 | // test extern_fn | 122 | // test extern_fn |
114 | // extern fn foo() {} | 123 | // extern fn foo() {} |
115 | 124 | ||
diff --git a/crates/ra_parser/src/syntax_kind/generated.rs b/crates/ra_parser/src/syntax_kind/generated.rs index 0eed44ecf..03247ae38 100644 --- a/crates/ra_parser/src/syntax_kind/generated.rs +++ b/crates/ra_parser/src/syntax_kind/generated.rs | |||
@@ -66,6 +66,7 @@ pub enum SyntaxKind { | |||
66 | SHR, | 66 | SHR, |
67 | SHLEQ, | 67 | SHLEQ, |
68 | SHREQ, | 68 | SHREQ, |
69 | ASYNC_KW, | ||
69 | USE_KW, | 70 | USE_KW, |
70 | FN_KW, | 71 | FN_KW, |
71 | STRUCT_KW, | 72 | STRUCT_KW, |
@@ -233,6 +234,7 @@ use self::SyntaxKind::*; | |||
233 | impl SyntaxKind { | 234 | impl SyntaxKind { |
234 | pub fn is_keyword(self) -> bool { | 235 | pub fn is_keyword(self) -> bool { |
235 | match self { | 236 | match self { |
237 | | ASYNC_KW | ||
236 | | USE_KW | 238 | | USE_KW |
237 | | FN_KW | 239 | | FN_KW |
238 | | STRUCT_KW | 240 | | STRUCT_KW |
@@ -403,6 +405,7 @@ impl SyntaxKind { | |||
403 | SHR => &SyntaxInfo { name: "SHR" }, | 405 | SHR => &SyntaxInfo { name: "SHR" }, |
404 | SHLEQ => &SyntaxInfo { name: "SHLEQ" }, | 406 | SHLEQ => &SyntaxInfo { name: "SHLEQ" }, |
405 | SHREQ => &SyntaxInfo { name: "SHREQ" }, | 407 | SHREQ => &SyntaxInfo { name: "SHREQ" }, |
408 | ASYNC_KW => &SyntaxInfo { name: "ASYNC_KW" }, | ||
406 | USE_KW => &SyntaxInfo { name: "USE_KW" }, | 409 | USE_KW => &SyntaxInfo { name: "USE_KW" }, |
407 | FN_KW => &SyntaxInfo { name: "FN_KW" }, | 410 | FN_KW => &SyntaxInfo { name: "FN_KW" }, |
408 | STRUCT_KW => &SyntaxInfo { name: "STRUCT_KW" }, | 411 | STRUCT_KW => &SyntaxInfo { name: "STRUCT_KW" }, |
@@ -570,6 +573,7 @@ impl SyntaxKind { | |||
570 | } | 573 | } |
571 | pub fn from_keyword(ident: &str) -> Option<SyntaxKind> { | 574 | pub fn from_keyword(ident: &str) -> Option<SyntaxKind> { |
572 | let kw = match ident { | 575 | let kw = match ident { |
576 | "async" => ASYNC_KW, | ||
573 | "use" => USE_KW, | 577 | "use" => USE_KW, |
574 | "fn" => FN_KW, | 578 | "fn" => FN_KW, |
575 | "struct" => STRUCT_KW, | 579 | "struct" => STRUCT_KW, |
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index b7a2d1c01..66f1339c1 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron | |||
@@ -59,6 +59,7 @@ Grammar( | |||
59 | [">>=", "SHREQ"], | 59 | [">>=", "SHREQ"], |
60 | ], | 60 | ], |
61 | keywords: [ | 61 | keywords: [ |
62 | "async", | ||
62 | "use", | 63 | "use", |
63 | "fn", | 64 | "fn", |
64 | "struct", | 65 | "struct", |
diff --git a/crates/ra_syntax/tests/data/lexer/0011_keywords.rs b/crates/ra_syntax/tests/data/lexer/0011_keywords.rs index e6bf64d4d..1e91bff4e 100644 --- a/crates/ra_syntax/tests/data/lexer/0011_keywords.rs +++ b/crates/ra_syntax/tests/data/lexer/0011_keywords.rs | |||
@@ -1,3 +1,3 @@ | |||
1 | fn use struct trait enum impl true false as extern crate | 1 | async fn use struct trait enum impl true false as extern crate |
2 | mod pub self super in where for loop while if match const | 2 | mod pub self super in where for loop while if match const |
3 | static mut type ref let else move return | 3 | static mut type ref let else move return |
diff --git a/crates/ra_syntax/tests/data/lexer/0011_keywords.txt b/crates/ra_syntax/tests/data/lexer/0011_keywords.txt index d6a1abe8a..22c00eefb 100644 --- a/crates/ra_syntax/tests/data/lexer/0011_keywords.txt +++ b/crates/ra_syntax/tests/data/lexer/0011_keywords.txt | |||
@@ -1,3 +1,5 @@ | |||
1 | ASYNC_KW 5 "async" | ||
2 | WHITESPACE 1 " " | ||
1 | FN_KW 2 "fn" | 3 | FN_KW 2 "fn" |
2 | WHITESPACE 1 " " | 4 | WHITESPACE 1 " " |
3 | USE_KW 3 "use" | 5 | USE_KW 3 "use" |
diff --git a/crates/ra_syntax/tests/data/parser/inline/err/0007_async_without_semicolon.rs b/crates/ra_syntax/tests/data/parser/inline/err/0007_async_without_semicolon.rs new file mode 100644 index 000000000..9a423248c --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/inline/err/0007_async_without_semicolon.rs | |||
@@ -0,0 +1 @@ | |||
fn foo() { let _ = async {} } | |||
diff --git a/crates/ra_syntax/tests/data/parser/inline/err/0007_async_without_semicolon.txt b/crates/ra_syntax/tests/data/parser/inline/err/0007_async_without_semicolon.txt new file mode 100644 index 000000000..bb9a2d029 --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/inline/err/0007_async_without_semicolon.txt | |||
@@ -0,0 +1,31 @@ | |||
1 | SOURCE_FILE@[0; 30) | ||
2 | FN_DEF@[0; 29) | ||
3 | FN_KW@[0; 2) | ||
4 | WHITESPACE@[2; 3) | ||
5 | NAME@[3; 6) | ||
6 | IDENT@[3; 6) "foo" | ||
7 | PARAM_LIST@[6; 8) | ||
8 | L_PAREN@[6; 7) | ||
9 | R_PAREN@[7; 8) | ||
10 | WHITESPACE@[8; 9) | ||
11 | BLOCK@[9; 29) | ||
12 | L_CURLY@[9; 10) | ||
13 | WHITESPACE@[10; 11) | ||
14 | LET_STMT@[11; 27) | ||
15 | LET_KW@[11; 14) | ||
16 | WHITESPACE@[14; 15) | ||
17 | PLACEHOLDER_PAT@[15; 16) | ||
18 | UNDERSCORE@[15; 16) | ||
19 | WHITESPACE@[16; 17) | ||
20 | EQ@[17; 18) | ||
21 | WHITESPACE@[18; 19) | ||
22 | BLOCK_EXPR@[19; 27) | ||
23 | ASYNC_KW@[19; 24) | ||
24 | WHITESPACE@[24; 25) | ||
25 | BLOCK@[25; 27) | ||
26 | L_CURLY@[25; 26) | ||
27 | R_CURLY@[26; 27) | ||
28 | err: `expected SEMI` | ||
29 | WHITESPACE@[27; 28) | ||
30 | R_CURLY@[28; 29) | ||
31 | WHITESPACE@[29; 30) | ||
diff --git a/crates/ra_syntax/tests/data/parser/inline/ok/0124_async_fn.rs b/crates/ra_syntax/tests/data/parser/inline/ok/0124_async_fn.rs new file mode 100644 index 000000000..f4adcb62b --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/inline/ok/0124_async_fn.rs | |||
@@ -0,0 +1 @@ | |||
async fn foo() {} | |||
diff --git a/crates/ra_syntax/tests/data/parser/inline/ok/0124_async_fn.txt b/crates/ra_syntax/tests/data/parser/inline/ok/0124_async_fn.txt new file mode 100644 index 000000000..d1a706ecc --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/inline/ok/0124_async_fn.txt | |||
@@ -0,0 +1,16 @@ | |||
1 | SOURCE_FILE@[0; 18) | ||
2 | FN_DEF@[0; 17) | ||
3 | ASYNC_KW@[0; 5) | ||
4 | WHITESPACE@[5; 6) | ||
5 | FN_KW@[6; 8) | ||
6 | WHITESPACE@[8; 9) | ||
7 | NAME@[9; 12) | ||
8 | IDENT@[9; 12) "foo" | ||
9 | PARAM_LIST@[12; 14) | ||
10 | L_PAREN@[12; 13) | ||
11 | R_PAREN@[13; 14) | ||
12 | WHITESPACE@[14; 15) | ||
13 | BLOCK@[15; 17) | ||
14 | L_CURLY@[15; 16) | ||
15 | R_CURLY@[16; 17) | ||
16 | WHITESPACE@[17; 18) | ||
diff --git a/editors/code/package.json b/editors/code/package.json index 47eaac878..0a1e84b4a 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -215,8 +215,8 @@ | |||
215 | "${workspaceRoot}" | 215 | "${workspaceRoot}" |
216 | ], | 216 | ], |
217 | "background": { | 217 | "background": { |
218 | "beginsPattern": "^\\[Running ", | 218 | "beginsPattern": "^\\[Running\\b", |
219 | "endsPattern": "^(\\[Finished running\\]|To learn more, run the command again with --verbose\\.)$" | 219 | "endsPattern": "^\\[Finished running\\b" |
220 | }, | 220 | }, |
221 | "pattern": "$rustc" | 221 | "pattern": "$rustc" |
222 | } | 222 | } |