Skip to content

Commit dcf4e3a

Browse files
alanzmeta-codesync[bot]
authored andcommitted
add file_id to diagnostic RelatedInformation
Summary: A given diagnostic can have related information in it, which carries relevant context to the provided diagnostic. For example, for a head mismatch diagnostic, it reports the specific head location(s) that do not match. The LSP spec uses a `Location` for these, which includes a file URL, meaning the related info can be in a different file from where the diagnostic is reported. In ELP, we currently do not provide a way of populating this information, so can only refer to related information within the same file. Thic can result in erroneous outcomes. ## This diff We include a `FileId` in our internal `RelatedInformation` structure, and map it to a URL when serializing for transmission to the LSP client Reviewed By: robertoaloi Differential Revision: D87316542 fbshipit-source-id: 5f94eb4fe5bb73e7bf1fff7cad458231c51bd6f0
1 parent 8394c84 commit dcf4e3a

File tree

8 files changed

+63
-39
lines changed

8 files changed

+63
-39
lines changed

crates/elp/src/bin/elp_parse_cli.rs

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ use elp::cli::Cli;
2424
use elp::convert;
2525
use elp::memory_usage::MemoryUsage;
2626
use elp::otp_file_to_ignore;
27-
use elp::server::file_id_to_url;
2827
use elp_eqwalizer::Mode;
2928
use elp_ide::Analysis;
3029
use elp_ide::diagnostics;
@@ -132,8 +131,7 @@ pub fn parse_all(
132131
(None, _, true) => do_parse_all_seq(cli, &loaded, &cfg, &args.to)?,
133132
(None, _, false) => do_parse_all_par(cli, &loaded, &cfg, &args.to)?,
134133
(Some(file_id), Some(name), _) => {
135-
do_parse_one(&analysis, &loaded.vfs, &cfg, &args.to, file_id, &name)?
136-
.map_or(vec![], |x| vec![x])
134+
do_parse_one(&analysis, &cfg, &args.to, file_id, &name)?.map_or(vec![], |x| vec![x])
137135
}
138136
(Some(file_id), _, _) => panic!("Could not get name from file_id for {file_id:?}"),
139137
};
@@ -144,10 +142,6 @@ pub fn parse_all(
144142

145143
let db = loaded.analysis_host.raw_database();
146144

147-
// We need a `Url` for converting to the lsp_types::Diagnostic for
148-
// printing, but do not print it out. So just create a dummy value
149-
let url = lsp_types::Url::parse("file:///unused_url").ok().unwrap();
150-
151145
telemetry::report_elapsed_time("parse-elp operational", start_time);
152146

153147
let memory_end = MemoryUsage::now();
@@ -197,7 +191,7 @@ pub fn parse_all(
197191
cli,
198192
)?;
199193
} else {
200-
print_diagnostic(&diag, &line_index, &url, &mut err_in_diag, cli)?;
194+
print_diagnostic(&diag, &line_index, &mut err_in_diag, cli)?;
201195
}
202196
}
203197
}
@@ -242,11 +236,10 @@ fn print_diagnostic_json(
242236
fn print_diagnostic(
243237
diag: &diagnostics::Diagnostic,
244238
line_index: &LineIndex,
245-
url: &lsp_types::Url,
246239
err_in_diag: &mut bool,
247240
cli: &mut dyn Cli,
248241
) -> Result<(), anyhow::Error> {
249-
let diag = convert::ide_to_lsp_diagnostic(line_index, url, diag);
242+
let diag = convert::ide_to_lsp_diagnostic(line_index, diag, |_file_id| None);
250243
let severity = match diag.severity {
251244
None => DiagnosticSeverity::ERROR,
252245
Some(sev) => {
@@ -289,7 +282,6 @@ fn do_parse_all_par(
289282

290283
let pb = cli.progress(module_iter.len() as u64, "Parsing modules");
291284

292-
let vfs = &loaded.vfs;
293285
Ok(module_iter
294286
.par_bridge()
295287
.progress_with(pb)
@@ -300,7 +292,7 @@ fn do_parse_all_par(
300292
&& file_source == FileSource::Src
301293
&& db.file_app_type(file_id).ok() != Some(Some(AppType::Dep))
302294
{
303-
do_parse_one(db, vfs, config, to, file_id, module_name.as_str()).unwrap()
295+
do_parse_one(db, config, to, file_id, module_name.as_str()).unwrap()
304296
} else {
305297
None
306298
}
@@ -321,7 +313,6 @@ fn do_parse_all_seq(
321313

322314
let pb = cli.progress(module_iter.len() as u64, "Parsing modules (sequential)");
323315

324-
let vfs = &loaded.vfs;
325316
let db = loaded.analysis();
326317
Ok(module_iter
327318
.progress_with(pb)
@@ -330,7 +321,7 @@ fn do_parse_all_seq(
330321
&& file_source == FileSource::Src
331322
&& db.file_app_type(file_id).ok() != Some(Some(AppType::Dep))
332323
{
333-
do_parse_one(&db, vfs, config, to, file_id, module_name.as_str()).unwrap()
324+
do_parse_one(&db, config, to, file_id, module_name.as_str()).unwrap()
334325
} else {
335326
None
336327
}
@@ -340,13 +331,11 @@ fn do_parse_all_seq(
340331

341332
fn do_parse_one(
342333
db: &Analysis,
343-
vfs: &Vfs,
344334
config: &DiagnosticsConfig,
345335
to: &Option<PathBuf>,
346336
file_id: FileId,
347337
name: &str,
348338
) -> Result<Option<ParseResult>> {
349-
let url = file_id_to_url(vfs, file_id);
350339
let native = db.native_diagnostics(config, &vec![], file_id)?;
351340
let erlang_service_diagnostics =
352341
db.erlang_service_diagnostics(file_id, config, RemoveElpReported::Yes)?;
@@ -364,11 +353,13 @@ fn do_parse_one(
364353
let mut output = File::create(to_path)?;
365354

366355
for diagnostic in native.iter() {
367-
let diagnostic = convert::ide_to_lsp_diagnostic(&line_index, &url, diagnostic);
356+
let diagnostic =
357+
convert::ide_to_lsp_diagnostic(&line_index, diagnostic, |_file_id| None);
368358
writeln!(output, "{diagnostic:?}")?;
369359
}
370360
for diagnostic in erlang_service.iter() {
371-
let diagnostic = convert::ide_to_lsp_diagnostic(&line_index, &url, diagnostic);
361+
let diagnostic =
362+
convert::ide_to_lsp_diagnostic(&line_index, diagnostic, |_file_id| None);
372363
writeln!(output, "{diagnostic:?}")?;
373364
}
374365
}

crates/elp/src/convert.rs

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use elp_ide::elp_ide_db::assists::AssistContextDiagnostic;
2626
use elp_ide::elp_ide_db::assists::AssistContextDiagnosticCode;
2727
use elp_ide::elp_ide_db::elp_base_db::AbsPath;
2828
use elp_ide::elp_ide_db::elp_base_db::AbsPathBuf;
29+
use elp_ide::elp_ide_db::elp_base_db::FileId;
2930
use elp_ide::elp_ide_db::elp_base_db::VfsPath;
3031
use lsp_types::DiagnosticRelatedInformation;
3132
use lsp_types::Location;
@@ -67,11 +68,14 @@ pub fn diagnostic_severity(severity: Severity) -> lsp_types::DiagnosticSeverity
6768
}
6869
}
6970

70-
pub fn ide_to_lsp_diagnostic(
71+
pub fn ide_to_lsp_diagnostic<F>(
7172
line_index: &LineIndex,
72-
url: &Url,
7373
d: &Diagnostic,
74-
) -> lsp_types::Diagnostic {
74+
get_file_info: F,
75+
) -> lsp_types::Diagnostic
76+
where
77+
F: Fn(FileId) -> Option<(LineIndex, Url)>,
78+
{
7579
let code_description = match &d.code_doc_uri {
7680
Some(uri) => match lsp_types::Url::parse(uri) {
7781
Ok(href) => Some(lsp_types::CodeDescription { href }),
@@ -90,7 +94,7 @@ pub fn ide_to_lsp_diagnostic(
9094
code_description,
9195
source,
9296
message: d.message.clone(),
93-
related_information: from_related(line_index, url, &d.related_info),
97+
related_information: from_related(get_file_info, &d.related_info),
9498
tags: d.tag.as_ref().map(lsp_diagnostic_tags),
9599
data: None,
96100
}
@@ -165,22 +169,26 @@ pub fn eqwalizer_to_arc_diagnostic(
165169
)
166170
}
167171

168-
fn from_related(
169-
line_index: &LineIndex,
170-
url: &Url,
172+
fn from_related<F>(
173+
get_file_info: F,
171174
r: &Option<Vec<RelatedInformation>>,
172-
) -> Option<Vec<DiagnosticRelatedInformation>> {
175+
) -> Option<Vec<DiagnosticRelatedInformation>>
176+
where
177+
F: Fn(elp_ide::elp_ide_db::elp_base_db::FileId) -> Option<(LineIndex, Url)>,
178+
{
173179
r.as_ref().map(|ri| {
174180
ri.iter()
175-
.map(|i| {
181+
.filter_map(|i| {
182+
// Get the line index and URL for the file that contains the related information
183+
let (line_index, uri) = get_file_info(i.file_id)?;
176184
let location = Location {
177-
range: range(line_index, i.range),
178-
uri: url.clone(),
185+
range: range(&line_index, i.range),
186+
uri,
179187
};
180-
DiagnosticRelatedInformation {
188+
Some(DiagnosticRelatedInformation {
181189
location,
182190
message: i.message.clone(),
183-
}
191+
})
184192
})
185193
.collect()
186194
})

crates/elp/src/server.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -624,7 +624,16 @@ impl Server {
624624
.diagnostics
625625
.diagnostics_for(*file_id)
626626
.iter()
627-
.map(|d| ide_to_lsp_diagnostic(&line_index, &url, d))
627+
.map(|d| {
628+
let vfs = self.vfs.clone();
629+
ide_to_lsp_diagnostic(&line_index, d, |related_file_id| {
630+
// Get the line index and URL for the related file
631+
let url = file_id_to_url(&vfs.read(), related_file_id);
632+
let snapshot = self.snapshot();
633+
let line_index = snapshot.analysis.line_index(related_file_id).ok()?;
634+
Some(((*line_index).clone(), url))
635+
})
636+
})
628637
.collect();
629638

630639
self.send_notification::<notification::PublishDiagnostics>(
@@ -828,11 +837,20 @@ impl Server {
828837
Arc::make_mut(&mut this.diagnostics)
829838
.clear(file_id);
830839
if let Ok(line_index) = analysis.line_index(file_id) {
840+
let vfs = this.vfs.clone();
831841
diagnostics = this
832842
.diagnostics
833843
.diagnostics_for(file_id)
834844
.iter()
835-
.map(|d| ide_to_lsp_diagnostic(&line_index, &url, d))
845+
.map(|d| {
846+
let snapshot = this.snapshot();
847+
ide_to_lsp_diagnostic(&line_index, d, |related_file_id| {
848+
// Get the line index and URL for the related file
849+
let url = file_id_to_url(&vfs.read(), related_file_id);
850+
let line_index = snapshot.analysis.line_index(related_file_id).ok()?;
851+
Some(((*line_index).clone(), url))
852+
})
853+
})
836854
.collect()
837855
}
838856
}

crates/ide/src/diagnostics.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,9 @@ impl Diagnostic {
227227
self
228228
}
229229

230-
pub(crate) fn as_related(&self) -> RelatedInformation {
230+
pub(crate) fn as_related(&self, file_id: FileId) -> RelatedInformation {
231231
RelatedInformation {
232+
file_id,
232233
range: self.range,
233234
message: self.message.clone(),
234235
}
@@ -447,6 +448,7 @@ impl Diagnostic {
447448

448449
#[derive(Debug, Clone)]
449450
pub struct RelatedInformation {
451+
pub file_id: FileId,
450452
pub range: TextRange,
451453
pub message: String,
452454
}
@@ -2818,6 +2820,7 @@ fn combine_syntax_errors(native: &Labeled, erlang_service: &Labeled) -> Labeled
28182820
/// Combine the ELP and erlang_service diagnostics. In particular,
28192821
/// flatten any cascading diagnostics if possible.
28202822
pub fn attach_related_diagnostics(
2823+
file_id: FileId,
28212824
native: LabeledDiagnostics,
28222825
erlang_service: LabeledDiagnostics,
28232826
) -> Vec<Diagnostic> {
@@ -2853,7 +2856,7 @@ pub fn attach_related_diagnostics(
28532856
.flat_map(|(mfa_label, syntax_error_diags)| {
28542857
if let Some(related) = erlang_service.labeled_undefined_errors.get(mfa_label) {
28552858
undefineds_to_remove.insert(mfa_label);
2856-
let related_info = related.iter().map(|d| d.as_related()).collect_vec();
2859+
let related_info = related.iter().map(|d| d.as_related(file_id)).collect_vec();
28572860
syntax_error_diags
28582861
.iter()
28592862
.map(|d| d.clone().with_related(Some(related_info.clone())))

crates/ide/src/diagnostics/head_mismatch.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ impl Validate<String> for Name {
270270
attr_loc,
271271
)
272272
.with_related(Some(vec![RelatedInformation {
273+
file_id,
273274
range: ref_loc,
274275
message: "Mismatched clause name".to_string(),
275276
}]))
@@ -293,7 +294,7 @@ impl Validate<usize> for Arity {
293294

294295
fn make_diagnostic(
295296
self,
296-
_file_id: FileId,
297+
file_id: FileId,
297298
attr: &usize,
298299
hattr: &usize,
299300
attr_loc: TextRange,
@@ -305,6 +306,7 @@ impl Validate<usize> for Arity {
305306
attr_loc,
306307
)
307308
.with_related(Some(vec![RelatedInformation {
309+
file_id,
308310
range: ref_loc,
309311
message: "Mismatched clause".to_string(),
310312
}]))

crates/ide/src/diagnostics/misspelled_attribute.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ fn make_diagnostic(
133133
attr_name_range,
134134
)
135135
.with_related(Some(vec![RelatedInformation {
136+
file_id,
136137
range: attr_name_range,
137138
message: "Misspelled attribute".to_string(),
138139
}]))

crates/ide/src/diagnostics_collection.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ impl DiagnosticCollection {
104104
let native = self.native.get(&file_id).unwrap_or(&empty_diags);
105105
let erlang_service = self.erlang_service.get(&file_id).unwrap_or(&empty_diags);
106106
let mut combined: Vec<Diagnostic> =
107-
attach_related_diagnostics(native.clone(), erlang_service.clone());
107+
attach_related_diagnostics(file_id, native.clone(), erlang_service.clone());
108108
let eqwalizer = self.eqwalizer.get(&file_id).into_iter().flatten().cloned();
109109
let eqwalizer_project = self
110110
.eqwalizer_project
@@ -348,7 +348,7 @@ mod tests {
348348
let file_id = *file_id;
349349
let diagnostics = diagnostics::native_diagnostics(&db, &config, &vec![], file_id);
350350

351-
let combined = attach_related_diagnostics(diagnostics, extra_diags.clone());
351+
let combined = attach_related_diagnostics(file_id, diagnostics, extra_diags.clone());
352352
let expected = fixture.annotations_by_file_id(&file_id);
353353
let mut actual = combined
354354
.into_iter()

crates/ide/src/tests.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,8 @@ pub(crate) fn check_diagnostics_with_config_and_extra(
513513
for file_id in &fixture.files {
514514
let file_id = *file_id;
515515
let diagnostics = diagnostics::native_diagnostics(&analysis.db, &config, &vec![], file_id);
516-
let diagnostics = diagnostics::attach_related_diagnostics(diagnostics, extra_diags.clone());
516+
let diagnostics =
517+
diagnostics::attach_related_diagnostics(file_id, diagnostics, extra_diags.clone());
517518

518519
let expected = fixture.annotations_by_file_id(&file_id);
519520
let actual = convert_diagnostics_to_annotations(diagnostics);

0 commit comments

Comments
 (0)