Skip to content

Commit

Permalink
Avoid panics for implictly concatenated forward references
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Mar 23, 2023
1 parent 1659de2 commit 661b796
Show file tree
Hide file tree
Showing 6 changed files with 28 additions and 24 deletions.
3 changes: 3 additions & 0 deletions crates/ruff/resources/test/fixtures/pyflakes/F722.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ def f() -> "A":

def g() -> "///":
pass


X: """List[int]"""'☃' = []
3 changes: 2 additions & 1 deletion crates/ruff/src/checkers/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5216,7 +5216,8 @@ impl<'a> Checker<'a> {
continue;
}

let body = str::raw_contents(contents);
// SAFETY: Safe for docstrings that pass `should_ignore_docstring`.
let body = str::raw_contents(contents).unwrap();
let docstring = Docstring {
kind: definition.kind,
expr,
Expand Down
2 changes: 1 addition & 1 deletion crates/ruff/src/rules/pyflakes/fixes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub fn remove_unused_format_arguments_from_dict(
!matches!(e, DictElement::Simple {
key: Expression::SimpleString(name),
..
} if unused_arguments.contains(&raw_contents(name.value)))
} if raw_contents(name.value).map_or(false, |name| unused_arguments.contains(&name)))
});

let mut state = CodegenState {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,17 @@ expression: diagnostics
column: 16
fix: ~
parent: ~
- kind:
name: ForwardAnnotationSyntaxError
body: "Syntax error in forward annotation: `List[int]☃`"
suggestion: ~
fixable: false
location:
row: 13
column: 3
end_location:
row: 13
column: 21
fix: ~
parent: ~

26 changes: 7 additions & 19 deletions crates/ruff_python_ast/src/str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,13 @@ pub const SINGLE_QUOTE_BYTE_PREFIXES: &[&str] = &[
const TRIPLE_QUOTE_SUFFIXES: &[&str] = &["\"\"\"", "'''"];
const SINGLE_QUOTE_SUFFIXES: &[&str] = &["\"", "'"];

/// Strip the leading and trailing quotes from a docstring.
pub fn raw_contents(contents: &str) -> &str {
for pattern in TRIPLE_QUOTE_STR_PREFIXES
.iter()
.chain(TRIPLE_QUOTE_BYTE_PREFIXES)
{
if contents.starts_with(pattern) {
return &contents[pattern.len()..contents.len() - 3];
}
}
for pattern in SINGLE_QUOTE_STR_PREFIXES
.iter()
.chain(SINGLE_QUOTE_BYTE_PREFIXES)
{
if contents.starts_with(pattern) {
return &contents[pattern.len()..contents.len() - 1];
}
}
unreachable!("Expected docstring to start with a valid triple- or single-quote prefix")
/// Strip the leading and trailing quotes from a string.
/// Assumes that the string is a valid string literal, but does not verify that the string
/// is a "simple" string literal (i.e., that it does not contain any implicit concatenations).
pub fn raw_contents(contents: &str) -> Option<&str> {
let leading_quote_str = leading_quote(contents)?;
let trailing_quote_str = trailing_quote(contents)?;
Some(&contents[leading_quote_str.len()..contents.len() - trailing_quote_str.len()])
}

/// Return the leading quote for a string or byte literal (e.g., `"""`).
Expand Down
5 changes: 2 additions & 3 deletions crates/ruff_python_ast/src/typing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,15 +93,14 @@ pub fn parse_type_annotation(
locator: &Locator,
) -> Result<(Expr, AnnotationKind)> {
let expression = locator.slice(range);
let body = str::raw_contents(expression);
if body == value {
if str::raw_contents(expression).map_or(false, |body| body == value) {
// The annotation is considered "simple" if and only if the raw representation (e.g.,
// `List[int]` within "List[int]") exactly matches the parsed representation. This
// isn't the case, e.g., for implicit concatenations, or for annotations that contain
// escaped quotes.
let leading_quote = str::leading_quote(expression).unwrap();
let expr = parser::parse_expression_located(
body,
value,
"<filename>",
Location::new(
range.location.row(),
Expand Down

0 comments on commit 661b796

Please sign in to comment.