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

Provide quick fix for invalid syntax error #1133

Merged
113 changes: 94 additions & 19 deletions kclvm/error/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ impl Handler {
}

/// Construct a parse error and put it into the handler diagnostic buffer
pub fn add_syntex_error(&mut self, msg: &str, range: Range) -> &mut Self {
pub fn add_syntax_error(&mut self, msg: &str, range: Range) -> &mut Self {
let message = format!("Invalid syntax: {msg}");
let diag = Diagnostic::new_with_code(
Level::Error,
Expand Down Expand Up @@ -337,9 +337,16 @@ pub enum ParseError {
Message {
message: String,
span: Span,
fix_info: Option<FixInfo>,
},
}

#[derive(Debug, Clone)]
pub struct FixInfo {
pub error_kind: ErrorKind,
pub additional_info: Option<String>,
}

Peefy marked this conversation as resolved.
Show resolved Hide resolved
/// A single string error.
pub struct StringError(pub String);

Expand All @@ -357,28 +364,55 @@ impl ParseError {
}

/// New a message parse error with span.
pub fn message(message: String, span: Span) -> Self {
ParseError::Message { message, span }
pub fn message(message: String, span: Span, fix_info: Option<FixInfo>) -> Self {
ParseError::Message {
message,
span,
fix_info,
}
}
}

impl ParseError {
/// Convert a parse error into a error diagnostic.
/// Convert a parse error into an error diagnostic.
pub fn into_diag(self, sess: &Session) -> Result<Diagnostic> {
let span = match self {
ParseError::UnexpectedToken { span, .. } => span,
ParseError::Message { span, .. } => span,
};
let loc = sess.sm.lookup_char_pos(span.lo());
let pos: Position = loc.into();
Ok(Diagnostic::new_with_code(
Level::Error,
&self.to_string(),
None,
(pos.clone(), pos),
Some(DiagnosticId::Error(ErrorKind::InvalidSyntax)),
None,
))
match self {
ParseError::UnexpectedToken { span, .. } => {
let loc = sess.sm.lookup_char_pos(span.lo());
let pos: Position = loc.into();
Ok(Diagnostic::new_with_code(
Level::Error,
&self.to_string(),
None,
(pos.clone(), pos),
Some(DiagnosticId::Error(ErrorKind::InvalidSyntax)),
None,
))
}
ParseError::Message {
message,
span,
fix_info,
} => {
let loc = sess.sm.lookup_char_pos(span.lo());
let pos: Position = loc.into();

let mut sb = StyledBuffer::<DiagnosticStyle>::new();
let mut errs = vec![];
let code_snippet = CodeSnippet::new(span, Arc::clone(&sess.sm));
code_snippet.format(&mut sb, &mut errs);
let code_line = extract_code_line(&sb);
Peefy marked this conversation as resolved.
Show resolved Hide resolved

Ok(Diagnostic::new_with_code(
Level::Error,
format!("{:?}",code_line).as_ref(),
None,
(pos.clone(), pos),
Some(DiagnosticId::Error(ErrorKind::InvalidSyntax)),
None
))
}
}
}
}

Expand All @@ -405,7 +439,11 @@ impl SessionDiagnostic for ParseError {
diag.append_component(Box::new(format!(" {}\n", self.to_string())));
Ok(diag)
}
ParseError::Message { message, span } => {
ParseError::Message {
message,
span,
fix_info,
} => {
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 Expand Up @@ -544,3 +582,40 @@ pub fn err_to_str(err: Box<dyn Any + Send>) -> String {
"".to_string()
}
}

fn extract_code_line(sb: &StyledBuffer<DiagnosticStyle>) -> String {
let rendered_lines = sb.render();
if let Some(line) = rendered_lines.get(0) {
if let Some(code_snippet) = line.get(1) {
code_snippet.text.clone()
} else {
String::new()
}
} else {
String::new()
}
}

fn parse_multiple_assignment(error_message: &str) -> Option<(Vec<String>, Vec<String>)> {
Peefy marked this conversation as resolved.
Show resolved Hide resolved
let parts: Vec<&str> = error_message.split('=').collect();
if parts.len() != 2 {
return None;
}

// Extract the variables and values, trimming whitespace and removing comments.
let vars = parts[0]
.trim()
.split(',')
.map(|s| s.trim().to_string())
.collect();
let values = parts[1]
.split('#')
.next()
.unwrap_or("")
.trim()
.split(',')
.map(|s| s.trim().to_string())
.collect();

Some((vars, values))
}
6 changes: 3 additions & 3 deletions kclvm/parser/src/lexer/indent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ impl<'a> Lexer<'a> {
}
Err(msg) => {
self.sess
.struct_span_error(msg, self.span(self.pos, self.pos));
.struct_span_error(msg, self.span(self.pos, self.pos), None);
break;
}
}
Expand All @@ -151,7 +151,7 @@ impl<'a> Lexer<'a> {
}
Err(msg) => {
self.sess
.struct_span_error(msg, self.span(self.pos, self.pos));
.struct_span_error(msg, self.span(self.pos, self.pos), None);
None
}
}
Expand All @@ -161,7 +161,7 @@ impl<'a> Lexer<'a> {
fn last_indent(&mut self) -> &IndentLevel {
if self.indent_cxt.indents.is_empty() {
self.sess
.struct_span_error("mismatched indent level", self.span(self.pos, self.pos));
.struct_span_error("mismatched indent level", self.span(self.pos, self.pos), None);
self.indent_cxt.indents.push(IndentLevel::default());
}
self.indent_cxt.indents.last().unwrap()
Expand Down
22 changes: 21 additions & 1 deletion kclvm/parser/src/lexer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ impl<'a> Lexer<'a> {
self.sess.struct_span_error(
"invalid token '!', consider using 'not'",
self.span(start, self.pos),
None,
);
token::UnaryOp(token::UNot)
}
Expand Down Expand Up @@ -327,6 +328,7 @@ impl<'a> Lexer<'a> {
self.sess.struct_span_error(
"error nesting on close paren",
self.span(start, self.pos),
None,
);
token::CloseDelim(token::Brace)
}
Expand All @@ -335,6 +337,7 @@ impl<'a> Lexer<'a> {
self.sess.struct_span_error(
"error nesting on close paren",
self.span(start, self.pos),
None,
);
token::CloseDelim(token::Bracket)
}
Expand All @@ -346,6 +349,7 @@ impl<'a> Lexer<'a> {
self.sess.struct_span_error(
"error nesting on close paren",
self.span(start, self.pos),
None,
);
token::CloseDelim(token::Paren)
}
Expand All @@ -364,6 +368,7 @@ impl<'a> Lexer<'a> {
self.sess.struct_span_error(
"error nesting on close brace",
self.span(start, self.pos),
None,
);
token::CloseDelim(token::Paren)
}
Expand All @@ -372,6 +377,7 @@ impl<'a> Lexer<'a> {
self.sess.struct_span_error(
"error nesting on close brace",
self.span(start, self.pos),
None,
);
token::CloseDelim(token::Bracket)
}
Expand All @@ -383,6 +389,7 @@ impl<'a> Lexer<'a> {
self.sess.struct_span_error(
"error nesting on close brace",
self.span(start, self.pos),
None,
);
token::CloseDelim(token::Brace)
}
Expand All @@ -403,6 +410,7 @@ impl<'a> Lexer<'a> {
self.sess.struct_span_error(
"mismatched closing delimiter",
self.span(start, self.pos),
None,
);
token::CloseDelim(token::Brace)
}
Expand All @@ -411,6 +419,7 @@ impl<'a> Lexer<'a> {
self.sess.struct_span_error(
"mismatched closing delimiter",
self.span(start, self.pos),
None,
);
token::CloseDelim(token::Paren)
}
Expand All @@ -422,6 +431,7 @@ impl<'a> Lexer<'a> {
self.sess.struct_span_error(
"mismatched closing delimiter",
self.span(start, self.pos),
None,
);
token::CloseDelim(token::Bracket)
}
Expand All @@ -433,6 +443,7 @@ impl<'a> Lexer<'a> {
self.sess.struct_span_error(
"unexpected character after line continuation character",
self.span(start, self.pos),
None,
);
return None;
}
Expand All @@ -441,12 +452,13 @@ impl<'a> Lexer<'a> {
self.sess.struct_span_error(
"the semicolon ';' here is unnecessary, please remove it",
self.span(start, self.pos),
None,
);
return None;
}
_ => {
self.sess
.struct_span_error("unknown start of token", self.span(start, self.pos));
.struct_span_error("unknown start of token", self.span(start, self.pos),None);
return None;
}
})
Expand Down Expand Up @@ -506,6 +518,7 @@ impl<'a> Lexer<'a> {
self.sess.struct_span_error(
"unterminated string",
self.span(quote_char_pos, self.pos),
None,
)
}
// Cut offset before validation.
Expand Down Expand Up @@ -537,6 +550,7 @@ impl<'a> Lexer<'a> {
self.sess.struct_span_error(
"invalid string syntax",
self.span(content_start, self.pos),
None,
);
"".to_string()
} else {
Expand All @@ -550,6 +564,7 @@ impl<'a> Lexer<'a> {
self.sess.struct_span_error(
"invalid string syntax",
self.span(content_start, self.pos),
None,
);
"".to_string()
}
Expand All @@ -571,6 +586,7 @@ impl<'a> Lexer<'a> {
self.sess.struct_span_error(
"no valid digits found for number",
self.span(start, self.pos),
None,
);
// If it is a empty int, returns number 0.
(token::Integer, Symbol::intern("0"), None, None)
Expand All @@ -588,6 +604,7 @@ impl<'a> Lexer<'a> {
self.sess.struct_span_error(
"invalid int binary suffix",
self.span(start, self.pos),
None,
);
None
} else {
Expand Down Expand Up @@ -643,6 +660,7 @@ impl<'a> Lexer<'a> {
self.sess.struct_span_error(
&format!("invalid digit for a base {base} literal, start: {lo}, stop: {hi}"),
self.span(lo, self.pos),
None,
);
return false;
}
Expand All @@ -655,6 +673,7 @@ impl<'a> Lexer<'a> {
self.sess.struct_span_error(
"expected at least one digit in exponent",
self.span(start, self.pos),
None,
);
false
} else {
Expand All @@ -665,6 +684,7 @@ impl<'a> Lexer<'a> {
self.sess.struct_span_error(
&format!("{} float literal is not supported", base.describe()),
self.span(start, self.pos),
None,
);
false
}
Expand Down
Loading
Loading