-
Notifications
You must be signed in to change notification settings - Fork 1.4k
[syntax-errors] Start detecting compile-time syntax errors #16106
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
Changes from 25 commits
c66f37a
825f971
4e702dd
877a108
af9d9dd
6f90709
ab2db92
c4a5cc7
34b5829
6cc9198
508d785
9c38e2d
da6d31d
33b5381
419b5cd
662ffc3
63276ef
ca582a5
f461718
67eb290
38ee75d
dc1a94a
edbc46c
6e131f6
659788a
7d00d50
c3417be
d6d3437
7144a82
ffdb7cb
16479d4
ef11801
4cfaad0
f1b2222
ae17231
a363c6b
d48afb7
7ffa680
c482a02
88cbb9b
5f7de2a
e8d44f1
7171e20
077bd37
f484a85
de0914b
3ccea07
0b62bde
452283a
b5f828b
ab89b04
79ddb2f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ use std::path::Path; | |
use anyhow::{anyhow, Result}; | ||
use colored::Colorize; | ||
use itertools::Itertools; | ||
use ruff_python_syntax_errors::SyntaxError; | ||
use rustc_hash::FxHashMap; | ||
|
||
use ruff_diagnostics::Diagnostic; | ||
|
@@ -77,6 +78,9 @@ pub fn check_path( | |
// Aggregate all diagnostics. | ||
let mut diagnostics = vec![]; | ||
|
||
// Aggregate all semantic syntax errors. | ||
let mut semantic_syntax_errors = vec![]; | ||
|
||
let tokens = parsed.tokens(); | ||
let comment_ranges = indexer.comment_ranges(); | ||
|
||
|
@@ -148,7 +152,7 @@ pub fn check_path( | |
let cell_offsets = source_kind.as_ipy_notebook().map(Notebook::cell_offsets); | ||
let notebook_index = source_kind.as_ipy_notebook().map(Notebook::index); | ||
if use_ast { | ||
diagnostics.extend(check_ast( | ||
let (new_diagnostics, new_semantic_syntax_errors) = check_ast( | ||
parsed, | ||
locator, | ||
stylist, | ||
|
@@ -162,7 +166,9 @@ pub fn check_path( | |
cell_offsets, | ||
notebook_index, | ||
target_version, | ||
)); | ||
); | ||
diagnostics.extend(new_diagnostics); | ||
semantic_syntax_errors.extend(new_semantic_syntax_errors); | ||
} | ||
if use_imports { | ||
let import_diagnostics = check_imports( | ||
|
@@ -327,10 +333,17 @@ pub fn check_path( | |
&[] | ||
}; | ||
|
||
let semantic_syntax_errors = if settings.preview.is_enabled() { | ||
semantic_syntax_errors.as_slice() | ||
} else { | ||
&[] | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's move this check into There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah I see what you mean about it being awkward. I thought I could just do something like if self.settings.preview.is_enabled() {
self.syntax_checker.visit_stmt(stmt);
} But this loses F404 detection when preview is disabled. For now I moved this into I don't think there's any panic-prone code in here, but I'll look back over it with that in mind. If we keep the context trait, the |
||
|
||
diagnostics_to_messages( | ||
diagnostics, | ||
parsed.errors(), | ||
syntax_errors, | ||
semantic_syntax_errors, | ||
path, | ||
locator, | ||
directives, | ||
|
@@ -445,8 +458,8 @@ pub fn lint_only( | |
); | ||
|
||
LinterResult { | ||
has_syntax_error: messages.iter().any(Message::is_syntax_error), | ||
messages, | ||
has_syntax_error: parsed.has_syntax_errors(), | ||
} | ||
} | ||
|
||
|
@@ -455,6 +468,7 @@ fn diagnostics_to_messages( | |
diagnostics: Vec<Diagnostic>, | ||
parse_errors: &[ParseError], | ||
unsupported_syntax_errors: &[UnsupportedSyntaxError], | ||
semantic_syntax_errors: &[SyntaxError], | ||
path: &Path, | ||
locator: &Locator, | ||
directives: &Directives, | ||
|
@@ -476,6 +490,11 @@ fn diagnostics_to_messages( | |
.chain(unsupported_syntax_errors.iter().map(|syntax_error| { | ||
Message::from_unsupported_syntax_error(syntax_error, file.deref().clone()) | ||
})) | ||
.chain( | ||
semantic_syntax_errors | ||
.iter() | ||
.map(|error| Message::from_semantic_syntax_error(error, file.deref().clone())), | ||
) | ||
.chain(diagnostics.into_iter().map(|diagnostic| { | ||
let noqa_offset = directives.noqa_line_for.resolve(diagnostic.start()); | ||
Message::from_diagnostic(diagnostic, file.deref().clone(), noqa_offset) | ||
|
@@ -547,7 +566,7 @@ pub fn lint_fix<'a>( | |
); | ||
|
||
if iterations == 0 { | ||
is_valid_syntax = parsed.has_no_syntax_errors(); | ||
is_valid_syntax = !messages.iter().any(Message::is_syntax_error); | ||
} else { | ||
// If the source code was parseable on the first pass, but is no | ||
// longer parseable on a subsequent pass, then we've introduced a | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe:
To clarify that it doesn't implement other syntax errors? We could also use
semantic_syntax_errors
but it feels very long.Another alternative is to implement the checks inside the parser crate. Or does the crate have dependencies that would prevent us from doing that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moving it to the parser makes a lot of sense to me. I moved it to a public
ruff_python_parser::semantic_errors
module. Another possible location would be a private submodule of theerror
module (ruff_python_parser::error::semantic_errors
or maybe justsemantic
at the end?) and then we could re-export the new related types like we do forParseError
andUnsupportedSyntaxError
. MaybeUnsupportedSyntaxError
should have had its ownerror
submodule too.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine with any of it but I myself would go with
semantic_errors
because it's short and simple.