Avoid panics for implicitly concatenated forward references (#3700)

This commit is contained in:
Charlie Marsh 2023-03-23 19:13:50 -04:00 committed by GitHub
parent 028329854b
commit 0f95056f13
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 28 additions and 24 deletions

View file

@ -8,3 +8,6 @@ def f() -> "A":
def g() -> "///": def g() -> "///":
pass pass
X: """List[int]"""'' = []

View file

@ -5216,7 +5216,8 @@ impl<'a> Checker<'a> {
continue; 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 { let docstring = Docstring {
kind: definition.kind, kind: definition.kind,
expr, expr,

View file

@ -30,7 +30,7 @@ pub fn remove_unused_format_arguments_from_dict(
!matches!(e, DictElement::Simple { !matches!(e, DictElement::Simple {
key: Expression::SimpleString(name), 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 { let mut state = CodegenState {

View file

@ -15,4 +15,17 @@ expression: diagnostics
column: 16 column: 16
fix: ~ fix: ~
parent: ~ 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: ~

View file

@ -17,25 +17,13 @@ pub const SINGLE_QUOTE_BYTE_PREFIXES: &[&str] = &[
const TRIPLE_QUOTE_SUFFIXES: &[&str] = &["\"\"\"", "'''"]; const TRIPLE_QUOTE_SUFFIXES: &[&str] = &["\"\"\"", "'''"];
const SINGLE_QUOTE_SUFFIXES: &[&str] = &["\"", "'"]; const SINGLE_QUOTE_SUFFIXES: &[&str] = &["\"", "'"];
/// Strip the leading and trailing quotes from a docstring. /// Strip the leading and trailing quotes from a string.
pub fn raw_contents(contents: &str) -> &str { /// Assumes that the string is a valid string literal, but does not verify that the string
for pattern in TRIPLE_QUOTE_STR_PREFIXES /// is a "simple" string literal (i.e., that it does not contain any implicit concatenations).
.iter() pub fn raw_contents(contents: &str) -> Option<&str> {
.chain(TRIPLE_QUOTE_BYTE_PREFIXES) let leading_quote_str = leading_quote(contents)?;
{ let trailing_quote_str = trailing_quote(contents)?;
if contents.starts_with(pattern) { Some(&contents[leading_quote_str.len()..contents.len() - trailing_quote_str.len()])
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")
} }
/// Return the leading quote for a string or byte literal (e.g., `"""`). /// Return the leading quote for a string or byte literal (e.g., `"""`).

View file

@ -93,15 +93,14 @@ pub fn parse_type_annotation(
locator: &Locator, locator: &Locator,
) -> Result<(Expr, AnnotationKind)> { ) -> Result<(Expr, AnnotationKind)> {
let expression = locator.slice(range); let expression = locator.slice(range);
let body = str::raw_contents(expression); if str::raw_contents(expression).map_or(false, |body| body == value) {
if body == value {
// The annotation is considered "simple" if and only if the raw representation (e.g., // 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 // `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 // isn't the case, e.g., for implicit concatenations, or for annotations that contain
// escaped quotes. // escaped quotes.
let leading_quote = str::leading_quote(expression).unwrap(); let leading_quote = str::leading_quote(expression).unwrap();
let expr = parser::parse_expression_located( let expr = parser::parse_expression_located(
body, value,
"<filename>", "<filename>",
Location::new( Location::new(
range.location.row(), range.location.row(),