r263713 - clang-format: [JS] Make requoting of JavaScript string literals only

Daniel Jasper via cfe-commits cfe-commits at lists.llvm.org
Thu Mar 17 06:03:42 PDT 2016


Author: djasper
Date: Thu Mar 17 08:03:41 2016
New Revision: 263713

URL: http://llvm.org/viewvc/llvm-project?rev=263713&view=rev
Log:
clang-format: [JS] Make requoting of JavaScript string literals only
change affected ranges.

Modified:
    cfe/trunk/lib/Format/Format.cpp
    cfe/trunk/unittests/Format/FormatTestSelective.cpp

Modified: cfe/trunk/lib/Format/Format.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Format/Format.cpp?rev=263713&r1=263712&r2=263713&view=diff
==============================================================================
--- cfe/trunk/lib/Format/Format.cpp (original)
+++ cfe/trunk/lib/Format/Format.cpp Thu Mar 17 08:03:41 2016
@@ -777,13 +777,13 @@ namespace {
 class FormatTokenLexer {
 public:
   FormatTokenLexer(SourceManager &SourceMgr, FileID ID, FormatStyle &Style,
-                   encoding::Encoding Encoding, tooling::Replacements &Replaces)
+                   encoding::Encoding Encoding)
       : FormatTok(nullptr), IsFirstToken(true), GreaterStashed(false),
         LessStashed(false), Column(0), TrailingWhitespace(0),
         SourceMgr(SourceMgr), ID(ID), Style(Style),
         IdentTable(getFormattingLangOpts(Style)), Keywords(IdentTable),
-        Encoding(Encoding), Replaces(Replaces), FirstInLineIndex(0),
-        FormattingDisabled(false), MacroBlockBeginRegex(Style.MacroBlockBegin),
+        Encoding(Encoding), FirstInLineIndex(0), FormattingDisabled(false),
+        MacroBlockBeginRegex(Style.MacroBlockBegin),
         MacroBlockEndRegex(Style.MacroBlockEnd) {
     Lex.reset(new Lexer(ID, SourceMgr.getBuffer(ID), SourceMgr,
                         getFormattingLangOpts(Style)));
@@ -802,8 +802,6 @@ public:
       if (Style.Language == FormatStyle::LK_JavaScript)
         tryParseJSRegexLiteral();
       tryMergePreviousTokens();
-      if (Style.Language == FormatStyle::LK_JavaScript)
-        tryRequoteJSStringLiteral();
       if (Tokens.back()->NewlinesBefore > 0 || Tokens.back()->IsMultiline)
         FirstInLineIndex = Tokens.size() - 1;
     } while (Tokens.back()->Tok.isNot(tok::eof));
@@ -1074,75 +1072,6 @@ private:
     return false;
   }
 
-  // If the last token is a double/single-quoted string literal, generates a
-  // replacement with a single/double quoted string literal, re-escaping the
-  // contents in the process.
-  void tryRequoteJSStringLiteral() {
-    if (Style.JavaScriptQuotes == FormatStyle::JSQS_Leave)
-      return;
-
-    FormatToken *FormatTok = Tokens.back();
-    StringRef Input = FormatTok->TokenText;
-    if (!FormatTok->isStringLiteral() ||
-        // NB: testing for not starting with a double quote to avoid breaking
-        // `template strings`.
-        (Style.JavaScriptQuotes == FormatStyle::JSQS_Single &&
-         !Input.startswith("\"")) ||
-        (Style.JavaScriptQuotes == FormatStyle::JSQS_Double &&
-         !Input.startswith("\'")))
-      return;
-
-    // Change start and end quote.
-    bool IsSingle = Style.JavaScriptQuotes == FormatStyle::JSQS_Single;
-    SourceLocation Start = FormatTok->Tok.getLocation();
-    auto Replace = [&](SourceLocation Start, unsigned Length,
-                       StringRef ReplacementText) {
-      Replaces.insert(
-          tooling::Replacement(SourceMgr, Start, Length, ReplacementText));
-    };
-    Replace(Start, 1, IsSingle ? "'" : "\"");
-    Replace(FormatTok->Tok.getEndLoc().getLocWithOffset(-1), 1,
-            IsSingle ? "'" : "\"");
-
-    // Escape internal quotes.
-    size_t ColumnWidth = FormatTok->TokenText.size();
-    bool Escaped = false;
-    for (size_t i = 1; i < Input.size() - 1; i++) {
-      switch (Input[i]) {
-      case '\\':
-        if (!Escaped && i + 1 < Input.size() &&
-            ((IsSingle && Input[i + 1] == '"') ||
-             (!IsSingle && Input[i + 1] == '\''))) {
-          // Remove this \, it's escaping a " or ' that no longer needs escaping
-          ColumnWidth--;
-          Replace(Start.getLocWithOffset(i), 1, "");
-          continue;
-        }
-        Escaped = !Escaped;
-        break;
-      case '\"':
-      case '\'':
-        if (!Escaped && IsSingle == (Input[i] == '\'')) {
-          // Escape the quote.
-          Replace(Start.getLocWithOffset(i), 0, "\\");
-          ColumnWidth++;
-        }
-        Escaped = false;
-        break;
-      default:
-        Escaped = false;
-        break;
-      }
-    }
-
-    // For formatting, count the number of non-escaped single quotes in them
-    // and adjust ColumnWidth to take the added escapes into account.
-    // FIXME(martinprobst): this might conflict with code breaking a long string
-    // literal (which clang-format doesn't do, yet). For that to work, this code
-    // would have to modify TokenText directly.
-    FormatTok->ColumnWidth = ColumnWidth;
-  }
-
   bool tryMerge_TMacro() {
     if (Tokens.size() < 4)
       return false;
@@ -1441,7 +1370,6 @@ private:
   IdentifierTable IdentTable;
   AdditionalKeywords Keywords;
   encoding::Encoding Encoding;
-  tooling::Replacements &Replaces;
   llvm::SpecificBumpPtrAllocator<FormatToken> Allocator;
   // Index (in 'Tokens') of the last token that starts a new line.
   unsigned FirstInLineIndex;
@@ -1531,7 +1459,7 @@ public:
 
   tooling::Replacements format(bool *IncompleteFormat) {
     tooling::Replacements Result;
-    FormatTokenLexer Tokens(SourceMgr, ID, Style, Encoding, Result);
+    FormatTokenLexer Tokens(SourceMgr, ID, Style, Encoding);
 
     UnwrappedLineParser Parser(Style, Tokens.getKeywords(), Tokens.lex(),
                                *this);
@@ -1545,7 +1473,7 @@ public:
         AnnotatedLines.push_back(new AnnotatedLine(UnwrappedLines[Run][i]));
       }
       tooling::Replacements RunResult =
-          format(AnnotatedLines, Tokens, IncompleteFormat);
+          format(AnnotatedLines, Tokens, Result, IncompleteFormat);
       DEBUG({
         llvm::dbgs() << "Replacements for run " << Run << ":\n";
         for (tooling::Replacements::iterator I = RunResult.begin(),
@@ -1565,16 +1493,21 @@ public:
 
   tooling::Replacements format(SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
                                FormatTokenLexer &Tokens,
+                               tooling::Replacements &Result,
                                bool *IncompleteFormat) {
     TokenAnnotator Annotator(Style, Tokens.getKeywords());
     for (unsigned i = 0, e = AnnotatedLines.size(); i != e; ++i) {
       Annotator.annotate(*AnnotatedLines[i]);
     }
     deriveLocalStyle(AnnotatedLines);
+    computeAffectedLines(AnnotatedLines.begin(), AnnotatedLines.end());
+    if (Style.Language == FormatStyle::LK_JavaScript &&
+        Style.JavaScriptQuotes != FormatStyle::JSQS_Leave)
+      requoteJSStringLiteral(AnnotatedLines, Result);
+
     for (unsigned i = 0, e = AnnotatedLines.size(); i != e; ++i) {
       Annotator.calculateFormattingInformation(*AnnotatedLines[i]);
     }
-    computeAffectedLines(AnnotatedLines.begin(), AnnotatedLines.end());
 
     Annotator.setCommentLineLevels(AnnotatedLines);
     ContinuationIndenter Indenter(Style, Tokens.getKeywords(), SourceMgr,
@@ -1625,6 +1558,83 @@ private:
     }
     return SomeLineAffected;
   }
+ 
+  // If the last token is a double/single-quoted string literal, generates a
+  // replacement with a single/double quoted string literal, re-escaping the
+  // contents in the process.
+  void requoteJSStringLiteral(SmallVectorImpl<AnnotatedLine *> &Lines,
+                                 tooling::Replacements &Result) {
+    for (AnnotatedLine *Line : Lines) {
+      requoteJSStringLiteral(Line->Children, Result);
+      if (!Line->Affected)
+        continue;
+      for (FormatToken *FormatTok = Line->First; FormatTok;
+           FormatTok = FormatTok->Next) {
+        StringRef Input = FormatTok->TokenText;
+        if (!FormatTok->isStringLiteral() ||
+            // NB: testing for not starting with a double quote to avoid
+            // breaking
+            // `template strings`.
+            (Style.JavaScriptQuotes == FormatStyle::JSQS_Single &&
+             !Input.startswith("\"")) ||
+            (Style.JavaScriptQuotes == FormatStyle::JSQS_Double &&
+             !Input.startswith("\'")))
+          continue;
+
+        // Change start and end quote.
+        bool IsSingle = Style.JavaScriptQuotes == FormatStyle::JSQS_Single;
+        SourceLocation Start = FormatTok->Tok.getLocation();
+        auto Replace = [&](SourceLocation Start, unsigned Length,
+                           StringRef ReplacementText) {
+          Result.insert(
+              tooling::Replacement(SourceMgr, Start, Length, ReplacementText));
+        };
+        Replace(Start, 1, IsSingle ? "'" : "\"");
+        Replace(FormatTok->Tok.getEndLoc().getLocWithOffset(-1), 1,
+                IsSingle ? "'" : "\"");
+
+        // Escape internal quotes.
+        size_t ColumnWidth = FormatTok->TokenText.size();
+        bool Escaped = false;
+        for (size_t i = 1; i < Input.size() - 1; i++) {
+          switch (Input[i]) {
+            case '\\':
+              if (!Escaped && i + 1 < Input.size() &&
+                  ((IsSingle && Input[i + 1] == '"') ||
+                   (!IsSingle && Input[i + 1] == '\''))) {
+                // Remove this \, it's escaping a " or ' that no longer needs
+                // escaping
+                ColumnWidth--;
+                Replace(Start.getLocWithOffset(i), 1, "");
+                continue;
+              }
+              Escaped = !Escaped;
+              break;
+            case '\"':
+            case '\'':
+              if (!Escaped && IsSingle == (Input[i] == '\'')) {
+                // Escape the quote.
+                Replace(Start.getLocWithOffset(i), 0, "\\");
+                ColumnWidth++;
+              }
+              Escaped = false;
+              break;
+            default:
+              Escaped = false;
+              break;
+          }
+        }
+
+        // For formatting, count the number of non-escaped single quotes in them
+        // and adjust ColumnWidth to take the added escapes into account.
+        // FIXME(martinprobst): this might conflict with code breaking a long string
+        // literal (which clang-format doesn't do, yet). For that to work, this code
+        // would have to modify TokenText directly.
+        FormatTok->ColumnWidth = ColumnWidth;
+      }
+    }
+  }
+
 
   // Determines whether 'Line' is affected by the SourceRanges given as input.
   // Returns \c true if line or one if its children is affected.

Modified: cfe/trunk/unittests/Format/FormatTestSelective.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Format/FormatTestSelective.cpp?rev=263713&r1=263712&r2=263713&view=diff
==============================================================================
--- cfe/trunk/unittests/Format/FormatTestSelective.cpp (original)
+++ cfe/trunk/unittests/Format/FormatTestSelective.cpp Thu Mar 17 08:03:41 2016
@@ -512,6 +512,18 @@ TEST_F(FormatTestSelective, StopFormatti
              15, 0));
 }
 
+TEST_F(FormatTestSelective, SelectivelyRequoteJavaScript) {
+  Style = getGoogleStyle(FormatStyle::LK_JavaScript);
+  EXPECT_EQ(
+      "var x = \"a\";\n"
+      "var x = 'a';\n"
+      "var x = \"a\";",
+      format("var x = \"a\";\n"
+             "var x = \"a\";\n"
+             "var x = \"a\";",
+             20, 0));
+}
+
 } // end namespace
 } // end namespace format
 } // end namespace clang




More information about the cfe-commits mailing list