Skip to content

Commit 4c85e53

Browse files
bors[bot]hdevalkeflodiebold
authored
Merge #3561 #3577
3561: feat: add debug code lens r=matklad a=hdevalke Refs #3539 3577: Protect against infinite macro expansion in def collector r=edwin0cheng a=flodiebold Something I noticed while trying to make macro expansion more resilient against errors. There was a test for this, but it wasn't actually working because the first recursive expansion failed. (The comma...) Even with this limit, that test (when fixed) still takes some time to pass because of the exponential growth of the expansions, so I disabled it and added a different one without growth. CC @edwin0cheng Co-authored-by: Hannes De Valkeneer <hannes@de-valkeneer.be> Co-authored-by: hdevalke <2261239+hdevalke@users.noreply.github.com> Co-authored-by: Florian Diebold <florian.diebold@freiheit.com>
3 parents 9019d79 + fa65591 + 89eb9e8 commit 4c85e53

File tree

12 files changed

+122
-45
lines changed

12 files changed

+122
-45
lines changed

crates/ra_hir_def/src/nameres/collector.rs

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ struct MacroDirective {
102102
module_id: LocalModuleId,
103103
ast_id: AstIdWithPath<ast::MacroCall>,
104104
legacy: Option<MacroCallId>,
105+
depth: usize,
105106
}
106107

107108
#[derive(Clone, Debug, Eq, PartialEq)]
@@ -134,6 +135,7 @@ where
134135
self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id };
135136
ModCollector {
136137
def_collector: &mut *self,
138+
macro_depth: 0,
137139
module_id,
138140
file_id: file_id.into(),
139141
raw_items: &raw_items,
@@ -516,7 +518,7 @@ where
516518
macros.retain(|directive| {
517519
if let Some(call_id) = directive.legacy {
518520
res = ReachedFixedPoint::No;
519-
resolved.push((directive.module_id, call_id));
521+
resolved.push((directive.module_id, call_id, directive.depth));
520522
return false;
521523
}
522524

@@ -530,7 +532,7 @@ where
530532
);
531533
resolved_res.resolved_def.take_macros()
532534
}) {
533-
resolved.push((directive.module_id, call_id));
535+
resolved.push((directive.module_id, call_id, directive.depth));
534536
res = ReachedFixedPoint::No;
535537
return false;
536538
}
@@ -541,7 +543,7 @@ where
541543
if let Some(call_id) =
542544
directive.ast_id.as_call_id(self.db, |path| self.resolve_attribute_macro(&path))
543545
{
544-
resolved.push((directive.module_id, call_id));
546+
resolved.push((directive.module_id, call_id, 0));
545547
res = ReachedFixedPoint::No;
546548
return false;
547549
}
@@ -552,8 +554,12 @@ where
552554
self.unexpanded_macros = macros;
553555
self.unexpanded_attribute_macros = attribute_macros;
554556

555-
for (module_id, macro_call_id) in resolved {
556-
self.collect_macro_expansion(module_id, macro_call_id);
557+
for (module_id, macro_call_id, depth) in resolved {
558+
if depth > 1024 {
559+
log::debug!("Max macro expansion depth reached");
560+
continue;
561+
}
562+
self.collect_macro_expansion(module_id, macro_call_id, depth);
557563
}
558564

559565
res
@@ -573,12 +579,18 @@ where
573579
None
574580
}
575581

576-
fn collect_macro_expansion(&mut self, module_id: LocalModuleId, macro_call_id: MacroCallId) {
582+
fn collect_macro_expansion(
583+
&mut self,
584+
module_id: LocalModuleId,
585+
macro_call_id: MacroCallId,
586+
depth: usize,
587+
) {
577588
let file_id: HirFileId = macro_call_id.as_file();
578589
let raw_items = self.db.raw_items(file_id);
579590
let mod_dir = self.mod_dirs[&module_id].clone();
580591
ModCollector {
581592
def_collector: &mut *self,
593+
macro_depth: depth,
582594
file_id,
583595
module_id,
584596
raw_items: &raw_items,
@@ -595,6 +607,7 @@ where
595607
/// Walks a single module, populating defs, imports and macros
596608
struct ModCollector<'a, D> {
597609
def_collector: D,
610+
macro_depth: usize,
598611
module_id: LocalModuleId,
599612
file_id: HirFileId,
600613
raw_items: &'a raw::RawItems,
@@ -684,6 +697,7 @@ where
684697

685698
ModCollector {
686699
def_collector: &mut *self.def_collector,
700+
macro_depth: self.macro_depth,
687701
module_id,
688702
file_id: self.file_id,
689703
raw_items: self.raw_items,
@@ -713,6 +727,7 @@ where
713727
let raw_items = self.def_collector.db.raw_items(file_id.into());
714728
ModCollector {
715729
def_collector: &mut *self.def_collector,
730+
macro_depth: self.macro_depth,
716731
module_id,
717732
file_id: file_id.into(),
718733
raw_items: &raw_items,
@@ -887,6 +902,7 @@ where
887902
module_id: self.module_id,
888903
ast_id,
889904
legacy: Some(macro_call_id),
905+
depth: self.macro_depth + 1,
890906
});
891907

892908
return;
@@ -902,6 +918,7 @@ where
902918
module_id: self.module_id,
903919
ast_id,
904920
legacy: None,
921+
depth: self.macro_depth + 1,
905922
});
906923
}
907924

@@ -971,13 +988,26 @@ mod tests {
971988
}
972989

973990
#[test]
974-
fn test_macro_expand_will_stop() {
991+
fn test_macro_expand_will_stop_1() {
992+
do_resolve(
993+
r#"
994+
macro_rules! foo {
995+
($($ty:ty)*) => { foo!($($ty)*); }
996+
}
997+
foo!(KABOOM);
998+
"#,
999+
);
1000+
}
1001+
1002+
#[ignore] // this test does succeed, but takes quite a while :/
1003+
#[test]
1004+
fn test_macro_expand_will_stop_2() {
9751005
do_resolve(
9761006
r#"
9771007
macro_rules! foo {
978-
($($ty:ty)*) => { foo!($($ty)*, $($ty)*); }
1008+
($($ty:ty)*) => { foo!($($ty)* $($ty)*); }
9791009
}
980-
foo!(KABOOM);
1010+
foo!(KABOOM);
9811011
"#,
9821012
);
9831013
}

crates/rust-analyzer/src/cargo_target_spec.rs

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,50 +19,48 @@ impl CargoTargetSpec {
1919
pub(crate) fn runnable_args(
2020
spec: Option<CargoTargetSpec>,
2121
kind: &RunnableKind,
22-
) -> Result<Vec<String>> {
23-
let mut res = Vec::new();
22+
) -> Result<(Vec<String>, Vec<String>)> {
23+
let mut args = Vec::new();
24+
let mut extra_args = Vec::new();
2425
match kind {
2526
RunnableKind::Test { test_id } => {
26-
res.push("test".to_string());
27+
args.push("test".to_string());
2728
if let Some(spec) = spec {
28-
spec.push_to(&mut res);
29+
spec.push_to(&mut args);
2930
}
30-
res.push("--".to_string());
31-
res.push(test_id.to_string());
31+
extra_args.push(test_id.to_string());
3232
if let TestId::Path(_) = test_id {
33-
res.push("--exact".to_string());
33+
extra_args.push("--exact".to_string());
3434
}
35-
res.push("--nocapture".to_string());
35+
extra_args.push("--nocapture".to_string());
3636
}
3737
RunnableKind::TestMod { path } => {
38-
res.push("test".to_string());
38+
args.push("test".to_string());
3939
if let Some(spec) = spec {
40-
spec.push_to(&mut res);
40+
spec.push_to(&mut args);
4141
}
42-
res.push("--".to_string());
43-
res.push(path.to_string());
44-
res.push("--nocapture".to_string());
42+
extra_args.push(path.to_string());
43+
extra_args.push("--nocapture".to_string());
4544
}
4645
RunnableKind::Bench { test_id } => {
47-
res.push("bench".to_string());
46+
args.push("bench".to_string());
4847
if let Some(spec) = spec {
49-
spec.push_to(&mut res);
48+
spec.push_to(&mut args);
5049
}
51-
res.push("--".to_string());
52-
res.push(test_id.to_string());
50+
extra_args.push(test_id.to_string());
5351
if let TestId::Path(_) = test_id {
54-
res.push("--exact".to_string());
52+
extra_args.push("--exact".to_string());
5553
}
56-
res.push("--nocapture".to_string());
54+
extra_args.push("--nocapture".to_string());
5755
}
5856
RunnableKind::Bin => {
59-
res.push("run".to_string());
57+
args.push("run".to_string());
6058
if let Some(spec) = spec {
61-
spec.push_to(&mut res);
59+
spec.push_to(&mut args);
6260
}
6361
}
6462
}
65-
Ok(res)
63+
Ok((args, extra_args))
6664
}
6765

6866
pub(crate) fn for_file(

crates/rust-analyzer/src/config.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ pub struct ServerConfig {
5555

5656
/// Cargo feature configurations.
5757
pub cargo_features: CargoFeatures,
58+
59+
/// Enabled if the vscode_lldb extension is available.
60+
pub vscode_lldb: bool,
5861
}
5962

6063
impl Default for ServerConfig {
@@ -76,6 +79,7 @@ impl Default for ServerConfig {
7679
additional_out_dirs: FxHashMap::default(),
7780
cargo_features: Default::default(),
7881
rustfmt_args: Vec::new(),
82+
vscode_lldb: false,
7983
}
8084
}
8185
}

crates/rust-analyzer/src/main_loop.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ pub fn main_loop(
189189
all_targets: config.cargo_watch_all_targets,
190190
},
191191
rustfmt_args: config.rustfmt_args,
192+
vscode_lldb: config.vscode_lldb,
192193
}
193194
};
194195

crates/rust-analyzer/src/main_loop/handlers.rs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,7 @@ pub fn handle_runnables(
381381
label,
382382
bin: "cargo".to_string(),
383383
args: check_args,
384+
extra_args: Vec::new(),
384385
env: FxHashMap::default(),
385386
cwd: workspace_root.map(|root| root.to_string_lossy().to_string()),
386387
});
@@ -794,18 +795,35 @@ pub fn handle_code_lens(
794795
RunnableKind::Bin => "Run",
795796
}
796797
.to_string();
797-
let r = to_lsp_runnable(&world, file_id, runnable)?;
798+
let mut r = to_lsp_runnable(&world, file_id, runnable)?;
798799
let lens = CodeLens {
799800
range: r.range,
800801
command: Some(Command {
801802
title,
802803
command: "rust-analyzer.runSingle".into(),
803-
arguments: Some(vec![to_value(r).unwrap()]),
804+
arguments: Some(vec![to_value(&r).unwrap()]),
804805
}),
805806
data: None,
806807
};
807-
808808
lenses.push(lens);
809+
810+
if world.options.vscode_lldb {
811+
if r.args[0] == "run" {
812+
r.args[0] = "build".into();
813+
} else {
814+
r.args.push("--no-run".into());
815+
}
816+
let debug_lens = CodeLens {
817+
range: r.range,
818+
command: Some(Command {
819+
title: "Debug".into(),
820+
command: "rust-analyzer.debugSingle".into(),
821+
arguments: Some(vec![to_value(r).unwrap()]),
822+
}),
823+
data: None,
824+
};
825+
lenses.push(debug_lens);
826+
}
809827
}
810828

811829
// Handle impls
@@ -952,7 +970,7 @@ fn to_lsp_runnable(
952970
runnable: Runnable,
953971
) -> Result<req::Runnable> {
954972
let spec = CargoTargetSpec::for_file(world, file_id)?;
955-
let args = CargoTargetSpec::runnable_args(spec, &runnable.kind)?;
973+
let (args, extra_args) = CargoTargetSpec::runnable_args(spec, &runnable.kind)?;
956974
let line_index = world.analysis().file_line_index(file_id)?;
957975
let label = match &runnable.kind {
958976
RunnableKind::Test { test_id } => format!("test {}", test_id),
@@ -965,6 +983,7 @@ fn to_lsp_runnable(
965983
label,
966984
bin: "cargo".to_string(),
967985
args,
986+
extra_args,
968987
env: {
969988
let mut m = FxHashMap::default();
970989
m.insert("RUST_BACKTRACE".to_string(), "short".to_string());
@@ -973,6 +992,7 @@ fn to_lsp_runnable(
973992
cwd: world.workspace_root_for(file_id).map(|root| root.to_string_lossy().to_string()),
974993
})
975994
}
995+
976996
fn highlight(world: &WorldSnapshot, file_id: FileId) -> Result<Vec<Decoration>> {
977997
let line_index = world.analysis().file_line_index(file_id)?;
978998
let res = world

crates/rust-analyzer/src/req.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ pub struct Runnable {
169169
pub label: String,
170170
pub bin: String,
171171
pub args: Vec<String>,
172+
pub extra_args: Vec<String>,
172173
pub env: FxHashMap<String, String>,
173174
pub cwd: Option<String>,
174175
}

crates/rust-analyzer/src/world.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ pub struct Options {
3838
pub inlay_hints: InlayHintsOptions,
3939
pub rustfmt_args: Vec<String>,
4040
pub cargo_watch: CheckOptions,
41+
pub vscode_lldb: bool,
4142
}
4243

4344
/// `WorldState` is the primary mutable state of the language server

crates/rust-analyzer/tests/heavy_tests/main.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ fn foo() {
7575
RunnablesParams { text_document: server.doc_id("lib.rs"), position: None },
7676
json!([
7777
{
78-
"args": [ "test", "--", "foo", "--nocapture" ],
78+
"args": [ "test" ],
79+
"extraArgs": [ "foo", "--nocapture" ],
7980
"bin": "cargo",
8081
"env": { "RUST_BACKTRACE": "short" },
8182
"cwd": null,
@@ -90,6 +91,7 @@ fn foo() {
9091
"check",
9192
"--all"
9293
],
94+
"extraArgs": [],
9395
"bin": "cargo",
9496
"env": {},
9597
"cwd": null,
@@ -141,13 +143,11 @@ fn main() {}
141143

142144
server.wait_until_workspace_is_loaded();
143145
server.request::<Runnables>(
144-
RunnablesParams {
145-
text_document: server.doc_id("foo/tests/spam.rs"),
146-
position: None,
147-
},
146+
RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None },
148147
json!([
149148
{
150-
"args": [ "test", "--package", "foo", "--test", "spam", "--", "test_eggs", "--exact", "--nocapture" ],
149+
"args": [ "test", "--package", "foo", "--test", "spam" ],
150+
"extraArgs": [ "test_eggs", "--exact", "--nocapture" ],
151151
"bin": "cargo",
152152
"env": { "RUST_BACKTRACE": "short" },
153153
"label": "test test_eggs",
@@ -165,6 +165,7 @@ fn main() {}
165165
"--test",
166166
"spam"
167167
],
168+
"extraArgs": [],
168169
"bin": "cargo",
169170
"env": {},
170171
"cwd": server.path().join("foo"),
@@ -180,7 +181,7 @@ fn main() {}
180181
}
181182
}
182183
}
183-
])
184+
]),
184185
);
185186
}
186187

editors/code/src/client.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export async function createClient(config: Config, serverPath: string): Promise<
4646
withSysroot: config.withSysroot,
4747
cargoFeatures: config.cargoFeatures,
4848
rustfmtArgs: config.rustfmtArgs,
49+
vscodeLldb: vscode.extensions.getExtension("vadimcn.vscode-lldb") != null,
4950
},
5051
traceOutputChannel,
5152
middleware: {

0 commit comments

Comments
 (0)