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

Fix LSP panic when importing JSON #1382

Merged
merged 5 commits into from
Jun 20, 2023
Merged

Fix LSP panic when importing JSON #1382

merged 5 commits into from
Jun 20, 2023

Conversation

yannham
Copy link
Member

@yannham yannham commented Jun 19, 2023

Closes #1378

Context

Nickel terms imported from a JSON file currently don't have any position set, which is making the LSP panic. Indeed, imported expressions are registered as an usage site, to enable go to definition on them (which jumps to the corresponding file). The LSP expects that parsed terms have all their positions set, and in particular the one of the root term (corresponding to the whole file).

This commits stores the original input format alongside cached terms, and makes this information available to the public API of cache. The LSP can thus use it and abort any usage analysis when the term comes from an external import.

Improvement

I tried to set the position of terms imported from YAML and JSON, which should enable goto definition on expressions imported from external formats as well. This commit is in fact included in this PR. This does make the original panic go away, but then completion (which works for the first level of nesting) on a value imported from e.g. the YAML file of the github settings of this repo causes a stack overflow on the second level (value.repository.[.]), which crashes the server with a stack overflow. By fixing this stack overflow, we might remove the special treatment of external formats, restoring goto definition and maybe even completion. Until then, it's probably better to have less features but make the LSP more resilient.

Nickel terms imported from a JSON file currently don't have any position
set, which was making the LSP panic. This commits stores the original
input format alongside cached terms, and makes this information
available to the public API. The LSP now uses it and abort any usage
analysis when the term comes from an external import.
This commit adds a test to the LSP test suite to check that importing an
external format doesn't make it crash as it used to.
@github-actions github-actions bot temporarily deployed to pull request June 19, 2023 17:21 Inactive
@github-actions github-actions bot temporarily deployed to pull request June 20, 2023 09:42 Inactive
@yannham yannham marked this pull request as ready for review June 20, 2023 09:42
Comment on lines 463 to 487

// If the import is an external format (such as JSON or YAML), no position will be
// set. We could in fact set at least the position of the root term, but since this
// isn't the case currently, an unwrap() below would fail. For now, we simply bail
// out. This just means we can't "go to definition" on an external import for the
// time being.
if lin
.cache
.input_format(file)
.map(|fmt| !matches!(fmt, InputFormat::Nickel))
.unwrap_or(false)
{
return;
}

// This is safe because the import file is resolved before we linearize the
// containing file, therefore the cache MUST have the term stored.
let term = lin.cache.get(*file).unwrap();
let position = final_term_pos(&term);

// This unwrap fails only when position is a `TermPos::None`, which only happens
// if the `RichTerm`, has been transformed or evaluated. None of these happen before
// linearization, so this is safe.
// unwrap(): this unwrap fails only when position is a `TermPos::None`, which only
// happens if the `RichTerm` has been transformed or evaluated. None of these
// happen before linearization. At this point, we also ensured that we are only
// considering a Nickel source (and not a term deserialized from e.g. JSON), and
// terms parsed from Nickel must have a position set. So
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not that it really matters, but in essence we're getting the term out of the linearization cache twice. First to check that the input format is Nickel. Then we throw away the entry and find it again, this time to just unwrap it. Maybe a let else construct could be better here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think cache.get returns a RichTerm, without the metadata. We probably don't want to change this API, which is used in several places. What I might do is to either add a method that returns the whole CachedTerm, but I'm not sure it's a useful interface. Or one that returns a term together with the input_format. On the other hand, it's just one additional hashmap get by import, which is probably totally negligible.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, it's probably negligible. Let's not hold up this PR with that 👍

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the end I added a method get_with_input_format, which looks reasonable.

Add an helper to `cache` to retrieve both the input format and a cached
term from the cache with only one hashmap access.

This commit also updates some related comments inside the code of the
LSP, which were not up-to-date anymore.
@yannham yannham enabled auto-merge June 20, 2023 13:19
@github-actions github-actions bot temporarily deployed to pull request June 20, 2023 13:23 Inactive
@yannham yannham added this pull request to the merge queue Jun 20, 2023
Merged via the queue into master with commit 9720405 Jun 20, 2023
@yannham yannham deleted the fix/lsp-crash branch June 20, 2023 14:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Importing json/yaml crashes nls
2 participants