Skip to content

Commit

Permalink
Don't crash in ModelWalker if name or type resolution fails
Browse files Browse the repository at this point in the history
The code action for adding the missing `@map` attribute for MongoDB
relies on the parser database being populated to be able to search for
the primary key of the model. However, the parser database constructor
returns early when there are validation errors during name and types
resolution, so trying to access model attributes later on leads to
runtime errors as those structures are not constructed.

This commit implements populating the model and enum attributes with
stub default values before returning. As no further validation will
happen, and the engines do not work with invalid schemas, this should
not lead to incorrect behaviour. However, the code in the language tools
should take into account that accessing the model or enum attributes in
invalid schemas, although won't lead to crashes anymore, will return
incomplete information.

Fixes: prisma/language-tools#1466
  • Loading branch information
aqrln committed Jul 27, 2023
1 parent 2a6ced8 commit ebb2e40
Show file tree
Hide file tree
Showing 9 changed files with 82 additions and 1 deletion.
2 changes: 1 addition & 1 deletion prisma-fmt/src/code_actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ pub(crate) fn available_actions(schema: String, params: CodeActionParams) -> Vec
}

if matches!(datasource, Some(ds) if ds.active_provider == "mongodb") {
mongodb::add_at_map_for_id(&mut actions, &params, validated_schema.db.source(), model)
mongodb::add_at_map_for_id(&mut actions, &params, validated_schema.db.source(), model);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
generator client {
provider = "prisma-client-js"
}

datasource db {
provider = "mongodb"
url = env("DATABASE_URL")
}

model Kattbjorn {
// As of now, the code action won't appear here until the other validation
// error is fixed. The reason is the implementation of this code action
// relies on the parser database to be fully populated and all attributes
// to be resolved, however when there are name and type resolution errors,
// the attributes are not currently resolved and only an empty stub for
// model attributes is constructed. This may change in the future, and this
// test expectation would need to be updated correspondingly then.
//
// Note that the validation error itself does not appear here too until the
// error on the next line is fixed (due to these being separate validation
// passes), so the behaviour is consistent.
id String @id
bar
}
1 change: 1 addition & 0 deletions prisma-fmt/tests/code_actions/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@ scenarios! {
multi_schema_one_model_one_enum
multi_schema_two_models
mongodb_at_map
mongodb_at_map_with_validation_errors
}
1 change: 1 addition & 0 deletions prisma-fmt/tests/regression_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mod regressions;
31 changes: 31 additions & 0 deletions prisma-fmt/tests/regressions/language_tools_1466.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#[test]
fn code_actions_should_not_crash_on_validation_errors_with_mongodb() {
let schema = r#"
datasource db {
provider = "mongodb"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model A {
1bar Bar[]
}
"#;

let params = lsp_types::CodeActionParams {
text_document: lsp_types::TextDocumentIdentifier {
uri: "file:/path/to/schema.prisma".parse().unwrap(),
},
range: lsp_types::Range::default(),
context: lsp_types::CodeActionContext::default(),
work_done_progress_params: lsp_types::WorkDoneProgressParams { work_done_token: None },
partial_result_params: lsp_types::PartialResultParams {
partial_result_token: None,
},
};

prisma_fmt::code_actions(schema.to_owned(), &serde_json::to_string_pretty(&params).unwrap());
}
1 change: 1 addition & 0 deletions prisma-fmt/tests/regressions/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mod language_tools_1466;
18 changes: 18 additions & 0 deletions psl/parser-database/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1091,3 +1091,21 @@ fn validate_clustering_setting(ctx: &mut Context<'_>) -> Option<bool> {
ctx.visit_optional_arg("clustered")
.and_then(|sort| coerce::boolean(sort, ctx.diagnostics))
}

/// Create the default values of [`ModelAttributes`] and [`EnumAttributes`] for each model and enum
/// in the AST to ensure [`crate::walkers::ModelWalker`] and [`crate::walkers::EnumWalker`] can
/// access their corresponding entries in the attributes map in the database even in the presence
/// of name and type resolution errors. This is useful for the language tools.
pub(super) fn populate_empty_attributes(ctx: &mut Context<'_>) {
for top in ctx.ast.iter_tops() {
match top {
(ast::TopId::Model(model_id), ast::Top::Model(_)) => {
ctx.types.model_attributes.insert(model_id, ModelAttributes::default());
}
(ast::TopId::Enum(enum_id), ast::Top::Enum(_)) => {
ctx.types.enum_attributes.insert(enum_id, EnumAttributes::default());
}
_ => (),
}
}
}
4 changes: 4 additions & 0 deletions psl/parser-database/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ impl ParserDatabase {

// Return early on name resolution errors.
if ctx.diagnostics.has_errors() {
attributes::populate_empty_attributes(&mut ctx);

return ParserDatabase {
ast,
file,
Expand All @@ -108,6 +110,8 @@ impl ParserDatabase {

// Return early on type resolution errors.
if ctx.diagnostics.has_errors() {
attributes::populate_empty_attributes(&mut ctx);

return ParserDatabase {
ast,
file,
Expand Down

0 comments on commit ebb2e40

Please sign in to comment.