From 98209be8aa8f3fe20a8f3e97d2d8ffcc9bc6c3be Mon Sep 17 00:00:00 2001 From: Martin Packman Date: Fri, 3 Mar 2023 04:59:33 +0000 Subject: [PATCH] Detect quote style ignoring docstrings (#3306) Currently the quote style of the first string in a file is used for autodetecting what to use when rewriting code for fixes. This is an okay heuristic, but often the first line in a file is a docstring, rather than a string constant, and it's not uncommon for pre-Black code to have different quoting styles for those. For example, in the Google style guide: https://google.github.io/styleguide/pyguide.html > Be consistent with your choice of string quote character within a file. Pick ' or " and stick with it. ... Docstrings must use """ regardless. This branch adjusts the logic to instead skip over any `"""` triple doublequote string tokens. The default, if there are no single quoted strings, is still to use double quote as the style. --- crates/ruff/src/source_code/stylist.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/crates/ruff/src/source_code/stylist.rs b/crates/ruff/src/source_code/stylist.rs index 8b4c726566..8bcf141be4 100644 --- a/crates/ruff/src/source_code/stylist.rs +++ b/crates/ruff/src/source_code/stylist.rs @@ -180,7 +180,9 @@ fn detect_quote(contents: &str, locator: &Locator) -> Option { if let Tok::String { .. } = tok { let content = locator.slice(&Range::new(start, end)); if let Some(pattern) = leading_quote(content) { - if pattern.contains('\'') { + if pattern.contains("\"\"\"") { + continue; + } else if pattern.contains('\'') { return Some(Quote::Single); } else if pattern.contains('"') { return Some(Quote::Double); @@ -276,13 +278,27 @@ x = ( let locator = Locator::new(contents); assert_eq!(detect_quote(contents, &locator), Some(Quote::Double)); + let contents = r#"s = "It's done.""#; + let locator = Locator::new(contents); + assert_eq!(detect_quote(contents, &locator), Some(Quote::Double)); + + // No style if only double quoted docstring (will take default Double) let contents = r#" def f(): """Docstring.""" pass "#; let locator = Locator::new(contents); - assert_eq!(detect_quote(contents, &locator), Some(Quote::Double)); + assert_eq!(detect_quote(contents, &locator), None); + + // Detect from string literal appearing after docstring + let contents = r#" +"""Module docstring.""" + +a = 'v' +"#; + let locator = Locator::new(contents); + assert_eq!(detect_quote(contents, &locator), Some(Quote::Single)); } #[test]