/// each \c FormatToken.
void replaceWhitespace(const FormatToken &Tok, unsigned NewLines,
unsigned Spaces) {
+ std::string NewLineText;
+ if (!Line.InPPDirective) {
+ NewLineText = std::string(NewLines, '\n');
+ } else if (NewLines > 0) {
+ unsigned Offset =
+ SourceMgr.getSpellingColumnNumber(Tok.WhiteSpaceStart) - 1;
+ for (unsigned i = 0; i < NewLines; ++i) {
+ NewLineText += std::string(Style.ColumnLimit - Offset - 1, ' ');
+ NewLineText += "\\\n";
+ Offset = 0;
+ }
+ }
Replaces.insert(tooling::Replacement(
SourceMgr, Tok.WhiteSpaceStart, Tok.WhiteSpaceLength,
- std::string(NewLines, '\n') + std::string(Spaces, ' ')));
+ NewLineText + std::string(Spaces, ' ')));
}
/// \brief Add a new line and the required indent before the first Token
next();
if (Index >= Tokens.size())
return;
+ // It is the responsibility of the UnwrappedLineParser to make sure
+ // this sequence is not produced inside an unwrapped line.
+ assert(Tokens[Index].Tok.getIdentifierInfo() != NULL);
switch (Tokens[Index].Tok.getIdentifierInfo()->getPPKeywordID()) {
case tok::pp_include:
case tok::pp_import:
// Consume and record whitespace until we find a significant token.
while (FormatTok.Tok.is(tok::unknown)) {
- FormatTok.NewlinesBefore += tokenText(FormatTok.Tok).count('\n');
+ StringRef Text = tokenText(FormatTok.Tok);
+ FormatTok.NewlinesBefore += Text.count('\n');
+ FormatTok.HasUnescapedNewline =
+ Text.count("\\\n") != FormatTok.NewlinesBefore;
FormatTok.WhiteSpaceLength += FormatTok.Tok.getLength();
if (FormatTok.Tok.is(tok::eof))
}
void UnwrappedLineParser::parsePPDirective() {
- while (!eof()) {
- nextToken();
- if (FormatTok.NewlinesBefore > 0) {
- addUnwrappedLine();
- return;
- }
+ assert(FormatTok.Tok.is(tok::hash) && "'#' expected");
+ nextToken();
+
+ Line.InPPDirective = true;
+ if (FormatTok.Tok.getIdentifierInfo() == NULL) {
+ addUnwrappedLine();
+ Line.InPPDirective = false;
+ return;
}
+
+ do {
+ if (FormatTok.NewlinesBefore > 0 &&
+ FormatTok.HasUnescapedNewline) {
+ break;
+ }
+ nextToken();
+ } while (!eof());
+ addUnwrappedLine();
+ Line.InPPDirective = false;
}
void UnwrappedLineParser::parseComments() {
/// \brief A wrapper around a \c Token storing information about the
/// whitespace characters preceeding it.
struct FormatToken {
- FormatToken() : NewlinesBefore(0), WhiteSpaceLength(0) {
+ FormatToken()
+ : NewlinesBefore(0), HasUnescapedNewline(false), WhiteSpaceLength(0) {
}
/// \brief The \c Token.
/// and thereby e.g. leave an empty line between two function definitions.
unsigned NewlinesBefore;
+ /// \brief Whether there is at least one unescaped newline before the \c
+ /// Token.
+ bool HasUnescapedNewline;
+
/// \brief The location of the start of the whitespace immediately preceeding
/// the \c Token.
///
/// \c UnwrappedLineFormatter. The key property is that changing the formatting
/// within an unwrapped line does not affect any other unwrapped lines.
struct UnwrappedLine {
- UnwrappedLine() : Level(0) {
+ UnwrappedLine() : Level(0), InPPDirective(false) {
}
/// \brief The \c Token comprising this \c UnwrappedLine.
/// \brief The indent level of the \c UnwrappedLine.
unsigned Level;
+
+ /// \brief Whether this \c UnwrappedLine is part of a preprocessor directive.
+ bool InPPDirective;
};
class UnwrappedLineConsumer {
" \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\" };");
}
+TEST_F(FormatTest, FormatsSmallMacroDefinitionsInSingleLine) {
+ verifyFormat("#define ALooooooooooooooooooooooooooooooooooooooongMacro("
+ " \\\n"
+ " aLoooooooooooooooooooooooongFuuuuuuuuuuuuuunctiooooooooo)");
+}
+
+TEST_F(FormatTest, BreaksOnHashWhenDirectiveIsInvalid) {
+ EXPECT_EQ("#\n;", format("#;"));
+}
+
+TEST_F(FormatTest, UnescapedEndOfLineEndsPPDirective) {
+ EXPECT_EQ("#line 42 \"test\"\n",
+ format("# \\\n line \\\n 42 \\\n \"test\"\n"));
+ EXPECT_EQ("#define A B\n", format("# \\\n define \\\n A \\\n B\n"));
+}
+
+TEST_F(FormatTest, EndOfFileEndsPPDirective) {
+ EXPECT_EQ("#line 42 \"test\"",
+ format("# \\\n line \\\n 42 \\\n \"test\""));
+ EXPECT_EQ("#define A B", format("# \\\n define \\\n A \\\n B"));
+}
+
+TEST_F(FormatTest, MixingPreprocessorDirectivesAndNormalCode) {
+ verifyFormat("#define ALooooooooooooooooooooooooooooooooooooooongMacro("
+ " \\\n"
+ " aLoooooooooooooooooooooooongFuuuuuuuuuuuuuunctiooooooooo)\n"
+ "\n"
+ "AlooooooooooooooooooooooooooooooooooooooongCaaaaaaaaaal(\n"
+ " aLooooooooooooooooooooooonPaaaaaaaaaaaaaaaaaaaaarmmmm);\n");
+}
+
//===----------------------------------------------------------------------===//
// Line break tests.
//===----------------------------------------------------------------------===//