mprobst updated this revision to Diff 49562.
mprobst marked 6 inline comments as done.
mprobst added a comment.
- Move code closer together by piping Replacements into the FormatTokenLexer.
- remove sstream import
- * Treat JavaScript single quoted string literals as tok::string_literal, not
- Address review comments, mostly renaming things.
http://reviews.llvm.org/D17385
Files:
include/clang/Format/Format.h
lib/Format/Format.cpp
unittests/Format/FormatTest.cpp
unittests/Format/FormatTestJS.cpp
Index: unittests/Format/FormatTestJS.cpp
===================================================================
--- unittests/Format/FormatTestJS.cpp
+++ unittests/Format/FormatTestJS.cpp
@@ -250,7 +250,7 @@
verifyFormat("f({'a': [{}]});");
}
-TEST_F(FormatTestJS, SingleQuoteStrings) {
+TEST_F(FormatTestJS, SingleQuotedStrings) {
verifyFormat("this.function('', true);");
}
@@ -636,7 +636,8 @@
TEST_F(FormatTestJS, StringLiteralConcatenation) {
verifyFormat("var literal = 'hello ' +\n"
- " 'world';");
+ " 'worldworldworldworld';",
+ getGoogleJSStyleWithColumns(30));
}
TEST_F(FormatTestJS, RegexLiteralClassification) {
@@ -874,7 +875,7 @@
verifyFormat("import {\n"
" X,\n"
" Y,\n"
- "} from 'some/long/module.js';",
+ "} from\n 'some/long/module.js';",
getGoogleJSStyleWithColumns(20));
verifyFormat("import {X as myLocalX, Y as myLocalY} from 'some/module.js';");
verifyFormat("import * as lib from 'some/module.js';");
@@ -1085,5 +1086,36 @@
getGoogleJSStyleWithColumns(20)));
}
+TEST_F(FormatTestJS, RequoteStringsSingle) {
+ EXPECT_EQ("var x = 'foo';", format("var x = \"foo\";"));
+ EXPECT_EQ("var x = 'fo\\'o\\'';", format("var x = \"fo'o'\";"));
+ EXPECT_EQ("var x = 'fo\\'o\\'';", format("var x = \"fo\\'o'\";"));
+ EXPECT_EQ("var x =\n"
+ " 'foo\\'';",
+ // Code below is 15 chars wide, doesn't fit into the line with the
+ // \ escape added.
+ format("var x = \"foo'\";", getGoogleJSStyleWithColumns(15)));
+ // Removes no-longer needed \ escape from ".
+ EXPECT_EQ("var x = 'fo\"o';", format("var x = \"fo\\\"o\";"));
+ // Code below fits into 15 chars *after* removing the \ escape.
+ EXPECT_EQ("var x = 'fo\"o';",
+ format("var x = \"fo\\\"o\";", getGoogleJSStyleWithColumns(15)));
+}
+
+TEST_F(FormatTestJS, RequoteStringsDouble) {
+ FormatStyle DoubleQuotes = getGoogleStyle(FormatStyle::LK_JavaScript);
+ DoubleQuotes.JavaScriptQuotes = FormatStyle::JSQS_Double;
+ verifyFormat("var x = \"foo\";", DoubleQuotes);
+ EXPECT_EQ("var x = \"foo\";", format("var x = 'foo';", DoubleQuotes));
+ EXPECT_EQ("var x = \"fo'o\";", format("var x = 'fo\\'o';", DoubleQuotes));
+}
+
+TEST_F(FormatTestJS, RequoteStringsLeave) {
+ FormatStyle LeaveQuotes = getGoogleStyle(FormatStyle::LK_JavaScript);
+ LeaveQuotes.JavaScriptQuotes = FormatStyle::JSQS_Leave;
+ verifyFormat("var x = \"foo\";", LeaveQuotes);
+ verifyFormat("var x = 'foo';", LeaveQuotes);
+}
+
} // end namespace tooling
} // end namespace clang
Index: unittests/Format/FormatTest.cpp
===================================================================
--- unittests/Format/FormatTest.cpp
+++ unittests/Format/FormatTest.cpp
@@ -6642,7 +6642,7 @@
" bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb};");
verifyNoCrash("a<,");
-
+
// No braced initializer here.
verifyFormat("void f() {\n"
" struct Dummy {};\n"
Index: lib/Format/Format.cpp
===================================================================
--- lib/Format/Format.cpp
+++ lib/Format/Format.cpp
@@ -71,6 +71,14 @@
}
};
+template <> struct ScalarEnumerationTraits<FormatStyle::JavaScriptQuoteStyle> {
+ static void enumeration(IO &IO, FormatStyle::JavaScriptQuoteStyle &Value) {
+ IO.enumCase(Value, "Leave", FormatStyle::JSQS_Leave);
+ IO.enumCase(Value, "Single", FormatStyle::JSQS_Single);
+ IO.enumCase(Value, "Double", FormatStyle::JSQS_Double);
+ }
+};
+
template <> struct ScalarEnumerationTraits<FormatStyle::ShortFunctionStyle> {
static void enumeration(IO &IO, FormatStyle::ShortFunctionStyle &Value) {
IO.enumCase(Value, "None", FormatStyle::SFS_None);
@@ -335,6 +343,7 @@
IO.mapOptional("Standard", Style.Standard);
IO.mapOptional("TabWidth", Style.TabWidth);
IO.mapOptional("UseTab", Style.UseTab);
+ IO.mapOptional("JavaScriptQuotes", Style.JavaScriptQuotes);
}
};
@@ -522,6 +531,7 @@
LLVMStyle.SpacesBeforeTrailingComments = 1;
LLVMStyle.Standard = FormatStyle::LS_Cpp11;
LLVMStyle.UseTab = FormatStyle::UT_Never;
+ LLVMStyle.JavaScriptQuotes = FormatStyle::JSQS_Leave;
LLVMStyle.ReflowComments = true;
LLVMStyle.SpacesInParentheses = false;
LLVMStyle.SpacesInSquareBrackets = false;
@@ -590,6 +600,7 @@
GoogleStyle.CommentPragmas = "@(export|see|visibility) ";
GoogleStyle.MaxEmptyLinesToKeep = 3;
GoogleStyle.SpacesInContainerLiterals = false;
+ GoogleStyle.JavaScriptQuotes = FormatStyle::JSQS_Single;
} else if (Language == FormatStyle::LK_Proto) {
GoogleStyle.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None;
GoogleStyle.SpacesInContainerLiterals = false;
@@ -766,13 +777,13 @@
class FormatTokenLexer {
public:
FormatTokenLexer(SourceManager &SourceMgr, FileID ID, FormatStyle &Style,
- encoding::Encoding Encoding)
+ encoding::Encoding Encoding, tooling::Replacements &Replaces)
: 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), FirstInLineIndex(0), FormattingDisabled(false),
- MacroBlockBeginRegex(Style.MacroBlockBegin),
+ Encoding(Encoding), Replaces(Replaces), FirstInLineIndex(0),
+ FormattingDisabled(false), MacroBlockBeginRegex(Style.MacroBlockBegin),
MacroBlockEndRegex(Style.MacroBlockEnd) {
Lex.reset(new Lexer(ID, SourceMgr.getBuffer(ID), SourceMgr,
getFormattingLangOpts(Style)));
@@ -791,6 +802,8 @@
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));
@@ -1061,6 +1074,67 @@
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;
+
+ char Quote = Style.JavaScriptQuotes == FormatStyle::JSQS_Single ? '\'' : '\"';
+
+ size_t ColumnWidth = FormatTok->TokenText.size();
+ std::string Res(1, Quote);
+ llvm::raw_string_ostream Out(Res);
+ bool Escaped = false;
+ for (size_t i = 1; i < Input.size() - 1; i++) {
+ switch (Input[i]) {
+ case '\\':
+ if (!Escaped && i + 1 < Input.size() &&
+ ((Quote == '\'' && Input[i + 1] == '"') ||
+ (Quote == '"' && Input[i + 1] == '\''))) {
+ // Skip this \, it's escaping a " or ' that no longer needs escaping.
+ ColumnWidth--;
+ continue;
+ }
+ Escaped = !Escaped;
+ break;
+ case '\"':
+ case '\'':
+ if (Input[i] == Quote && !Escaped) {
+ Out << '\\';
+ ColumnWidth++;
+ }
+ Escaped = false;
+ break;
+ default:
+ Escaped = false;
+ break;
+ }
+ Out << Input[i];
+ }
+ Out << Quote;
+
+ // For formatting, count the number of non-escaped single quotes in them
+ // and adjust ColumnWidth to take the added escapes into account.
+ FormatTok->ColumnWidth = ColumnWidth;
+
+ SourceRange Range(FormatTok->Tok.getLocation(), FormatTok->Tok.getEndLoc());
+ Replaces.insert(tooling::Replacement(
+ SourceMgr, CharSourceRange::getCharRange(Range), Out.str()));
+ }
+
bool tryMerge_TMacro() {
if (Tokens.size() < 4)
return false;
@@ -1359,6 +1433,7 @@
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;
@@ -1382,10 +1457,15 @@
Tok.IsUnterminatedLiteral = true;
} else if (Style.Language == FormatStyle::LK_JavaScript &&
Tok.TokenText == "''") {
- Tok.Tok.setKind(tok::char_constant);
+ Tok.Tok.setKind(tok::string_literal);
}
}
+ if (Style.Language == FormatStyle::LK_JavaScript &&
+ Tok.is(tok::char_constant)) {
+ Tok.Tok.setKind(tok::string_literal);
+ }
+
if (Tok.is(tok::comment) && (Tok.TokenText == "// clang-format on" ||
Tok.TokenText == "/* clang-format on */")) {
FormattingDisabled = false;
@@ -1443,7 +1523,7 @@
tooling::Replacements format(bool *IncompleteFormat) {
tooling::Replacements Result;
- FormatTokenLexer Tokens(SourceMgr, ID, Style, Encoding);
+ FormatTokenLexer Tokens(SourceMgr, ID, Style, Encoding, Result);
UnwrappedLineParser Parser(Style, Tokens.getKeywords(), Tokens.lex(),
*this);
Index: include/clang/Format/Format.h
===================================================================
--- include/clang/Format/Format.h
+++ include/clang/Format/Format.h
@@ -591,6 +591,20 @@
/// \brief The way to use tab characters in the resulting file.
UseTabStyle UseTab;
+ /// \brief Quotation styles for JavaScript strings. Does not affect template
+ /// strings.
+ enum JavaScriptQuoteStyle {
+ /// Leave string quotes as they are.
+ JSQS_Leave,
+ /// Always use single quotes.
+ JSQS_Single,
+ /// Always use double quotes.
+ JSQS_Double
+ };
+
+ /// \brief The JavaScriptQuoteStyle to use for JavaScript strings.
+ JavaScriptQuoteStyle JavaScriptQuotes;
+
bool operator==(const FormatStyle &R) const {
return AccessModifierOffset == R.AccessModifierOffset &&
AlignAfterOpenBracket == R.AlignAfterOpenBracket &&
@@ -667,7 +681,8 @@
SpacesInParentheses == R.SpacesInParentheses &&
SpacesInSquareBrackets == R.SpacesInSquareBrackets &&
Standard == R.Standard && TabWidth == R.TabWidth &&
- UseTab == R.UseTab;
+ UseTab == R.UseTab &&
+ JavaScriptQuotes == R.JavaScriptQuotes;
}
};
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits