Skip to content

Commit

Permalink
feat: add parse error kind and fix the invalid syntax quick fix code
Browse files Browse the repository at this point in the history
Signed-off-by: peefy <xpf6677@163.com>
  • Loading branch information
Peefy committed May 27, 2024
1 parent 385879c commit 17438f5
Show file tree
Hide file tree
Showing 12 changed files with 128 additions and 241 deletions.
9 changes: 5 additions & 4 deletions kclvm/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions kclvm/error/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ serde = { version = "1.0", features = ["derive"] }
termize = "0.1.1"
indexmap = "1.0"
serde_json = "1.0"
thiserror = "1.0.61"
168 changes: 47 additions & 121 deletions kclvm/error/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use diagnostic::Range;
use indexmap::IndexSet;
use kclvm_runtime::PanicInfo;
use std::{any::Any, sync::Arc};
use thiserror::Error;

pub use diagnostic::{Diagnostic, DiagnosticId, Level, Message, Position, Style};
pub use error::*;
Expand Down Expand Up @@ -327,6 +328,28 @@ impl From<PanicInfo> for Diagnostic {
}
}

#[derive(Error, Debug, Clone)]
pub enum ParseErrorMessage {
#[error("invalid token '!', consider using 'not '")]
InvalidTokenNot,
#[error("'else if' here is invalid in KCL, consider using the 'elif' keyword")]
InvalidTokenElseIf,
#[error("unterminated string")]
UnterminatedString,
#[error("unexpected character after line continuation character")]
CharAfterLineContinuationToken,
#[error("the semicolon ';' here is unnecessary, please remove it")]
RedundantSemicolon,
#[error("expected expression, got {0}")]
ExpectExpr(String),
#[error("invalid string interpolation expression: '{0}'")]
InvalidStringInterpolationExpr(String),
#[error("invalid joined string spec without #")]
InvalidJoinedStringSpec,
#[error("invalid joined string")]
InvalidJoinedStringExpr,
}

#[derive(Debug, Clone)]
pub enum ParseError {
UnexpectedToken {
Expand All @@ -335,18 +358,16 @@ pub enum ParseError {
span: Span,
},
Message {
message: ParseErrorMessage,
span: Span,
suggestions: Option<Vec<String>>,
},
String {
message: String,
span: Span,
fix_info: Option<FixInfo>,
},
}

#[derive(Debug, Clone)]
pub struct FixInfo {
pub suggestion: Option<String>,
pub replacement: Option<String>,
}

/// A single string error.
pub struct StringError(pub String);

Expand All @@ -364,11 +385,15 @@ impl ParseError {
}

/// New a message parse error with span.
pub fn message(message: String, span: Span, fix_info: Option<FixInfo>) -> Self {
pub fn message(
message: ParseErrorMessage,
span: Span,
suggestions: Option<Vec<String>>,
) -> Self {
ParseError::Message {
message,
span,
fix_info,
suggestions,
}
}
}
Expand All @@ -379,24 +404,14 @@ impl ParseError {
let span = match self {
ParseError::UnexpectedToken { span, .. } => span,
ParseError::Message { span, .. } => span,
ParseError::String { span, .. } => span,
};
let loc = sess.sm.lookup_char_pos(span.lo());
let pos: Position = loc.into();
let suggestions = match self {
ParseError::Message {
fix_info: Some(ref info),
..
} => Some(vec![
info.suggestion
.clone()
.unwrap_or_else(|| "No suggestion available".to_string()),
info.replacement.clone().unwrap_or_else(|| " ".to_string()),
]),
let start_pos = sess.sm.lookup_char_pos(span.lo()).into();
let end_pos = sess.sm.lookup_char_pos(span.hi()).into();
let suggestions = match &self {
ParseError::Message { suggestions, .. } => suggestions.clone(),
_ => None,
};

let (start_pos, end_pos) = self.generate_modified_range(&self.to_string(), &pos);

Ok(Diagnostic::new_with_code(
Level::Error,
&self.to_string(),
Expand All @@ -406,98 +421,6 @@ impl ParseError {
suggestions,
))
}

fn generate_modified_range(&self, msg: &str, pos: &Position) -> (Position, Position) {
match msg {
"invalid token '!', consider using 'not '" => {
let start_column = pos.column.unwrap_or(0);
let end_column = start_column + 1;
(
Position {
column: Some(start_column),
..pos.clone()
},
Position {
column: Some(end_column),
..pos.clone()
},
)
}
"'else if' here is invalid in KCL, consider using the 'elif' keyword" => {
let start_column = pos.column.map(|col| col.saturating_sub(5)).unwrap_or(0);
let end_column = pos.column.map(|col| col.saturating_add(2)).unwrap_or(0);
(
Position {
column: Some(start_column),
..pos.clone()
},
Position {
column: Some(end_column),
..pos.clone()
},
)
}
"error nesting on close paren"
| "mismatched closing delimiter"
| "error nesting on close brace" => {
let start_column = pos.column.unwrap_or(0);
let end_column = start_column + 1;
(
Position {
column: Some(start_column),
..pos.clone()
},
Position {
column: Some(end_column),
..pos.clone()
},
)
}
"unterminated string" => {
let start_column = pos.column.unwrap_or(0);
let end_column = start_column + 1;
(
Position {
column: Some(start_column),
..pos.clone()
},
Position {
column: Some(end_column),
..pos.clone()
},
)
}
"unexpected character after line continuation character" => {
let start_column = pos.column.unwrap_or(0);
let end_column = u32::MAX;
(
Position {
column: Some(start_column),
..pos.clone()
},
Position {
column: Some(end_column.into()),
..pos.clone()
},
)
}
"the semicolon ';' here is unnecessary, please remove it" => {
let start_column = pos.column.unwrap_or(0);
let end_column = start_column + 1;
(
Position {
column: Some(start_column),
..pos.clone()
},
Position {
column: Some(end_column),
..pos.clone()
},
)
}
_ => (pos.clone(), pos.clone()),
}
}
}

impl ToString for ParseError {
Expand All @@ -507,6 +430,7 @@ impl ToString for ParseError {
format!("expected one of {expected:?} got {got}")
}
ParseError::Message { message, .. } => message.to_string(),
ParseError::String { message, .. } => message.to_string(),
}
}
}
Expand All @@ -523,11 +447,13 @@ impl SessionDiagnostic for ParseError {
diag.append_component(Box::new(format!(" {}\n", self.to_string())));
Ok(diag)
}
ParseError::Message {
message,
span,
fix_info: _,
} => {
ParseError::Message { message, span, .. } => {
let code_snippet = CodeSnippet::new(span, Arc::clone(&sess.sm));
diag.append_component(Box::new(code_snippet));
diag.append_component(Box::new(format!(" {message}\n")));
Ok(diag)
}
ParseError::String { message, span } => {
let code_snippet = CodeSnippet::new(span, Arc::clone(&sess.sm));
diag.append_component(Box::new(code_snippet));
diag.append_component(Box::new(format!(" {message}\n")));
Expand Down
Loading

0 comments on commit 17438f5

Please sign in to comment.