From 7b1dbd97e8899e9555bf0b77a3923bee4bc52fa5 Mon Sep 17 00:00:00 2001 From: Shashank Mittal Date: Thu, 14 Mar 2024 00:26:08 +0530 Subject: [PATCH 1/7] added quick fix for multiple assignments Signed-off-by: Shashank Mittal --- kclvm/error/src/lib.rs | 65 ++++++++++++++++++-------- kclvm/parser/src/session/mod.rs | 12 +++++ kclvm/tools/src/LSP/src/quick_fix.rs | 68 ++++++++++++++++++++++++++++ kclvm/tools/src/LSP/src/to_lsp.rs | 29 +++++++----- 4 files changed, 144 insertions(+), 30 deletions(-) diff --git a/kclvm/error/src/lib.rs b/kclvm/error/src/lib.rs index a45797b2d..02b7d0ae4 100644 --- a/kclvm/error/src/lib.rs +++ b/kclvm/error/src/lib.rs @@ -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, @@ -337,6 +337,7 @@ pub enum ParseError { Message { message: String, span: Span, + line_content: String, }, } @@ -357,28 +358,49 @@ 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, line_content: String) -> Self { + ParseError::Message { + message, + span, + line_content, + } } } 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 { - 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, + line_content, + } => { + let loc = sess.sm.lookup_char_pos(span.lo()); + let pos: Position = loc.into(); + // Pass line_content as a note or suggestion + Ok(Diagnostic::new_with_code( + Level::Error, + &message, + Some(&line_content), + (pos.clone(), pos), + Some(DiagnosticId::Error(ErrorKind::InvalidSyntax)), + None, + )) + } + } } } @@ -405,10 +427,15 @@ impl SessionDiagnostic for ParseError { diag.append_component(Box::new(format!(" {}\n", self.to_string()))); Ok(diag) } - ParseError::Message { message, span } => { + ParseError::Message { + message, + span, + line_content, + } => { 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"))); + diag.append_component(Box::new(format!("Line content: {line_content}\n"))); Ok(diag) } } diff --git a/kclvm/parser/src/session/mod.rs b/kclvm/parser/src/session/mod.rs index e15ea170d..d68100540 100644 --- a/kclvm/parser/src/session/mod.rs +++ b/kclvm/parser/src/session/mod.rs @@ -54,12 +54,24 @@ impl ParseSession { /// Struct and report an error based on a span and not abort the compiler process. #[inline] pub fn struct_span_error(&self, msg: &str, span: Span) { + let line_content = self.get_line_content_from_span(span); self.add_parse_err(ParseError::Message { message: msg.to_string(), span, + line_content, }); } + /// Extracts the content of the line corresponding to the given span. + fn get_line_content_from_span(&self, span: Span) -> String { + let source_file = self.0.sm.lookup_source_file(span.lo()); + let line_index = source_file.lookup_line(span.lo()).unwrap(); + source_file + .get_line(line_index) + .unwrap_or_else(|| "unknown line".into()) + .to_string() + } + /// Add a error into the session. #[inline] fn add_parse_err(&self, err: ParseError) { diff --git a/kclvm/tools/src/LSP/src/quick_fix.rs b/kclvm/tools/src/LSP/src/quick_fix.rs index 1647e7b29..e0bbe5aaf 100644 --- a/kclvm/tools/src/LSP/src/quick_fix.rs +++ b/kclvm/tools/src/LSP/src/quick_fix.rs @@ -40,6 +40,42 @@ pub(crate) fn quick_fix(uri: &Url, diags: &Vec) -> Vec { + let line_content = extract_line_content(&diag.data).unwrap_or_default(); + + if let Some((vars, values)) = parse_multiple_assignment(&line_content) { + let mut changes = HashMap::new(); + let new_text = vars + .iter() + .zip(values.iter()) + .map(|(var, value)| format!("{} = {}", var, value)) + .collect::>() + .join("\n"); + + let mut fixed_range = diag.range; + fixed_range.start.character = 0; + fixed_range.end.character = line_content.len() as u32; + + changes.insert( + uri.clone(), + vec![TextEdit { + range: fixed_range, + new_text, + }], + ); + code_actions.push(CodeActionOrCommand::CodeAction(CodeAction { + title: "Split multiple assignment into separate assignments" + .to_string(), + kind: Some(CodeActionKind::QUICKFIX), + diagnostics: Some(vec![diag.clone()]), + edit: Some(lsp_types::WorkspaceEdit { + changes: Some(changes), + ..Default::default() + }), + ..Default::default() + })); + } + } _ => continue, }, DiagnosticId::Warning(warn) => match warn { @@ -109,6 +145,13 @@ fn extract_suggested_replacements(data: &Option) -> Vec { .unwrap_or_default() } +fn extract_line_content(data: &Option) -> Option { + data.as_ref() + .and_then(|data| data.get("line_content")) + .and_then(|value| value.as_str()) + .map(|s| s.to_string()) +} + pub(crate) fn convert_code_to_kcl_diag_id(code: &NumberOrString) -> Option { match code { NumberOrString::Number(_) => None, @@ -117,6 +160,7 @@ pub(crate) fn convert_code_to_kcl_diag_id(code: &NumberOrString) -> Option Some(DiagnosticId::Warning(WarningKind::UnusedImportWarning)), "ReimportWarning" => Some(DiagnosticId::Warning(WarningKind::ReimportWarning)), "CompileError" => Some(DiagnosticId::Error(ErrorKind::CompileError)), + "InvalidSyntax" => Some(DiagnosticId::Error(ErrorKind::InvalidSyntax)), "ImportPositionWarning" => { Some(DiagnosticId::Warning(WarningKind::ImportPositionWarning)) } @@ -125,6 +169,30 @@ pub(crate) fn convert_code_to_kcl_diag_id(code: &NumberOrString) -> Option Option<(Vec, Vec)> { + 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)) +} + #[cfg(test)] mod tests { diff --git a/kclvm/tools/src/LSP/src/to_lsp.rs b/kclvm/tools/src/LSP/src/to_lsp.rs index ee96ab549..b89a87036 100644 --- a/kclvm/tools/src/LSP/src/to_lsp.rs +++ b/kclvm/tools/src/LSP/src/to_lsp.rs @@ -46,17 +46,24 @@ fn kcl_msg_to_lsp_diags( let start_position = lsp_pos(&range.0); let end_position = lsp_pos(&range.1); - let data = msg - .suggested_replacement - .as_ref() - .map(|s_vec| { - s_vec - .iter() - .filter(|s| !s.is_empty()) - .collect::>() - }) - .filter(|v| !v.is_empty()) - .map(|s| json!({ "suggested_replacement": s })); + let mut data_map = serde_json::Map::new(); + if let Some(s_vec) = msg.suggested_replacement.as_ref().filter(|v| !v.is_empty()) { + let suggestions = s_vec + .iter() + .filter(|s| !s.is_empty()) + .collect::>(); + if !suggestions.is_empty() { + data_map.insert("suggested_replacement".to_string(), json!(suggestions)); + } + } + if let Some(note) = &msg.note { + data_map.insert("line_content".to_string(), json!(note)); + } + let data = if data_map.is_empty() { + None + } else { + Some(json!(data_map)) + }; let related_information = if related_msg.is_empty() { None From 82d34c886d20b4e2668a512da405107bb5c6c93e Mon Sep 17 00:00:00 2001 From: Shashank Mittal Date: Fri, 15 Mar 2024 00:35:12 +0530 Subject: [PATCH 2/7] fix Signed-off-by: Shashank Mittal --- kclvm/error/src/lib.rs | 58 +++++++++++++++++++++++----- kclvm/parser/src/session/mod.rs | 12 ------ kclvm/tools/src/LSP/src/quick_fix.rs | 51 ++---------------------- kclvm/tools/src/LSP/src/to_lsp.rs | 3 -- 4 files changed, 52 insertions(+), 72 deletions(-) diff --git a/kclvm/error/src/lib.rs b/kclvm/error/src/lib.rs index 02b7d0ae4..171c0bc09 100644 --- a/kclvm/error/src/lib.rs +++ b/kclvm/error/src/lib.rs @@ -337,7 +337,6 @@ pub enum ParseError { Message { message: String, span: Span, - line_content: String, }, } @@ -358,11 +357,10 @@ impl ParseError { } /// New a message parse error with span. - pub fn message(message: String, span: Span, line_content: String) -> Self { + pub fn message(message: String, span: Span) -> Self { ParseError::Message { message, span, - line_content, } } } @@ -386,18 +384,23 @@ impl ParseError { ParseError::Message { message, span, - line_content, } => { let loc = sess.sm.lookup_char_pos(span.lo()); let pos: Position = loc.into(); - // Pass line_content as a note or suggestion + + let mut sb = StyledBuffer::::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); + Ok(Diagnostic::new_with_code( Level::Error, - &message, - Some(&line_content), + format!("{:?}",code_line).as_ref(), + None, (pos.clone(), pos), Some(DiagnosticId::Error(ErrorKind::InvalidSyntax)), - None, + None )) } } @@ -430,12 +433,10 @@ impl SessionDiagnostic for ParseError { ParseError::Message { message, span, - line_content, } => { 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"))); - diag.append_component(Box::new(format!("Line content: {line_content}\n"))); Ok(diag) } } @@ -571,3 +572,40 @@ pub fn err_to_str(err: Box) -> String { "".to_string() } } + +fn extract_code_line(sb: &StyledBuffer) -> 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, Vec)> { + 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)) +} \ No newline at end of file diff --git a/kclvm/parser/src/session/mod.rs b/kclvm/parser/src/session/mod.rs index d68100540..e15ea170d 100644 --- a/kclvm/parser/src/session/mod.rs +++ b/kclvm/parser/src/session/mod.rs @@ -54,24 +54,12 @@ impl ParseSession { /// Struct and report an error based on a span and not abort the compiler process. #[inline] pub fn struct_span_error(&self, msg: &str, span: Span) { - let line_content = self.get_line_content_from_span(span); self.add_parse_err(ParseError::Message { message: msg.to_string(), span, - line_content, }); } - /// Extracts the content of the line corresponding to the given span. - fn get_line_content_from_span(&self, span: Span) -> String { - let source_file = self.0.sm.lookup_source_file(span.lo()); - let line_index = source_file.lookup_line(span.lo()).unwrap(); - source_file - .get_line(line_index) - .unwrap_or_else(|| "unknown line".into()) - .to_string() - } - /// Add a error into the session. #[inline] fn add_parse_err(&self, err: ParseError) { diff --git a/kclvm/tools/src/LSP/src/quick_fix.rs b/kclvm/tools/src/LSP/src/quick_fix.rs index e0bbe5aaf..6af39299a 100644 --- a/kclvm/tools/src/LSP/src/quick_fix.rs +++ b/kclvm/tools/src/LSP/src/quick_fix.rs @@ -41,26 +41,14 @@ pub(crate) fn quick_fix(uri: &Url, diags: &Vec) -> Vec { - let line_content = extract_line_content(&diag.data).unwrap_or_default(); - - if let Some((vars, values)) = parse_multiple_assignment(&line_content) { + let replacement_texts = extract_suggested_replacements(&diag.data); + for replacement_text in replacement_texts { let mut changes = HashMap::new(); - let new_text = vars - .iter() - .zip(values.iter()) - .map(|(var, value)| format!("{} = {}", var, value)) - .collect::>() - .join("\n"); - - let mut fixed_range = diag.range; - fixed_range.start.character = 0; - fixed_range.end.character = line_content.len() as u32; - changes.insert( uri.clone(), vec![TextEdit { - range: fixed_range, - new_text, + range: diag.range, + new_text: replacement_text.clone(), }], ); code_actions.push(CodeActionOrCommand::CodeAction(CodeAction { @@ -145,13 +133,6 @@ fn extract_suggested_replacements(data: &Option) -> Vec { .unwrap_or_default() } -fn extract_line_content(data: &Option) -> Option { - data.as_ref() - .and_then(|data| data.get("line_content")) - .and_then(|value| value.as_str()) - .map(|s| s.to_string()) -} - pub(crate) fn convert_code_to_kcl_diag_id(code: &NumberOrString) -> Option { match code { NumberOrString::Number(_) => None, @@ -169,30 +150,6 @@ pub(crate) fn convert_code_to_kcl_diag_id(code: &NumberOrString) -> Option Option<(Vec, Vec)> { - 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)) -} - #[cfg(test)] mod tests { diff --git a/kclvm/tools/src/LSP/src/to_lsp.rs b/kclvm/tools/src/LSP/src/to_lsp.rs index b89a87036..e02978d6d 100644 --- a/kclvm/tools/src/LSP/src/to_lsp.rs +++ b/kclvm/tools/src/LSP/src/to_lsp.rs @@ -56,9 +56,6 @@ fn kcl_msg_to_lsp_diags( data_map.insert("suggested_replacement".to_string(), json!(suggestions)); } } - if let Some(note) = &msg.note { - data_map.insert("line_content".to_string(), json!(note)); - } let data = if data_map.is_empty() { None } else { From 9c811319b7c5c2bbdf9c52b6720fb7e7a792cd12 Mon Sep 17 00:00:00 2001 From: Shashank Mittal Date: Tue, 9 Apr 2024 03:47:17 +0530 Subject: [PATCH 3/7] expanded ParseError enum Signed-off-by: Shashank Mittal --- kclvm/error/src/lib.rs | 12 +++++++++++- kclvm/parser/src/lexer/indent.rs | 6 +++--- kclvm/parser/src/lexer/mod.rs | 22 +++++++++++++++++++++- kclvm/parser/src/parser/expr.rs | 19 ++++++++++++++----- kclvm/parser/src/parser/mod.rs | 1 + kclvm/parser/src/parser/stmt.rs | 11 +++++++++-- kclvm/parser/src/session/mod.rs | 5 +++-- 7 files changed, 62 insertions(+), 14 deletions(-) diff --git a/kclvm/error/src/lib.rs b/kclvm/error/src/lib.rs index 171c0bc09..ad348cc10 100644 --- a/kclvm/error/src/lib.rs +++ b/kclvm/error/src/lib.rs @@ -337,9 +337,16 @@ pub enum ParseError { Message { message: String, span: Span, + fix_info: Option, }, } +#[derive(Debug, Clone)] +pub struct FixInfo { + pub error_kind: ErrorKind, + pub additional_info: Option, +} + /// A single string error. pub struct StringError(pub String); @@ -357,10 +364,11 @@ impl ParseError { } /// New a message parse error with span. - pub fn message(message: String, span: Span) -> Self { + pub fn message(message: String, span: Span, fix_info: Option) -> Self { ParseError::Message { message, span, + fix_info, } } } @@ -384,6 +392,7 @@ impl ParseError { ParseError::Message { message, span, + fix_info, } => { let loc = sess.sm.lookup_char_pos(span.lo()); let pos: Position = loc.into(); @@ -433,6 +442,7 @@ impl SessionDiagnostic for ParseError { ParseError::Message { message, span, + fix_info, } => { let code_snippet = CodeSnippet::new(span, Arc::clone(&sess.sm)); diag.append_component(Box::new(code_snippet)); diff --git a/kclvm/parser/src/lexer/indent.rs b/kclvm/parser/src/lexer/indent.rs index f53ee8ff3..313747cad 100644 --- a/kclvm/parser/src/lexer/indent.rs +++ b/kclvm/parser/src/lexer/indent.rs @@ -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; } } @@ -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 } } @@ -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() diff --git a/kclvm/parser/src/lexer/mod.rs b/kclvm/parser/src/lexer/mod.rs index 187c124b4..0e97a15f9 100644 --- a/kclvm/parser/src/lexer/mod.rs +++ b/kclvm/parser/src/lexer/mod.rs @@ -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) } @@ -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) } @@ -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) } @@ -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) } @@ -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) } @@ -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) } @@ -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) } @@ -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) } @@ -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) } @@ -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) } @@ -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; } @@ -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; } }) @@ -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. @@ -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 { @@ -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() } @@ -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) @@ -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 { @@ -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; } @@ -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 { @@ -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 } diff --git a/kclvm/parser/src/parser/expr.rs b/kclvm/parser/src/parser/expr.rs index b300f2c42..0a0123a75 100644 --- a/kclvm/parser/src/parser/expr.rs +++ b/kclvm/parser/src/parser/expr.rs @@ -55,6 +55,7 @@ impl<'a> Parser<'a> { UnitUsize(n, "space".to_string()).into_string_with_unit(), ), self.token.span, + None, ); self.bump(); } @@ -90,6 +91,7 @@ impl<'a> Parser<'a> { self.sess.struct_span_error( &format!("unexpected '{:?}'", self.token.kind), self.token.span, + None, ); self.bump(); } @@ -178,6 +180,7 @@ impl<'a> Parser<'a> { self.sess.struct_span_error( "'not is' here is invalid, consider using 'is not'", self.token.span, + None, ); BinOrCmpOp::Cmp(CmpOp::IsNot) } else { @@ -506,7 +509,7 @@ impl<'a> Parser<'a> { if !is_slice && round == 1 { // it just has one round for an array self.sess - .struct_span_error("a list should have only one expr", self.token.span) + .struct_span_error("a list should have only one expr", self.token.span, None,) } exprs[expr_index] = Some(self.parse_expr()); @@ -514,7 +517,7 @@ impl<'a> Parser<'a> { if exprs_consecutive > 1 { self.sess - .struct_span_error("consecutive exprs found", self.token.span) + .struct_span_error("consecutive exprs found", self.token.span, None,) } } } @@ -523,7 +526,7 @@ impl<'a> Parser<'a> { if exprs.len() != 3 { self.sess - .struct_span_error("a slice should have three exprs", self.token.span) + .struct_span_error("a slice should have three exprs", self.token.span, None,) } // RIGHT_BRACKETS @@ -554,11 +557,12 @@ impl<'a> Parser<'a> { self.sess.struct_span_error( &format!("expected expression got {}", token_str), self.token.span, + None, ) } if !(exprs[1].is_none() && exprs[2].is_none()) { self.sess - .struct_span_error("a list should have only one expr", self.token.span) + .struct_span_error("a list should have only one expr", self.token.span, None,) } Box::new(Node::node( Expr::Subscript(Subscript { @@ -993,6 +997,7 @@ impl<'a> Parser<'a> { items.len() ), item_start_token.span, + None, ); Box::new(Node::node( Expr::ListComp(ListComp { @@ -1013,6 +1018,7 @@ impl<'a> Parser<'a> { self.sess.struct_span_error( "missing list comp clause expression", item_start_token.span, + None, ); Box::new(Node::node( Expr::List(ListExpr { @@ -1321,6 +1327,7 @@ impl<'a> Parser<'a> { items.len() ), item_start_token.span, + None, ); Box::new(Node::node( Expr::DictComp(DictComp { @@ -1341,6 +1348,7 @@ impl<'a> Parser<'a> { self.sess.struct_span_error( "missing config comp clause expression", item_start_token.span, + None, ); Box::new(Node::node( Expr::Config(ConfigExpr { items }), @@ -2027,6 +2035,7 @@ impl<'a> Parser<'a> { self.sess.struct_span_error( "positional argument follows keyword argument", token.span, + None, ) } } @@ -2383,7 +2392,7 @@ impl<'a> Parser<'a> { x } else { self.sess - .struct_span_error("expected identifier", token.span); + .struct_span_error("expected identifier", token.span, None,); expr.into_missing_identifier().node } } diff --git a/kclvm/parser/src/parser/mod.rs b/kclvm/parser/src/parser/mod.rs index 58ae1532c..552018fd5 100644 --- a/kclvm/parser/src/parser/mod.rs +++ b/kclvm/parser/src/parser/mod.rs @@ -145,6 +145,7 @@ impl<'a> Parser<'a> { self.sess.struct_span_error( &format!("expected expression got {}", token_str), self.token.span, + None ); self.bump(); true diff --git a/kclvm/parser/src/parser/stmt.rs b/kclvm/parser/src/parser/stmt.rs index 2f6558ae9..4d7dec34d 100644 --- a/kclvm/parser/src/parser/stmt.rs +++ b/kclvm/parser/src/parser/stmt.rs @@ -42,6 +42,7 @@ impl<'a> Parser<'a> { self.sess.struct_span_error( &format!("unexpected '{:?}'", self.token.kind), self.token.span, + None, ); None @@ -442,7 +443,7 @@ impl<'a> Parser<'a> { 1 => Some(ident.names[0].clone()), _ => { self.sess - .struct_span_error("Invalid import asname", self.token.span); + .struct_span_error("Invalid import asname", self.token.span, None); None } } @@ -603,6 +604,7 @@ impl<'a> Parser<'a> { self.sess.struct_span_error( "'else if' here is invalid in KCL, consider using the 'elif' keyword", self.token.span, + None, ); } else if self.token.kind != TokenKind::Colon { self.sess @@ -1035,6 +1037,7 @@ impl<'a> Parser<'a> { self.sess.struct_span_error( "duplicate schema index signature definitions, only one is allowed in the schema", token.span, + None, ); } body_index_signature = @@ -1051,6 +1054,7 @@ impl<'a> Parser<'a> { Into::::into(self.token) ), self.token.span, + None, ); } @@ -1585,6 +1589,7 @@ impl<'a> Parser<'a> { this.sess.struct_span_error( "string interpolation expression can not be empty", Span::new(start_pos, end_pos), + None, ); } @@ -1621,6 +1626,7 @@ impl<'a> Parser<'a> { this.sess.struct_span_error( "invalid joined string spec without #", parser.token.span, + None, ); } // Whether there is syntax error or not, bump the joined string spec token. @@ -1638,6 +1644,7 @@ impl<'a> Parser<'a> { parser.sess.struct_span_error( &format!("invalid string interpolation expression: '{src}'"), Span::new(lo, hi), + None, ) } @@ -1675,7 +1682,7 @@ impl<'a> Parser<'a> { continue; } else { self.sess - .struct_span_error("invalid joined string", self.token.span); + .struct_span_error("invalid joined string", self.token.span, None,); joined_value .values .push(node_ref!(Expr::StringLit(StringLit { diff --git a/kclvm/parser/src/session/mod.rs b/kclvm/parser/src/session/mod.rs index e15ea170d..870be1cfa 100644 --- a/kclvm/parser/src/session/mod.rs +++ b/kclvm/parser/src/session/mod.rs @@ -3,7 +3,7 @@ use compiler_base_macros::bug; use compiler_base_session::Session; use indexmap::IndexSet; use kclvm_ast::token::Token; -use kclvm_error::{Diagnostic, Handler, ParseError}; +use kclvm_error::{Diagnostic, FixInfo, Handler, ParseError}; use kclvm_span::{BytePos, Loc, Span}; use std::{cell::RefCell, sync::Arc}; @@ -53,10 +53,11 @@ impl ParseSession { /// Struct and report an error based on a span and not abort the compiler process. #[inline] - pub fn struct_span_error(&self, msg: &str, span: Span) { + pub fn struct_span_error(&self, msg: &str, span: Span, fix_info: Option) { self.add_parse_err(ParseError::Message { message: msg.to_string(), span, + fix_info, }); } From a4a439bef4226b8d6063d1cbaaf1c9b66aa20fa6 Mon Sep 17 00:00:00 2001 From: Shashank Mittal Date: Tue, 21 May 2024 03:14:09 +0530 Subject: [PATCH 4/7] new structure introduced Signed-off-by: Shashank Mittal --- kclvm/error/src/lib.rs | 120 ++++++++----------------------- kclvm/parser/src/lexer/indent.rs | 8 +-- kclvm/parser/src/lexer/mod.rs | 28 ++------ kclvm/parser/src/parser/expr.rs | 20 ++---- kclvm/parser/src/parser/mod.rs | 3 +- kclvm/parser/src/parser/stmt.rs | 12 +--- kclvm/parser/src/session/mod.rs | 20 +++++- 7 files changed, 67 insertions(+), 144 deletions(-) diff --git a/kclvm/error/src/lib.rs b/kclvm/error/src/lib.rs index b1b3c8d65..3c79bd58b 100644 --- a/kclvm/error/src/lib.rs +++ b/kclvm/error/src/lib.rs @@ -94,7 +94,7 @@ impl Handler { } /// Construct a parse error and put it into the handler diagnostic buffer - pub fn add_syntax_error(&mut self, msg: &str, range: Range) -> &mut Self { + pub fn add_syntex_error(&mut self, msg: &str, range: Range) -> &mut Self { let message = format!("Invalid syntax: {msg}"); let diag = Diagnostic::new_with_code( Level::Error, @@ -337,13 +337,14 @@ pub enum ParseError { Message { message: String, span: Span, - fix_info: Option, + fix_info: Option }, } #[derive(Debug, Clone)] -pub struct FixInfo { - pub error_kind: ErrorKind, +pub struct FixInfo{ + pub suggestion: Option, + pub replacement: Option, pub additional_info: Option, } @@ -364,55 +365,37 @@ impl ParseError { } /// New a message parse error with span. - pub fn message(message: String, span: Span, fix_info: Option) -> Self { - ParseError::Message { - message, - span, - fix_info, - } + pub fn message(message: String, span: Span, fix_info:Option) -> Self { + ParseError::Message { message, span, fix_info } } } impl ParseError { - /// Convert a parse error into an error diagnostic. + /// Convert a parse error into a error diagnostic. pub fn into_diag(self, sess: &Session) -> Result { - 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::::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); - - Ok(Diagnostic::new_with_code( - Level::Error, - format!("{:?}",code_line).as_ref(), - None, - (pos.clone(), pos), - Some(DiagnosticId::Error(ErrorKind::InvalidSyntax)), - None - )) + 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(); + 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()) + ]) } - } + _ => None, + }; + Ok(Diagnostic::new_with_code( + Level::Error, + &self.to_string(), + None, + (pos.clone(), pos), + Some(DiagnosticId::Error(ErrorKind::InvalidSyntax)), + suggestions, + )) } } @@ -439,11 +422,7 @@ 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, 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"))); @@ -587,41 +566,4 @@ pub fn err_to_str(err: Box) -> String { } else { "".to_string() } -} - -fn extract_code_line(sb: &StyledBuffer) -> 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, Vec)> { - 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)) } \ No newline at end of file diff --git a/kclvm/parser/src/lexer/indent.rs b/kclvm/parser/src/lexer/indent.rs index 313747cad..577f0521d 100644 --- a/kclvm/parser/src/lexer/indent.rs +++ b/kclvm/parser/src/lexer/indent.rs @@ -139,7 +139,7 @@ impl<'a> Lexer<'a> { } Err(msg) => { self.sess - .struct_span_error(msg, self.span(self.pos, self.pos), None); + .struct_span_error(msg, self.span(self.pos, self.pos)); break; } } @@ -151,7 +151,7 @@ impl<'a> Lexer<'a> { } Err(msg) => { self.sess - .struct_span_error(msg, self.span(self.pos, self.pos), None); + .struct_span_error(msg, self.span(self.pos, self.pos)); None } } @@ -161,9 +161,9 @@ 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), None); + .struct_span_error("mismatched indent level", self.span(self.pos, self.pos)); self.indent_cxt.indents.push(IndentLevel::default()); } self.indent_cxt.indents.last().unwrap() } -} +} \ No newline at end of file diff --git a/kclvm/parser/src/lexer/mod.rs b/kclvm/parser/src/lexer/mod.rs index 0e97a15f9..0d4b55320 100644 --- a/kclvm/parser/src/lexer/mod.rs +++ b/kclvm/parser/src/lexer/mod.rs @@ -258,10 +258,11 @@ impl<'a> Lexer<'a> { // Unary op kclvm_lexer::TokenKind::Tilde => token::UnaryOp(token::UTilde), kclvm_lexer::TokenKind::Bang => { - self.sess.struct_span_error( + self.sess.struct_span_error_with_suggestions( "invalid token '!', consider using 'not'", self.span(start, self.pos), - None, + Some("Replace '!' with 'not'".to_string()), + Some("not".to_string()), ); token::UnaryOp(token::UNot) } @@ -328,7 +329,6 @@ impl<'a> Lexer<'a> { self.sess.struct_span_error( "error nesting on close paren", self.span(start, self.pos), - None, ); token::CloseDelim(token::Brace) } @@ -337,7 +337,6 @@ impl<'a> Lexer<'a> { self.sess.struct_span_error( "error nesting on close paren", self.span(start, self.pos), - None, ); token::CloseDelim(token::Bracket) } @@ -349,7 +348,6 @@ impl<'a> Lexer<'a> { self.sess.struct_span_error( "error nesting on close paren", self.span(start, self.pos), - None, ); token::CloseDelim(token::Paren) } @@ -368,7 +366,6 @@ impl<'a> Lexer<'a> { self.sess.struct_span_error( "error nesting on close brace", self.span(start, self.pos), - None, ); token::CloseDelim(token::Paren) } @@ -377,7 +374,6 @@ impl<'a> Lexer<'a> { self.sess.struct_span_error( "error nesting on close brace", self.span(start, self.pos), - None, ); token::CloseDelim(token::Bracket) } @@ -389,7 +385,6 @@ impl<'a> Lexer<'a> { self.sess.struct_span_error( "error nesting on close brace", self.span(start, self.pos), - None, ); token::CloseDelim(token::Brace) } @@ -410,7 +405,6 @@ impl<'a> Lexer<'a> { self.sess.struct_span_error( "mismatched closing delimiter", self.span(start, self.pos), - None, ); token::CloseDelim(token::Brace) } @@ -419,7 +413,6 @@ impl<'a> Lexer<'a> { self.sess.struct_span_error( "mismatched closing delimiter", self.span(start, self.pos), - None, ); token::CloseDelim(token::Paren) } @@ -431,7 +424,6 @@ impl<'a> Lexer<'a> { self.sess.struct_span_error( "mismatched closing delimiter", self.span(start, self.pos), - None, ); token::CloseDelim(token::Bracket) } @@ -443,7 +435,6 @@ impl<'a> Lexer<'a> { self.sess.struct_span_error( "unexpected character after line continuation character", self.span(start, self.pos), - None, ); return None; } @@ -452,13 +443,12 @@ 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),None); + .struct_span_error("unknown start of token", self.span(start, self.pos)); return None; } }) @@ -518,7 +508,6 @@ impl<'a> Lexer<'a> { self.sess.struct_span_error( "unterminated string", self.span(quote_char_pos, self.pos), - None, ) } // Cut offset before validation. @@ -550,7 +539,6 @@ impl<'a> Lexer<'a> { self.sess.struct_span_error( "invalid string syntax", self.span(content_start, self.pos), - None, ); "".to_string() } else { @@ -564,7 +552,6 @@ impl<'a> Lexer<'a> { self.sess.struct_span_error( "invalid string syntax", self.span(content_start, self.pos), - None, ); "".to_string() } @@ -586,7 +573,6 @@ 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) @@ -604,7 +590,6 @@ impl<'a> Lexer<'a> { self.sess.struct_span_error( "invalid int binary suffix", self.span(start, self.pos), - None, ); None } else { @@ -660,7 +645,6 @@ 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; } @@ -673,7 +657,6 @@ impl<'a> Lexer<'a> { self.sess.struct_span_error( "expected at least one digit in exponent", self.span(start, self.pos), - None, ); false } else { @@ -684,7 +667,6 @@ impl<'a> Lexer<'a> { self.sess.struct_span_error( &format!("{} float literal is not supported", base.describe()), self.span(start, self.pos), - None, ); false } @@ -776,4 +758,4 @@ impl TokenStreamBuilder { None => &TokenKind::Dummy, } } -} +} \ No newline at end of file diff --git a/kclvm/parser/src/parser/expr.rs b/kclvm/parser/src/parser/expr.rs index e39feb216..8d509195e 100644 --- a/kclvm/parser/src/parser/expr.rs +++ b/kclvm/parser/src/parser/expr.rs @@ -55,7 +55,6 @@ impl<'a> Parser<'a> { UnitUsize(n, "space".to_string()).into_string_with_unit(), ), self.token.span, - None, ); self.bump(); } @@ -178,7 +177,6 @@ impl<'a> Parser<'a> { self.sess.struct_span_error( "'not is' here is invalid, consider using 'is not'", self.token.span, - None, ); BinOrCmpOp::Cmp(CmpOp::IsNot) } else { @@ -524,7 +522,7 @@ impl<'a> Parser<'a> { if !is_slice && round == 1 { // it just has one round for an array self.sess - .struct_span_error("a list should have only one expr", self.token.span, None,) + .struct_span_error("a list should have only one expr", self.token.span) } exprs[expr_index] = Some(self.parse_expr()); @@ -532,7 +530,7 @@ impl<'a> Parser<'a> { if exprs_consecutive > 1 { self.sess - .struct_span_error("consecutive exprs found", self.token.span, None,) + .struct_span_error("consecutive exprs found", self.token.span) } } } @@ -541,7 +539,7 @@ impl<'a> Parser<'a> { if exprs.len() != 3 { self.sess - .struct_span_error("a slice should have three exprs", self.token.span, None,) + .struct_span_error("a slice should have three exprs", self.token.span) } // RIGHT_BRACKETS @@ -572,12 +570,11 @@ impl<'a> Parser<'a> { self.sess.struct_span_error( &format!("expected expression got {}", token_str), self.token.span, - None, ) } if !(exprs[1].is_none() && exprs[2].is_none()) { self.sess - .struct_span_error("a list should have only one expr", self.token.span, None,) + .struct_span_error("a list should have only one expr", self.token.span) } Box::new(Node::node( Expr::Subscript(Subscript { @@ -1012,7 +1009,6 @@ impl<'a> Parser<'a> { items.len() ), item_start_token.span, - None, ); Box::new(Node::node( Expr::ListComp(ListComp { @@ -1033,7 +1029,6 @@ impl<'a> Parser<'a> { self.sess.struct_span_error( "missing list comp clause expression", item_start_token.span, - None, ); Box::new(Node::node( Expr::List(ListExpr { @@ -1342,7 +1337,6 @@ impl<'a> Parser<'a> { items.len() ), item_start_token.span, - None, ); Box::new(Node::node( Expr::DictComp(DictComp { @@ -1363,7 +1357,6 @@ impl<'a> Parser<'a> { self.sess.struct_span_error( "missing config comp clause expression", item_start_token.span, - None, ); Box::new(Node::node( Expr::Config(ConfigExpr { items }), @@ -2069,7 +2062,6 @@ impl<'a> Parser<'a> { self.sess.struct_span_error( "positional argument follows keyword argument", token.span, - None, ) } } @@ -2428,8 +2420,8 @@ impl<'a> Parser<'a> { x } else { self.sess - .struct_span_error("expected identifier", token.span, None,); + .struct_span_error("expected identifier", token.span); expr.into_missing_identifier().node } } -} +} \ No newline at end of file diff --git a/kclvm/parser/src/parser/mod.rs b/kclvm/parser/src/parser/mod.rs index 2dc9ff94e..b15840dde 100644 --- a/kclvm/parser/src/parser/mod.rs +++ b/kclvm/parser/src/parser/mod.rs @@ -149,7 +149,6 @@ impl<'a> Parser<'a> { self.sess.struct_span_error( &format!("expected expression got {}", token_str), self.token.span, - None ); self.bump(); true @@ -226,4 +225,4 @@ impl<'a> Parser<'a> { (non_comment_tokens, comments) } -} +} \ No newline at end of file diff --git a/kclvm/parser/src/parser/stmt.rs b/kclvm/parser/src/parser/stmt.rs index e75936920..b76130342 100644 --- a/kclvm/parser/src/parser/stmt.rs +++ b/kclvm/parser/src/parser/stmt.rs @@ -441,7 +441,7 @@ impl<'a> Parser<'a> { 1 => Some(ident.names[0].clone()), _ => { self.sess - .struct_span_error("Invalid import asname", self.token.span, None); + .struct_span_error("Invalid import asname", self.token.span); None } } @@ -602,7 +602,6 @@ impl<'a> Parser<'a> { self.sess.struct_span_error( "'else if' here is invalid in KCL, consider using the 'elif' keyword", self.token.span, - None, ); } else if self.token.kind != TokenKind::Colon { self.sess @@ -1035,7 +1034,6 @@ impl<'a> Parser<'a> { self.sess.struct_span_error( "duplicate schema index signature definitions, only one is allowed in the schema", token.span, - None, ); } body_index_signature = @@ -1052,7 +1050,6 @@ impl<'a> Parser<'a> { Into::::into(self.token) ), self.token.span, - None, ); } @@ -1587,7 +1584,6 @@ impl<'a> Parser<'a> { this.sess.struct_span_error( "string interpolation expression can not be empty", Span::new(start_pos, end_pos), - None, ); } @@ -1624,7 +1620,6 @@ impl<'a> Parser<'a> { this.sess.struct_span_error( "invalid joined string spec without #", parser.token.span, - None, ); } // Whether there is syntax error or not, bump the joined string spec token. @@ -1642,7 +1637,6 @@ impl<'a> Parser<'a> { parser.sess.struct_span_error( &format!("invalid string interpolation expression: '{src}'"), Span::new(lo, hi), - None, ) } @@ -1680,7 +1674,7 @@ impl<'a> Parser<'a> { continue; } else { self.sess - .struct_span_error("invalid joined string", self.token.span, None,); + .struct_span_error("invalid joined string", self.token.span); joined_value .values .push(node_ref!(Expr::StringLit(StringLit { @@ -1709,4 +1703,4 @@ impl<'a> Parser<'a> { Some(joined_value) } -} +} \ No newline at end of file diff --git a/kclvm/parser/src/session/mod.rs b/kclvm/parser/src/session/mod.rs index 870be1cfa..d898cd2b1 100644 --- a/kclvm/parser/src/session/mod.rs +++ b/kclvm/parser/src/session/mod.rs @@ -53,11 +53,25 @@ impl ParseSession { /// Struct and report an error based on a span and not abort the compiler process. #[inline] - pub fn struct_span_error(&self, msg: &str, span: Span, fix_info: Option) { + pub fn struct_span_error(&self, msg: &str, span: Span) { self.add_parse_err(ParseError::Message { message: msg.to_string(), span, - fix_info, + fix_info:None, + }); + } + + #[inline] + pub fn struct_span_error_with_suggestions(&self, msg: &str, span: Span, suggestion_text: Option, replacement_text:Option) { + let fix_info = suggestion_text.map(|text| FixInfo { + suggestion: Some(text), + replacement:replacement_text, + additional_info: None, + }); + self.add_parse_err(ParseError::Message { + message: msg.to_string(), + span, + fix_info }); } @@ -89,4 +103,4 @@ impl ParseSession { pub fn classification(&self) -> (IndexSet, IndexSet) { self.1.borrow().classification() } -} +} \ No newline at end of file From 7b57a284cd7c0bd893e2f858c7330c02a1e994b1 Mon Sep 17 00:00:00 2001 From: Shashank Mittal Date: Wed, 22 May 2024 21:11:34 +0530 Subject: [PATCH 5/7] some quick fixes completed Signed-off-by: Shashank Mittal --- kclvm/error/src/lib.rs | 36 ++++++---- kclvm/parser/src/lexer/indent.rs | 2 +- kclvm/parser/src/lexer/mod.rs | 70 +++++++++++++----- kclvm/parser/src/parser/expr.rs | 2 +- kclvm/parser/src/parser/mod.rs | 2 +- kclvm/parser/src/parser/stmt.rs | 6 +- kclvm/parser/src/session/mod.rs | 102 ++++++++++++++++++++++++--- kclvm/tools/src/LSP/src/quick_fix.rs | 27 +++++-- 8 files changed, 197 insertions(+), 50 deletions(-) diff --git a/kclvm/error/src/lib.rs b/kclvm/error/src/lib.rs index 3c79bd58b..2c47b6c7f 100644 --- a/kclvm/error/src/lib.rs +++ b/kclvm/error/src/lib.rs @@ -337,15 +337,14 @@ pub enum ParseError { Message { message: String, span: Span, - fix_info: Option + fix_info: Option, }, } #[derive(Debug, Clone)] -pub struct FixInfo{ +pub struct FixInfo { pub suggestion: Option, pub replacement: Option, - pub additional_info: Option, } /// A single string error. @@ -365,8 +364,12 @@ impl ParseError { } /// New a message parse error with span. - pub fn message(message: String, span: Span, fix_info:Option) -> Self { - ParseError::Message { message, span, fix_info } + pub fn message(message: String, span: Span, fix_info: Option) -> Self { + ParseError::Message { + message, + span, + fix_info, + } } } @@ -380,12 +383,15 @@ impl ParseError { 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()) - ]) - } + 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()), + ]), _ => None, }; Ok(Diagnostic::new_with_code( @@ -422,7 +428,11 @@ 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, + 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"))); @@ -566,4 +576,4 @@ pub fn err_to_str(err: Box) -> String { } else { "".to_string() } -} \ No newline at end of file +} diff --git a/kclvm/parser/src/lexer/indent.rs b/kclvm/parser/src/lexer/indent.rs index 577f0521d..f53ee8ff3 100644 --- a/kclvm/parser/src/lexer/indent.rs +++ b/kclvm/parser/src/lexer/indent.rs @@ -166,4 +166,4 @@ impl<'a> Lexer<'a> { } self.indent_cxt.indents.last().unwrap() } -} \ No newline at end of file +} diff --git a/kclvm/parser/src/lexer/mod.rs b/kclvm/parser/src/lexer/mod.rs index 0d4b55320..39d46fc5f 100644 --- a/kclvm/parser/src/lexer/mod.rs +++ b/kclvm/parser/src/lexer/mod.rs @@ -262,7 +262,7 @@ impl<'a> Lexer<'a> { "invalid token '!', consider using 'not'", self.span(start, self.pos), Some("Replace '!' with 'not'".to_string()), - Some("not".to_string()), + Some("not ".to_string()), ); token::UnaryOp(token::UNot) } @@ -326,17 +326,21 @@ impl<'a> Lexer<'a> { token::OpenDelim(token::Paren) => token::CloseDelim(token::Paren), // error recovery token::OpenDelim(token::Brace) => { - self.sess.struct_span_error( + self.sess.struct_span_error_with_suggestions( "error nesting on close paren", self.span(start, self.pos), + Some("Replace with '}'".to_string()), + Some("}".to_string()), ); token::CloseDelim(token::Brace) } // error recovery token::OpenDelim(token::Bracket) => { - self.sess.struct_span_error( + self.sess.struct_span_error_with_suggestions( "error nesting on close paren", self.span(start, self.pos), + Some("Replace with ']'".to_string()), + Some("]".to_string()), ); token::CloseDelim(token::Bracket) } @@ -345,9 +349,11 @@ impl<'a> Lexer<'a> { }, // error recovery None => { - self.sess.struct_span_error( + self.sess.struct_span_error_with_suggestions( "error nesting on close paren", self.span(start, self.pos), + Some("Insert ')'".to_string()), + Some(")".to_string()), ); token::CloseDelim(token::Paren) } @@ -363,17 +369,21 @@ impl<'a> Lexer<'a> { token::OpenDelim(token::Brace) => token::CloseDelim(token::Brace), // error recovery token::OpenDelim(token::Paren) => { - self.sess.struct_span_error( + self.sess.struct_span_error_with_suggestions( "error nesting on close brace", self.span(start, self.pos), + Some("Replace with ')'".to_string()), + Some(")".to_string()), ); token::CloseDelim(token::Paren) } // error recovery token::OpenDelim(token::Bracket) => { - self.sess.struct_span_error( + self.sess.struct_span_error_with_suggestions( "error nesting on close brace", self.span(start, self.pos), + Some("Replace with ']'".to_string()), + Some("]".to_string()), ); token::CloseDelim(token::Bracket) } @@ -382,9 +392,11 @@ impl<'a> Lexer<'a> { }, // error recovery None => { - self.sess.struct_span_error( + self.sess.struct_span_error_with_suggestions( "error nesting on close brace", self.span(start, self.pos), + Some("Insert '}'".to_string()), + Some("}".to_string()), ); token::CloseDelim(token::Brace) } @@ -402,17 +414,21 @@ impl<'a> Lexer<'a> { token::OpenDelim(token::Bracket) => token::CloseDelim(token::Bracket), // error recovery token::OpenDelim(token::Brace) => { - self.sess.struct_span_error( + self.sess.struct_span_error_with_suggestions( "mismatched closing delimiter", self.span(start, self.pos), + Some("Replace with '}'".to_string()), + Some("}".to_string()), ); token::CloseDelim(token::Brace) } // error recovery token::OpenDelim(token::Paren) => { - self.sess.struct_span_error( + self.sess.struct_span_error_with_suggestions( "mismatched closing delimiter", self.span(start, self.pos), + Some("Replace with ')'".to_string()), + Some(")".to_string()), ); token::CloseDelim(token::Paren) } @@ -421,9 +437,11 @@ impl<'a> Lexer<'a> { }, // error recovery None => { - self.sess.struct_span_error( + self.sess.struct_span_error_with_suggestions( "mismatched closing delimiter", self.span(start, self.pos), + Some("Insert ']'".to_string()), + Some("]".to_string()), ); token::CloseDelim(token::Bracket) } @@ -432,23 +450,31 @@ impl<'a> Lexer<'a> { kclvm_lexer::TokenKind::InvalidLineContinue => { // If we encounter an illegal line continuation character, // we will restore it to a normal line continuation character. - self.sess.struct_span_error( + self.sess.struct_span_error_with_suggestions( "unexpected character after line continuation character", self.span(start, self.pos), + Some("Replace with '\\'".to_string()), + Some("\\".to_string()), ); return None; } kclvm_lexer::TokenKind::Semi => { // If we encounter an illegal semi token ';', raise a friendly error. - self.sess.struct_span_error( + self.sess.struct_span_error_with_suggestions( "the semicolon ';' here is unnecessary, please remove it", self.span(start, self.pos), + Some("Remove ';'".to_string()), + Some("".to_string()), ); return None; } _ => { - self.sess - .struct_span_error("unknown start of token", self.span(start, self.pos)); + self.sess.struct_span_error_with_suggestions( + "unknown start of token", + self.span(start, self.pos), + Some("Remove unknown token".to_string()), + Some("".to_string()), + ); return None; } }) @@ -505,10 +531,12 @@ impl<'a> Lexer<'a> { _ => (false, start, start_char), }; if !terminated { - self.sess.struct_span_error( + self.sess.struct_span_error_with_suggestions( "unterminated string", self.span(quote_char_pos, self.pos), - ) + Some("Close the string with matching quote".to_string()), + Some("\"".to_string()), + ); } // Cut offset before validation. let offset: u32 = if triple_quoted { @@ -536,9 +564,11 @@ impl<'a> Lexer<'a> { let value = if content_start > content_end { // If get an error string from the eval process, // directly return an empty string. - self.sess.struct_span_error( + self.sess.struct_span_error_with_suggestions( "invalid string syntax", self.span(content_start, self.pos), + Some("Correct the string syntax".to_string()), + Some("\"\"".to_string()), ); "".to_string() } else { @@ -549,9 +579,11 @@ impl<'a> Lexer<'a> { None => { // If get an error string from the eval process, // directly return an empty string. - self.sess.struct_span_error( + self.sess.struct_span_error_with_suggestions( "invalid string syntax", self.span(content_start, self.pos), + Some("Correct the string syntax".to_string()), + Some("\"\"".to_string()), ); "".to_string() } @@ -758,4 +790,4 @@ impl TokenStreamBuilder { None => &TokenKind::Dummy, } } -} \ No newline at end of file +} diff --git a/kclvm/parser/src/parser/expr.rs b/kclvm/parser/src/parser/expr.rs index 8d509195e..9283f2d28 100644 --- a/kclvm/parser/src/parser/expr.rs +++ b/kclvm/parser/src/parser/expr.rs @@ -2424,4 +2424,4 @@ impl<'a> Parser<'a> { expr.into_missing_identifier().node } } -} \ No newline at end of file +} diff --git a/kclvm/parser/src/parser/mod.rs b/kclvm/parser/src/parser/mod.rs index b15840dde..e90b0ad85 100644 --- a/kclvm/parser/src/parser/mod.rs +++ b/kclvm/parser/src/parser/mod.rs @@ -225,4 +225,4 @@ impl<'a> Parser<'a> { (non_comment_tokens, comments) } -} \ No newline at end of file +} diff --git a/kclvm/parser/src/parser/stmt.rs b/kclvm/parser/src/parser/stmt.rs index b76130342..4f4fe9669 100644 --- a/kclvm/parser/src/parser/stmt.rs +++ b/kclvm/parser/src/parser/stmt.rs @@ -599,9 +599,11 @@ impl<'a> Parser<'a> { // `else if -> elif` error recovery. if self.token.is_keyword(kw::If) { - self.sess.struct_span_error( + self.sess.struct_span_error_with_suggestions( "'else if' here is invalid in KCL, consider using the 'elif' keyword", self.token.span, + Some("Use 'elif' instead of 'else if'".to_string()), + Some("elif".to_string()), ); } else if self.token.kind != TokenKind::Colon { self.sess @@ -1703,4 +1705,4 @@ impl<'a> Parser<'a> { Some(joined_value) } -} \ No newline at end of file +} diff --git a/kclvm/parser/src/session/mod.rs b/kclvm/parser/src/session/mod.rs index d898cd2b1..7140dfc7e 100644 --- a/kclvm/parser/src/session/mod.rs +++ b/kclvm/parser/src/session/mod.rs @@ -57,24 +57,110 @@ impl ParseSession { self.add_parse_err(ParseError::Message { message: msg.to_string(), span, - fix_info:None, + fix_info: None, }); } #[inline] - pub fn struct_span_error_with_suggestions(&self, msg: &str, span: Span, suggestion_text: Option, replacement_text:Option) { - let fix_info = suggestion_text.map(|text| FixInfo { - suggestion: Some(text), - replacement:replacement_text, - additional_info: None, + pub fn struct_span_error_with_suggestions( + &self, + msg: &str, + span: Span, + suggestion_text: Option, + replacement_text: Option, + ) { + let code_line = self.get_line_content_from_span(span); + let modified_code_line = + self.generate_modified_code_line(msg, &code_line, replacement_text.as_deref()); + + let fix_info = Some(FixInfo { + suggestion: suggestion_text, + replacement: Some(modified_code_line), }); + self.add_parse_err(ParseError::Message { message: msg.to_string(), span, - fix_info + fix_info, }); } + fn generate_modified_code_line( + &self, + msg: &str, + code_line: &str, + replacement_text: Option<&str>, + ) -> String { + match msg { + "invalid token '!', consider using 'not'" => { + if let Some(replacement) = replacement_text { + code_line.replace("!", replacement) + } else { + code_line.replace("!", "not ") + } + } + "'else if' here is invalid in KCL, consider using the 'elif' keyword" => { + if let Some(replacement) = replacement_text { + code_line.replace("else if", replacement) + } else { + code_line.replace("else if", "elif") + } + } + "error nesting on close paren" + | "mismatched closing delimiter" + | "error nesting on close brace" => { + let mismatched_delim = self.find_mismatched_delimiter(code_line); + if let Some(replacement) = replacement_text { + code_line.replace(&mismatched_delim, replacement) + } else { + code_line.to_string() + } + } + "unterminated string" => { + if let Some(replacement) = replacement_text { + format!("{}{}", code_line, replacement) + } else { + format!("{}\"", code_line) + } + } + "unexpected character after line continuation character" => { + if let Some(replacement) = replacement_text { + if let Some(pos) = code_line.find('\\') { + format!("{}{}", &code_line[..=pos - 1], replacement) + } else { + code_line.to_string() + } + } else { + code_line.to_string() + } + } + "the semicolon ';' here is unnecessary, please remove it" => code_line.replace(";", ""), + _ => code_line.to_string(), + } + } + + fn find_mismatched_delimiter(&self, code_line: &str) -> String { + if code_line.contains(")") { + ")".to_string() + } else if code_line.contains("]") { + "]".to_string() + } else if code_line.contains("}") { + "}".to_string() + } else { + "".to_string() + } + } + + /// Extracts the content of the line corresponding to the given span. + fn get_line_content_from_span(&self, span: Span) -> String { + let source_file = self.0.sm.lookup_source_file(span.lo()); + let line_index = source_file.lookup_line(span.lo()).unwrap(); + source_file + .get_line(line_index) + .unwrap_or_else(|| "unknown line".into()) + .to_string() + } + /// Add a error into the session. #[inline] fn add_parse_err(&self, err: ParseError) { @@ -103,4 +189,4 @@ impl ParseSession { pub fn classification(&self) -> (IndexSet, IndexSet) { self.1.borrow().classification() } -} \ No newline at end of file +} diff --git a/kclvm/tools/src/LSP/src/quick_fix.rs b/kclvm/tools/src/LSP/src/quick_fix.rs index 4d19ec0e7..f0ccf549d 100644 --- a/kclvm/tools/src/LSP/src/quick_fix.rs +++ b/kclvm/tools/src/LSP/src/quick_fix.rs @@ -42,18 +42,35 @@ pub(crate) fn quick_fix(uri: &Url, diags: &Vec) -> Vec { let replacement_texts = extract_suggested_replacements(&diag.data); - for replacement_text in replacement_texts { + if replacement_texts.len() >= 2 { + let title = &replacement_texts[0]; + let new_code_line = &replacement_texts[1]; + let mut changes = HashMap::new(); + + // Create a new range that covers the entire line + let line_range = lsp_types::Range { + start: lsp_types::Position { + line: diag.range.start.line, + character: 0, + }, + end: lsp_types::Position { + line: diag.range.start.line, + character: u32::MAX, // Ensure it covers to the end of the line + }, + }; + + // Add the new text with a newline character at the end changes.insert( uri.clone(), vec![TextEdit { - range: diag.range, - new_text: replacement_text.clone(), + range: line_range, + new_text: format!("{}", new_code_line), }], ); + code_actions.push(CodeActionOrCommand::CodeAction(CodeAction { - title: "Split multiple assignment into separate assignments" - .to_string(), + title: title.clone(), kind: Some(CodeActionKind::QUICKFIX), diagnostics: Some(vec![diag.clone()]), edit: Some(lsp_types::WorkspaceEdit { From 9d11488836abc5aedbc3737e95917be9d6f62428 Mon Sep 17 00:00:00 2001 From: Shashank Mittal Date: Fri, 24 May 2024 20:07:50 +0530 Subject: [PATCH 6/7] changes implemented Signed-off-by: Shashank Mittal --- kclvm/error/src/lib.rs | 106 ++++++++++++++++++++++++++- kclvm/parser/src/lexer/mod.rs | 2 +- kclvm/parser/src/session/mod.rs | 82 +-------------------- kclvm/tools/src/LSP/src/quick_fix.rs | 19 +---- 4 files changed, 108 insertions(+), 101 deletions(-) diff --git a/kclvm/error/src/lib.rs b/kclvm/error/src/lib.rs index 2c47b6c7f..f8f8b581d 100644 --- a/kclvm/error/src/lib.rs +++ b/kclvm/error/src/lib.rs @@ -374,7 +374,7 @@ impl ParseError { } 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 { let span = match self { ParseError::UnexpectedToken { span, .. } => span, @@ -390,21 +390,121 @@ impl ParseError { info.suggestion .clone() .unwrap_or_else(|| "No suggestion available".to_string()), - info.replacement.clone().unwrap_or_else(|| "".to_string()), + info.replacement.clone().unwrap_or_else(|| " ".to_string()), ]), _ => None, }; + + let (start_pos, end_pos) = self.generate_modified_range(&self.to_string(), &pos); + Ok(Diagnostic::new_with_code( Level::Error, &self.to_string(), None, - (pos.clone(), pos), + (start_pos, end_pos), Some(DiagnosticId::Error(ErrorKind::InvalidSyntax)), 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 { fn to_string(&self) -> String { match self { diff --git a/kclvm/parser/src/lexer/mod.rs b/kclvm/parser/src/lexer/mod.rs index 39d46fc5f..f175c7901 100644 --- a/kclvm/parser/src/lexer/mod.rs +++ b/kclvm/parser/src/lexer/mod.rs @@ -464,7 +464,7 @@ impl<'a> Lexer<'a> { "the semicolon ';' here is unnecessary, please remove it", self.span(start, self.pos), Some("Remove ';'".to_string()), - Some("".to_string()), + Some(" ".to_string()), ); return None; } diff --git a/kclvm/parser/src/session/mod.rs b/kclvm/parser/src/session/mod.rs index 7140dfc7e..3095a58f4 100644 --- a/kclvm/parser/src/session/mod.rs +++ b/kclvm/parser/src/session/mod.rs @@ -69,13 +69,9 @@ impl ParseSession { suggestion_text: Option, replacement_text: Option, ) { - let code_line = self.get_line_content_from_span(span); - let modified_code_line = - self.generate_modified_code_line(msg, &code_line, replacement_text.as_deref()); - let fix_info = Some(FixInfo { suggestion: suggestion_text, - replacement: Some(modified_code_line), + replacement: replacement_text, }); self.add_parse_err(ParseError::Message { @@ -85,82 +81,6 @@ impl ParseSession { }); } - fn generate_modified_code_line( - &self, - msg: &str, - code_line: &str, - replacement_text: Option<&str>, - ) -> String { - match msg { - "invalid token '!', consider using 'not'" => { - if let Some(replacement) = replacement_text { - code_line.replace("!", replacement) - } else { - code_line.replace("!", "not ") - } - } - "'else if' here is invalid in KCL, consider using the 'elif' keyword" => { - if let Some(replacement) = replacement_text { - code_line.replace("else if", replacement) - } else { - code_line.replace("else if", "elif") - } - } - "error nesting on close paren" - | "mismatched closing delimiter" - | "error nesting on close brace" => { - let mismatched_delim = self.find_mismatched_delimiter(code_line); - if let Some(replacement) = replacement_text { - code_line.replace(&mismatched_delim, replacement) - } else { - code_line.to_string() - } - } - "unterminated string" => { - if let Some(replacement) = replacement_text { - format!("{}{}", code_line, replacement) - } else { - format!("{}\"", code_line) - } - } - "unexpected character after line continuation character" => { - if let Some(replacement) = replacement_text { - if let Some(pos) = code_line.find('\\') { - format!("{}{}", &code_line[..=pos - 1], replacement) - } else { - code_line.to_string() - } - } else { - code_line.to_string() - } - } - "the semicolon ';' here is unnecessary, please remove it" => code_line.replace(";", ""), - _ => code_line.to_string(), - } - } - - fn find_mismatched_delimiter(&self, code_line: &str) -> String { - if code_line.contains(")") { - ")".to_string() - } else if code_line.contains("]") { - "]".to_string() - } else if code_line.contains("}") { - "}".to_string() - } else { - "".to_string() - } - } - - /// Extracts the content of the line corresponding to the given span. - fn get_line_content_from_span(&self, span: Span) -> String { - let source_file = self.0.sm.lookup_source_file(span.lo()); - let line_index = source_file.lookup_line(span.lo()).unwrap(); - source_file - .get_line(line_index) - .unwrap_or_else(|| "unknown line".into()) - .to_string() - } - /// Add a error into the session. #[inline] fn add_parse_err(&self, err: ParseError) { diff --git a/kclvm/tools/src/LSP/src/quick_fix.rs b/kclvm/tools/src/LSP/src/quick_fix.rs index f0ccf549d..1d19ac7e8 100644 --- a/kclvm/tools/src/LSP/src/quick_fix.rs +++ b/kclvm/tools/src/LSP/src/quick_fix.rs @@ -44,28 +44,15 @@ pub(crate) fn quick_fix(uri: &Url, diags: &Vec) -> Vec= 2 { let title = &replacement_texts[0]; - let new_code_line = &replacement_texts[1]; + let replacement_text = &replacement_texts[1]; let mut changes = HashMap::new(); - // Create a new range that covers the entire line - let line_range = lsp_types::Range { - start: lsp_types::Position { - line: diag.range.start.line, - character: 0, - }, - end: lsp_types::Position { - line: diag.range.start.line, - character: u32::MAX, // Ensure it covers to the end of the line - }, - }; - - // Add the new text with a newline character at the end changes.insert( uri.clone(), vec![TextEdit { - range: line_range, - new_text: format!("{}", new_code_line), + range: diag.range, + new_text: replacement_text.clone(), }], ); From f2b3bd2380033c51fb1ae00f1ff012457d9bc260 Mon Sep 17 00:00:00 2001 From: Shashank Mittal Date: Fri, 24 May 2024 20:12:36 +0530 Subject: [PATCH 7/7] fmt Signed-off-by: Shashank Mittal --- kclvm/error/src/lib.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/kclvm/error/src/lib.rs b/kclvm/error/src/lib.rs index f8f8b581d..36fabb3ce 100644 --- a/kclvm/error/src/lib.rs +++ b/kclvm/error/src/lib.rs @@ -407,11 +407,7 @@ impl ParseError { )) } - fn generate_modified_range( - &self, - msg: &str, - pos: &Position, - ) -> (Position, Position) { + 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); @@ -504,7 +500,6 @@ impl ParseError { } } - impl ToString for ParseError { fn to_string(&self) -> String { match self {