def test_error_string_literal(self):
- self._check_error("'blech", "unterminated string literal")
- self._check_error('"blech', "unterminated string literal")
+ self._check_error("'blech", r"unterminated string literal \(.*\)$")
+ self._check_error('"blech', r"unterminated string literal \(.*\)$")
+ self._check_error(
+ r'"blech\"', r"unterminated string literal \(.*\); perhaps you escaped the end quote"
+ )
+ self._check_error(
+ r'r"blech\"', r"unterminated string literal \(.*\); perhaps you escaped the end quote"
+ )
self._check_error("'''blech", "unterminated triple-quoted string literal")
self._check_error('"""blech', "unterminated triple-quoted string literal")
int quote = c;
int quote_size = 1; /* 1 or 3 */
int end_quote_size = 0;
+ int has_escaped_quote = 0;
/* Nodes of type STRING, especially multi line strings
must be handled differently in order to get both
return MAKE_TOKEN(ERRORTOKEN);
}
else {
- _PyTokenizer_syntaxerror(tok, "unterminated string literal (detected at"
- " line %d)", start);
+ if (has_escaped_quote) {
+ _PyTokenizer_syntaxerror(
+ tok,
+ "unterminated string literal (detected at line %d); "
+ "perhaps you escaped the end quote?",
+ start
+ );
+ } else {
+ _PyTokenizer_syntaxerror(
+ tok, "unterminated string literal (detected at line %d)", start
+ );
+ }
if (c != '\n') {
tok->done = E_EOLS;
}
end_quote_size = 0;
if (c == '\\') {
c = tok_nextc(tok); /* skip escaped char */
+ if (c == quote) { /* but record whether the escaped char was a quote */
+ has_escaped_quote = 1;
+ }
if (c == '\r') {
c = tok_nextc(tok);
}