mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 10:44:36 +08:00
Fix interpolated string warning diagnostic locations (#9314)
* Use closing-token locations for interpolated body warnings Track CPython string-parser fragment anchors separately from escape source offsets. Preserve scoped error-line text and mode-specific final newlines with lazy sparse location indexing. Assisted-by: OpenAI Codex:model-version-unavailable * Fix lint checks for interpolated warning diagnostics Assisted-by: OpenAI Codex (model version unavailable) * Match CPython interpolated warning phases and fragment diagnostics Reconstruct body and format-spec decoder fragments, preserve tokenizer/parser action order and pending Single/type-comment errors, and match Unicode protection plus scoped numeric and ordinary-token diagnostics. Assisted-by: OpenAI Codex:model-version-unavailable * Use physical final-line locations for ordinary exec warnings Keep warning and error coordinates consistent across bare CR, CRLF, and LF while retaining file-input synthetic newlines. Assisted-by: OpenAI Codex:model-version-unavailable
This commit is contained in:
1 parent
1cd1b5d09f
commit
a788de29da
3 files changed
+844
-113
No files matched your search
@@ -1233,6 +1233,44 @@ fn invalid_type_comment_syntax_error(
|
||||
None
|
||||
}
|
||||
|
||||
/// The parser phase where a rejected TYPE_COMMENT stops grammar actions.
|
||||
/// Use the validator's token-backed location, not a textual comment search.
|
||||
pub(crate) fn invalid_type_comment_warning_boundary(
|
||||
source_file: &SourceFile,
|
||||
parsed: &parser::Parsed<ast::Mod>,
|
||||
interactive: bool,
|
||||
) -> Option<usize> {
|
||||
let comments = TypeCommentSource::new(source_file.source_text(), parsed.tokens());
|
||||
match invalid_type_comment_syntax_error(source_file, &comments)? {
|
||||
CompileError::Parse(error) => {
|
||||
let offset = error.raw_location.start().to_usize();
|
||||
(!interactive || type_comment_precedes_single_boundary(offset, source_file, parsed))
|
||||
.then_some(offset)
|
||||
}
|
||||
CompileError::Codegen(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn type_comment_precedes_single_boundary(
|
||||
offset: usize,
|
||||
source_file: &SourceFile,
|
||||
parsed: &parser::Parsed<ast::Mod>,
|
||||
) -> bool {
|
||||
use rustpython_compiler::SingleModeStatementBoundary;
|
||||
match rustpython_compiler::single_mode_statement_boundary(parsed) {
|
||||
Some(SingleModeStatementBoundary::Simple { end, .. }) => offset < end.to_usize(),
|
||||
Some(SingleModeStatementBoundary::Compound { .. }) => {
|
||||
// Compound input still requests NEWLINE after the statement. A
|
||||
// rejected comment may be that next token, before the next AST node.
|
||||
match rustpython_compiler::single_mode_multiple_statements_error(source_file, parsed) {
|
||||
Some(CompileError::Parse(error)) => offset < error.raw_location.start().to_usize(),
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
None => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn feature_version_syntax_error(
|
||||
source: &str,
|
||||
source_file: &SourceFile,
|
||||
@@ -1769,6 +1807,7 @@ pub(crate) fn parse<E: From<CompileError>>(
|
||||
interactive: bool,
|
||||
explicit_future_features: crate::bytecode::CodeFlags,
|
||||
dont_imply_dedent: bool,
|
||||
type_comment_error_offset: Option<&mut Option<usize>>,
|
||||
mut emit_warning: impl FnMut(usize, char) -> Result<(), E>,
|
||||
) -> Result<PyObjectRef, E> {
|
||||
let source_file = SourceFileBuilder::new(filename.to_owned(), source.to_owned()).finish();
|
||||
@@ -1785,17 +1824,38 @@ pub(crate) fn parse<E: From<CompileError>>(
|
||||
);
|
||||
rustpython_compiler::pre_parse_source_error(&source_file)?;
|
||||
let parsed = parser::parse_unchecked(barry_source.source(), options);
|
||||
let type_comment_source =
|
||||
type_comments.then(|| TypeCommentSource::new(source, parsed.tokens()));
|
||||
let mut type_comment_error = type_comment_source
|
||||
.as_ref()
|
||||
.and_then(|lines| invalid_type_comment_syntax_error(&source_file, lines));
|
||||
if interactive && parsed.has_valid_syntax() {
|
||||
// A rejected comment before the first logical-input boundary prevents
|
||||
// Simple input from succeeding. Compound input still needs NEWLINE,
|
||||
// so compare its actual next-token error rather than its AST end.
|
||||
type_comment_error = type_comment_error.filter(|error| match error {
|
||||
CompileError::Parse(error) => type_comment_precedes_single_boundary(
|
||||
error.raw_location.start().to_usize(),
|
||||
&source_file,
|
||||
&parsed,
|
||||
),
|
||||
CompileError::Codegen(_) => false,
|
||||
});
|
||||
}
|
||||
if interactive
|
||||
&& !(parsed.has_valid_syntax() && type_comment_error.is_some())
|
||||
&& let Some(error) =
|
||||
rustpython_compiler::single_mode_multiple_statements_error(&source_file, &parsed)
|
||||
{
|
||||
return Err(error.into());
|
||||
}
|
||||
let type_comment_source =
|
||||
type_comments.then(|| TypeCommentSource::new(source, parsed.tokens()));
|
||||
if let Some(lines) = &type_comment_source
|
||||
&& let Some(error) = invalid_type_comment_syntax_error(&source_file, lines)
|
||||
{
|
||||
if let Some(error) = type_comment_error {
|
||||
if parsed.has_valid_syntax()
|
||||
&& let Some(offset) = type_comment_error_offset
|
||||
&& let CompileError::Parse(error) = &error
|
||||
{
|
||||
*offset = Some(error.raw_location.start().to_usize());
|
||||
}
|
||||
return Err(error.into());
|
||||
}
|
||||
if let Err(errors) = parsed.as_result() {
|
||||
|
||||
@@ -260,6 +260,9 @@ fn warn_invalid_escape_sequences_in_format_spec<E>(
|
||||
}
|
||||
true
|
||||
}
|
||||
// CPython's string-parser prepass protects a backslash before a
|
||||
// non-ASCII codepoint before invoking the escape decoder.
|
||||
ch if !ch.is_ascii() => true,
|
||||
_ => false,
|
||||
};
|
||||
if !valid {
|
||||
|
||||
+776
-108
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user