Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allowed to get the diagnostics mappings in the LS. #6276

Merged
merged 1 commit into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions crates/cairo-lang-language-server/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ pub struct Config {
///
/// The property is set by the user under the `cairo1.corelibPath` key in client configuration.
pub unmanaged_core_path: Option<PathBuf>,
/// Whether to include the trace of the generation location of diagnostic location mapped by
/// macros.
///
/// The property is set by the user under the `cairo1.traceMacroDiagnostics` key in client
/// configuration.
pub trace_macro_diagnostics: bool,
}

impl Config {
Expand All @@ -42,10 +48,13 @@ impl Config {
return;
}

let items = vec![ConfigurationItem {
scope_uri: None,
section: Some("cairo1.corelibPath".to_owned()),
}];
let items = vec![
ConfigurationItem { scope_uri: None, section: Some("cairo1.corelibPath".to_owned()) },
ConfigurationItem {
scope_uri: None,
section: Some("cairo1.traceMacroDiagnostics".to_owned()),
},
];
let expected_len = items.len();
if let Ok(response) = client
.configuration(items)
Expand All @@ -71,6 +80,8 @@ impl Config {
.and_then(Value::as_str)
.filter(|s| !s.is_empty())
.map(Into::into);
self.trace_macro_diagnostics =
response.pop_front().as_ref().and_then(Value::as_bool).unwrap_or_default();

debug!("reloaded configuration: {self:#?}");
}
Expand Down
56 changes: 45 additions & 11 deletions crates/cairo-lang-language-server/src/lang/diagnostics/lsp.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use cairo_lang_diagnostics::{DiagnosticEntry, DiagnosticLocation, Diagnostics, Severity};
use cairo_lang_filesystem::db::FilesGroup;
use cairo_lang_filesystem::ids::FileId;
use cairo_lang_utils::Upcast;
use tower_lsp::lsp_types::{
Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, Location, NumberOrString, Range,
Expand All @@ -14,35 +15,46 @@ pub fn map_cairo_diagnostics_to_lsp<T: DiagnosticEntry>(
db: &T::DbType,
diags: &mut Vec<Diagnostic>,
diagnostics: &Diagnostics<T>,
trace_macro_diagnostics: bool,
) {
for diagnostic in diagnostics.get_diagnostics_without_duplicates(db) {
for diagnostic in if trace_macro_diagnostics {
diagnostics.get_all()
} else {
diagnostics.get_diagnostics_without_duplicates(db)
} {
let mut message = diagnostic.format(db);
let mut related_information = vec![];
for note in diagnostic.notes(db) {
if let Some(location) = &note.location {
let Some(range) = get_range(db.upcast(), location) else {
let Some((range, file_id)) = get_mapped_range_and_add_mapping_note(
db,
location,
trace_macro_diagnostics.then_some(&mut related_information),
"Next note mapped from here.",
) else {
continue;
};
related_information.push(DiagnosticRelatedInformation {
location: Location { uri: db.url_for_file(location.file_id), range },
location: Location { uri: db.url_for_file(file_id), range },
message: note.text.clone(),
});
} else {
message += &format!("\nnote: {}", note.text);
}
}

let Some(range) = get_range(db.upcast(), &diagnostic.location(db)) else {
let Some((range, _)) = get_mapped_range_and_add_mapping_note(
db,
&diagnostic.location(db),
trace_macro_diagnostics.then_some(&mut related_information),
"Diagnostic mapped from here.",
) else {
continue;
};
diags.push(Diagnostic {
range,
message,
related_information: if related_information.is_empty() {
None
} else {
Some(related_information)
},
related_information: (!related_information.is_empty()).then_some(related_information),
severity: Some(match diagnostic.severity() {
Severity::Error => DiagnosticSeverity::ERROR,
Severity::Warning => DiagnosticSeverity::WARNING,
Expand All @@ -53,9 +65,31 @@ pub fn map_cairo_diagnostics_to_lsp<T: DiagnosticEntry>(
}
}

/// Returns the mapped range of a location, optionally adds a note about the mapping of the
/// location.
fn get_mapped_range_and_add_mapping_note(
db: &(impl Upcast<dyn FilesGroup> + ?Sized),
orig: &DiagnosticLocation,
related_info: Option<&mut Vec<DiagnosticRelatedInformation>>,
message: &str,
) -> Option<(Range, FileId)> {
let mapped = orig.user_location(db.upcast());
let mapped_range = get_lsp_range(db.upcast(), &mapped)?;
if let Some(related_info) = related_info {
if *orig != mapped {
if let Some(range) = get_lsp_range(db.upcast(), orig) {
related_info.push(DiagnosticRelatedInformation {
location: Location { uri: db.url_for_file(orig.file_id), range },
message: message.to_string(),
});
}
}
}
Some((mapped_range, mapped.file_id))
}

/// Converts an internal diagnostic location to an LSP range.
fn get_range(db: &dyn FilesGroup, location: &DiagnosticLocation) -> Option<Range> {
let location = location.user_location(db);
fn get_lsp_range(db: &dyn FilesGroup, location: &DiagnosticLocation) -> Option<Range> {
let Some(span) = location.span.position_in_file(db, location.file_id) else {
error!("failed to get range for diagnostic");
return None;
Expand Down
25 changes: 21 additions & 4 deletions crates/cairo-lang-language-server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -452,9 +452,25 @@ impl Backend {
drop(state);

let mut diags = Vec::new();
map_cairo_diagnostics_to_lsp((*db).upcast(), &mut diags, &new_file_diagnostics.parser);
map_cairo_diagnostics_to_lsp((*db).upcast(), &mut diags, &new_file_diagnostics.semantic);
map_cairo_diagnostics_to_lsp((*db).upcast(), &mut diags, &new_file_diagnostics.lowering);
let trace_macro_diagnostics = self.config.read().await.trace_macro_diagnostics;
map_cairo_diagnostics_to_lsp(
(*db).upcast(),
&mut diags,
&new_file_diagnostics.parser,
trace_macro_diagnostics,
);
map_cairo_diagnostics_to_lsp(
(*db).upcast(),
&mut diags,
&new_file_diagnostics.semantic,
trace_macro_diagnostics,
);
map_cairo_diagnostics_to_lsp(
(*db).upcast(),
&mut diags,
&new_file_diagnostics.lowering,
trace_macro_diagnostics,
);

// Drop database snapshot before we wait for the client responding to our notification.
drop(db);
Expand Down Expand Up @@ -654,11 +670,12 @@ impl Backend {

/// Reload the [`Config`] and all its dependencies.
async fn reload_config(&self) {
let mut config = self.config.write().await;
{
let mut config = self.config.write().await;
let client_capabilities = self.client_capabilities.read().await;
config.reload(&self.client, &client_capabilities).await;
}
self.refresh_diagnostics().await.ok();
}
}

Expand Down
5 changes: 5 additions & 0 deletions vscode-cairo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@
"description": "Absolute path to the Cairo core library, used in non-Scarb projects.",
"scope": "window"
},
"cairo1.traceMacroDiagnostics": {
"type": "boolean",
"description": "Attach additional information to diagnostics coming from macros, providing diagnostic source in macro generated code.",
"scope": "window"
},
"cairo1.languageServerExtraEnv": {
"type": [
"null",
Expand Down
1 change: 1 addition & 0 deletions vscode-cairo/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ interface ConfigProps {
preferScarbLanguageServer: boolean;
scarbPath: string;
corelibPath: string;
traceMacroDiagnostics: boolean;
languageServerExtraEnv: null | Record<string, string | number>;
}

Expand Down
Loading