From: Daniel Jasper Date: Wed, 24 Jun 2015 16:01:02 +0000 (+0000) Subject: clang-format: [JS] Support regex literals containing quotes (' and "). X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=13e2ca85e71f1bff6012e802b3a9501def8fc7d5;p=clang clang-format: [JS] Support regex literals containing quotes (' and "). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@240548 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Format/Format.cpp b/lib/Format/Format.cpp index 0feeaa007f..5275f9f080 100644 --- a/lib/Format/Format.cpp +++ b/lib/Format/Format.cpp @@ -738,19 +738,33 @@ private: bool tryMergeJSRegexLiteral() { if (Tokens.size() < 2) return false; + + // If this is a string literal with a slash inside, compute the slash's + // offset and try to find the beginning of the regex literal. + // Also look at tok::unknown, as it can be an unterminated char literal. + size_t SlashInStringPos = StringRef::npos; + if (Tokens.back()->isOneOf(tok::string_literal, tok::char_constant, + tok::unknown)) { + // Start search from position 1 as otherwise, this is an unknown token + // for an unterminated /*-comment which is handled elsewhere. + SlashInStringPos = Tokens.back()->TokenText.find('/', 1); + if (SlashInStringPos == StringRef::npos) + return false; + } + // If a regex literal ends in "\//", this gets represented by an unknown // token "\" and a comment. bool MightEndWithEscapedSlash = Tokens.back()->is(tok::comment) && Tokens.back()->TokenText.startswith("//") && Tokens[Tokens.size() - 2]->TokenText == "\\"; - if (!MightEndWithEscapedSlash && + if (!MightEndWithEscapedSlash && SlashInStringPos == StringRef::npos && (Tokens.back()->isNot(tok::slash) || (Tokens[Tokens.size() - 2]->is(tok::unknown) && Tokens[Tokens.size() - 2]->TokenText == "\\"))) return false; + unsigned TokenCount = 0; - unsigned LastColumn = Tokens.back()->OriginalColumn; for (auto I = Tokens.rbegin() + 1, E = Tokens.rend(); I != E; ++I) { ++TokenCount; if (I[0]->isOneOf(tok::slash, tok::slashequal) && I + 1 != E && @@ -758,11 +772,17 @@ private: tok::exclaim, tok::l_square, tok::colon, tok::comma, tok::question, tok::kw_return) || I[1]->isBinaryOperator())) { + unsigned LastColumn = Tokens.back()->OriginalColumn; + SourceLocation Loc = Tokens.back()->Tok.getLocation(); if (MightEndWithEscapedSlash) { // This regex literal ends in '\//'. Skip past the '//' of the last // token and re-start lexing from there. - SourceLocation Loc = Tokens.back()->Tok.getLocation(); resetLexer(SourceMgr.getFileOffset(Loc) + 2); + } else if (SlashInStringPos != StringRef::npos) { + // This regex literal ends in a string_literal with a slash inside. + // Calculate end column and reset lexer appropriately. + resetLexer(SourceMgr.getFileOffset(Loc) + SlashInStringPos + 1); + LastColumn += SlashInStringPos; } Tokens.resize(Tokens.size() - TokenCount); Tokens.back()->Tok.setKind(tok::unknown); diff --git a/unittests/Format/FormatTestJS.cpp b/unittests/Format/FormatTestJS.cpp index 15d62eb66c..63bcfdc174 100644 --- a/unittests/Format/FormatTestJS.cpp +++ b/unittests/Format/FormatTestJS.cpp @@ -624,10 +624,16 @@ TEST_F(FormatTestJS, RegexLiteralSpecialCharacters) { verifyFormat("var regex = /\a\\//g;"); verifyFormat("var regex = /a\\//;\n" "var x = 0;"); + EXPECT_EQ("var regex = /'/g;", format("var regex = /'/g ;")); + EXPECT_EQ("var regex = /'/g; //'", format("var regex = /'/g ; //'")); EXPECT_EQ("var regex = /\\/*/;\n" "var x = 0;", format("var regex = /\\/*/;\n" "var x=0;")); + verifyFormat("var regex = /\"/;", getGoogleJSStyleWithColumns(16)); + verifyFormat("var regex =\n" + " /\"/;", + getGoogleJSStyleWithColumns(15)); } TEST_F(FormatTestJS, RegexLiteralModifiers) {