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

Add code action to create missing fragment argument #4681

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
30 changes: 30 additions & 0 deletions compiler/crates/common/src/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use std::fmt::Write;

use lsp_types::DiagnosticSeverity;
use lsp_types::DiagnosticTag;
use lsp_types::NumberOrString;
use serde::ser::SerializeMap;
use serde_json::Value;

Expand Down Expand Up @@ -64,6 +65,7 @@ impl Diagnostic {
tags: Vec<DiagnosticTag>,
) -> Self {
Self(Box::new(DiagnosticData {
code: None,
message: Box::new(message),
location,
related_information: Vec::new(),
Expand All @@ -88,6 +90,27 @@ impl Diagnostic {
) -> Self {
let data = message.get_data();
Self(Box::new(DiagnosticData {
code: None,
message: Box::new(message),
location,
tags: Vec::new(),
severity: DiagnosticSeverity::ERROR,
related_information: Vec::new(),
data,
machine_readable: BTreeMap::new(),
}))
}

/// Creates a new error Diagnostic with additional data and code
/// that can be used in IDE code actions
pub fn error_with_data_and_code<T: 'static + DiagnosticDisplay + WithDiagnosticData>(
code: i32,
message: T,
location: Location,
) -> Self {
let data = message.get_data();
Self(Box::new(DiagnosticData {
code: Some(NumberOrString::Number(code)),
message: Box::new(message),
location,
tags: Vec::new(),
Expand Down Expand Up @@ -167,6 +190,10 @@ impl Diagnostic {
self
}

pub fn code(&self) -> Option<NumberOrString> {
self.0.code.to_owned()
}

pub fn message(&self) -> &impl DiagnosticDisplay {
&self.0.message
}
Expand Down Expand Up @@ -276,6 +303,9 @@ where

#[derive(fmt::Debug)]
struct DiagnosticData {
/// The diagnostic's code. Can be omitted.
code: Option<NumberOrString>,

/// Human readable error message.
message: Box<dyn DiagnosticDisplay>,

Expand Down
11 changes: 7 additions & 4 deletions compiler/crates/graphql-ir/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ use schema::Type;
use schema::TypeReference;

use crate::constants::ARGUMENT_DEFINITION;
use crate::errors::ValidationDiagnosticCode;
use crate::errors::ValidationMessage;
use crate::errors::ValidationMessageWithData;
use crate::ir::*;
Expand Down Expand Up @@ -423,10 +424,12 @@ impl<'schema, 'signatures, 'options> Builder<'schema, 'signatures, 'options> {
.used_variables
.iter()
.map(|(undefined_variable, usage)| {
Diagnostic::error(
ValidationMessage::ExpectedOperationVariableToBeDefined(
*undefined_variable,
),
Diagnostic::error_with_data_and_code(
ValidationDiagnosticCode::EXPECTED_OPERATION_VARIABLE_TO_BE_DEFINED,
ValidationMessageWithData::ExpectedOperationVariableToBeDefined {
variable_name: *undefined_variable,
variable_type: self.schema.get_type_string(&usage.type_),
},
self.location.with_span(usage.span),
)
})
Expand Down
45 changes: 36 additions & 9 deletions compiler/crates/graphql-ir/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,6 @@ pub enum ValidationMessage {
next_type: String,
},

#[error("Expected variable `${0}` to be defined on the operation")]
ExpectedOperationVariableToBeDefined(VariableName),

#[error(
"Expected argument definition to have an input type (scalar, enum, or input object), found type '{0}'"
)]
Expand Down Expand Up @@ -423,12 +420,6 @@ pub enum ValidationMessage {
)]
UnusedIgnoreUnusedVariablesDirective { operation_name: StringKey },

#[error("Operation '{operation_name}' references undefined variable{variables_string}.")]
GlobalVariables {
operation_name: StringKey,
variables_string: String,
},

#[error("Subscription '{subscription_name}' must have a single selection")]
GenerateSubscriptionNameSingleSelectionItem { subscription_name: StringKey },

Expand Down Expand Up @@ -539,6 +530,14 @@ pub enum ValidationMessage {
ResolverInMutation,
}

#[derive(Clone, Debug)]
pub struct ValidationDiagnosticCode;

impl ValidationDiagnosticCode {
pub const EXPECTED_OPERATION_VARIABLE_TO_BE_DEFINED: i32 = 1;
pub const UNDEFINED_VARIABLE_REFERENCED: i32 = 2;
}

#[derive(
Clone,
Debug,
Expand Down Expand Up @@ -584,6 +583,19 @@ pub enum ValidationMessageWithData {
argument_name: StringKey,
suggestions: Vec<StringKey>,
},

#[error("Expected variable `${variable_name}` to be defined on the operation")]
ExpectedOperationVariableToBeDefined {
variable_name: VariableName,
variable_type: String,
},

#[error("Operation '{operation_name}' references undefined variable '${variable_name}'.")]
UndefinedVariableReferenced {
operation_name: StringKey,
variable_name: VariableName,
variable_type: String,
},
}

impl WithDiagnosticData for ValidationMessageWithData {
Expand All @@ -599,6 +611,21 @@ impl WithDiagnosticData for ValidationMessageWithData {
ValidationMessageWithData::ExpectedSelectionsOnObjectField { field_name, .. } => {
vec![Box::new(format!("{} {{ }}", field_name))]
}
ValidationMessageWithData::ExpectedOperationVariableToBeDefined {
variable_name,
variable_type,
} => vec![
Box::new(variable_name.to_owned()),
Box::new(variable_type.to_owned()),
],
ValidationMessageWithData::UndefinedVariableReferenced {
variable_name,
variable_type,
..
} => vec![
Box::new(variable_name.to_owned()),
Box::new(variable_type.to_owned()),
],
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions compiler/crates/graphql-ir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ pub use transform::Transformer;
pub use validator::Validator;
pub use visitor::Visitor;

pub use crate::errors::ValidationDiagnosticCode;
pub use crate::errors::ValidationMessage;
pub use crate::errors::ValidationMessageWithData;

/// Re-exported values to be used by the `associated_data_impl!` macro.
pub mod reexport {
Expand Down