void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
unsigned ExtraSpaces) {
- const FormatToken &Current = *State.NextToken;
+ FormatToken &Current = *State.NextToken;
const FormatToken &Previous = *State.NextToken->Previous;
if (Current.is(tok::equal) &&
(State.Line->First->is(tok::kw_for) || State.ParenLevel == 0) &&
unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State,
bool DryRun) {
- const FormatToken &Current = *State.NextToken;
+ FormatToken &Current = *State.NextToken;
const FormatToken &Previous = *State.NextToken->Previous;
// If we are continuing an expression, we want to indent an extra 4 spaces.
unsigned ContinuationIndent =
unsigned Column;
/// \brief The token that needs to be next formatted.
- const FormatToken *NextToken;
+ FormatToken *NextToken;
/// \brief \c true if this line contains a continued for-loop section.
bool LineContainsContinuedForLoopSection;
// State already examined with lower penalty.
continue;
- addNextStateToQueue(Penalty, Node, /*NewLine=*/false);
- addNextStateToQueue(Penalty, Node, /*NewLine=*/true);
+ FormatDecision LastFormat = Node->State.NextToken->Decision;
+ if (LastFormat == FD_Unformatted || LastFormat == FD_Continue)
+ addNextStateToQueue(Penalty, Node, /*NewLine=*/false);
+ if (LastFormat == FD_Unformatted || LastFormat == FD_Break)
+ addNextStateToQueue(Penalty, Node, /*NewLine=*/true);
}
- if (Queue.empty())
+ if (Queue.empty()) {
// We were unable to find a solution, do nothing.
// FIXME: Add diagnostic?
+ DEBUG(llvm::dbgs() << "Could not find a solution.\n");
return 0;
+ }
// Reconstruct the solution.
if (!DryRun)
const std::vector<CharSourceRange> &Ranges)
: Style(Style), Lex(Lex), SourceMgr(SourceMgr),
Whitespaces(SourceMgr, Style, inputUsesCRLF(Lex.getBuffer())),
- Ranges(Ranges), Encoding(encoding::detectEncoding(Lex.getBuffer())) {
+ Ranges(Ranges), UnwrappedLines(1),
+ Encoding(encoding::detectEncoding(Lex.getBuffer())) {
DEBUG(llvm::dbgs() << "File encoding: "
<< (Encoding == encoding::Encoding_UTF8 ? "UTF8"
: "unknown")
<< "\n");
}
- virtual ~Formatter() {
- for (unsigned i = 0, e = AnnotatedLines.size(); i != e; ++i) {
- delete AnnotatedLines[i];
- }
- }
-
tooling::Replacements format() {
+ tooling::Replacements Result;
FormatTokenLexer Tokens(Lex, SourceMgr, Style, Encoding);
UnwrappedLineParser Parser(Style, Tokens.lex(), *this);
bool StructuralError = Parser.parse();
+ assert(UnwrappedLines.rbegin()->empty());
+ for (unsigned Run = 0, RunE = UnwrappedLines.size(); Run + 1 != RunE;
+ ++Run) {
+ DEBUG(llvm::dbgs() << "Run " << Run << "...\n");
+ SmallVector<AnnotatedLine *, 16> AnnotatedLines;
+ for (unsigned i = 0, e = UnwrappedLines[Run].size(); i != e; ++i) {
+ AnnotatedLines.push_back(new AnnotatedLine(UnwrappedLines[Run][i]));
+ }
+ tooling::Replacements RunResult =
+ format(AnnotatedLines, StructuralError, Tokens);
+ DEBUG({
+ llvm::dbgs() << "Replacements for run " << Run << ":\n";
+ for (tooling::Replacements::iterator I = RunResult.begin(),
+ E = RunResult.end();
+ I != E; ++I) {
+ llvm::dbgs() << I->toString() << "\n";
+ }
+ });
+ for (unsigned i = 0, e = AnnotatedLines.size(); i != e; ++i) {
+ delete AnnotatedLines[i];
+ }
+ Result.insert(RunResult.begin(), RunResult.end());
+ Whitespaces.reset();
+ }
+ return Result;
+ }
+
+ tooling::Replacements format(SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
+ bool StructuralError, FormatTokenLexer &Tokens) {
TokenAnnotator Annotator(Style, Tokens.getIdentTable().get("in"));
for (unsigned i = 0, e = AnnotatedLines.size(); i != e; ++i) {
Annotator.annotate(*AnnotatedLines[i]);
}
- deriveLocalStyle();
+ deriveLocalStyle(AnnotatedLines);
for (unsigned i = 0, e = AnnotatedLines.size(); i != e; ++i) {
Annotator.calculateFormattingInformation(*AnnotatedLines[i]);
}
} else {
// Format the first token if necessary, and notify the WhitespaceManager
// about the unchanged whitespace.
- for (const FormatToken *Tok = TheLine.First; Tok != NULL;
- Tok = Tok->Next) {
+ for (FormatToken *Tok = TheLine.First; Tok != NULL; Tok = Tok->Next) {
if (Tok == TheLine.First &&
(Tok->NewlinesBefore > 0 || Tok->IsFirst)) {
unsigned LevelIndent = Tok->OriginalColumn;
// last token.
PreviousLineWasTouched = false;
}
+ for (FormatToken *Tok = TheLine.First; Tok != NULL; Tok = Tok->Next) {
+ Tok->Finalized = true;
+ }
PreviousLine = *I;
}
return Whitespaces.generateReplacements();
return Text.count('\r') * 2 > Text.count('\n');
}
- void deriveLocalStyle() {
+ void
+ deriveLocalStyle(const SmallVectorImpl<AnnotatedLine *> &AnnotatedLines) {
unsigned CountBoundToVariable = 0;
unsigned CountBoundToType = 0;
bool HasCpp03IncompatibleFormat = false;
}
virtual void consumeUnwrappedLine(const UnwrappedLine &TheLine) {
- AnnotatedLines.push_back(new AnnotatedLine(TheLine));
+ assert(!UnwrappedLines.empty());
+ UnwrappedLines.back().push_back(TheLine);
+ }
+
+ virtual void finishRun() {
+ UnwrappedLines.push_back(SmallVector<UnwrappedLine, 16>());
}
/// \brief Add a new line and the required indent before the first Token
/// of the \c UnwrappedLine if there was no structural parsing error.
/// Returns the indent level of the \c UnwrappedLine.
- void formatFirstToken(const FormatToken &RootToken,
+ void formatFirstToken(FormatToken &RootToken,
const AnnotatedLine *PreviousLine, unsigned Indent,
bool InPPDirective) {
unsigned Newlines =
SourceManager &SourceMgr;
WhitespaceManager Whitespaces;
std::vector<CharSourceRange> Ranges;
- SmallVector<AnnotatedLine *, 16> AnnotatedLines;
+ SmallVector<SmallVector<UnwrappedLine, 16>, 2> UnwrappedLines;
encoding::Encoding Encoding;
bool BinPackInconclusiveFunctions;
bool HasRowWithSufficientColumns = false;
unsigned Column = 0;
for (unsigned i = 0, e = ItemLengths.size(); i != e; ++i) {
+ assert(i < MustBreakBeforeItem.size());
if (MustBreakBeforeItem[i] || Column == Columns) {
++Format.LineCount;
Column = 0;
PPK_Inconclusive
};
+enum FormatDecision {
+ FD_Unformatted,
+ FD_Continue,
+ FD_Break
+};
+
class TokenRole;
class AnnotatedLine;
LongestObjCSelectorName(0), FakeRParens(0),
StartsBinaryExpression(false), EndsBinaryExpression(false),
LastInChainOfCalls(false), PartOfMultiVariableDeclStmt(false),
- MatchingParen(NULL), Previous(NULL), Next(NULL) {}
+ MatchingParen(NULL), Previous(NULL), Next(NULL),
+ Decision(FD_Unformatted), Finalized(false) {}
/// \brief The \c Token.
Token Tok;
SmallVector<AnnotatedLine *, 1> Children;
+ /// \brief Stores the formatting decision for the token once it was made.
+ FormatDecision Decision;
+
+ /// \brief If \c true, this token has been fully formatted (indented and
+ /// potentially re-formatted inside), and we do not allow further formatting
+ /// changes.
+ bool Finalized;
+
private:
// Disallow copying.
FormatToken(const FormatToken &) LLVM_DELETED_FUNCTION;
if (CurrentToken != NULL)
CurrentToken = CurrentToken->Next;
- // Reset token type in case we have already looked at it and then recovered
- // from an error (e.g. failure to find the matching >).
- if (CurrentToken != NULL && CurrentToken->Type != TT_LambdaLSquare &&
- CurrentToken->Type != TT_ImplicitStringLiteral)
- CurrentToken->Type = TT_Unknown;
+ if (CurrentToken != NULL) {
+ // Reset token type in case we have already looked at it and then
+ // recovered from an error (e.g. failure to find the matching >).
+ if (CurrentToken->Type != TT_LambdaLSquare &&
+ CurrentToken->Type != TT_ImplicitStringLiteral)
+ CurrentToken->Type = TT_Unknown;
+ if (CurrentToken->Role)
+ CurrentToken->Role.reset(NULL);
+ CurrentToken->FakeLParens.clear();
+ CurrentToken->FakeRParens = 0;
+ }
}
/// \brief A struct to hold information valid in a specific context, e.g.
<< " C=" << Tok->CanBreakBefore << " T=" << Tok->Type
<< " S=" << Tok->SpacesRequiredBefore
<< " P=" << Tok->SplitPenalty << " Name=" << Tok->Tok.getName()
- << " PPK=" << Tok->PackingKind << " FakeLParens=";
+ << " L=" << Tok->TotalLength << " PPK=" << Tok->PackingKind
+ << " FakeLParens=";
for (unsigned i = 0, e = Tok->FakeLParens.size(); i != e; ++i)
llvm::errs() << Tok->FakeLParens[i] << "/";
llvm::errs() << " FakeRParens=" << Tok->FakeRParens << "\n";
+ if (Tok->Next == NULL)
+ assert(Tok == Line.Last);
Tok = Tok->Next;
}
llvm::errs() << "----\n";
return Tokens[Position];
}
+ void reset() { Position = -1; }
+
private:
ArrayRef<FormatToken *> Tokens;
int Position;
UnwrappedLineConsumer &Callback)
: Line(new UnwrappedLine), MustBreakBeforeNextToken(false),
CurrentLines(&Lines), StructuralError(false), Style(Style), Tokens(NULL),
- Callback(Callback), AllTokens(Tokens) {}
+ Callback(Callback), AllTokens(Tokens), PPBranchLevel(-1) {}
+
+void UnwrappedLineParser::reset() {
+ PPBranchLevel = -1;
+ Line.reset(new UnwrappedLine);
+ CommentsBeforeNextToken.clear();
+ FormatTok = NULL;
+ MustBreakBeforeNextToken = false;
+ PreprocessorDirectives.clear();
+ CurrentLines = &Lines;
+ DeclarationScopeStack.clear();
+ StructuralError = false;
+ PPStack.clear();
+}
bool UnwrappedLineParser::parse() {
- DEBUG(llvm::dbgs() << "----\n");
IndexedTokenSource TokenSource(AllTokens);
- Tokens = &TokenSource;
- readToken();
- parseFile();
- for (SmallVectorImpl<UnwrappedLine>::iterator I = Lines.begin(),
- E = Lines.end();
- I != E; ++I) {
- Callback.consumeUnwrappedLine(*I);
- }
+ do {
+ DEBUG(llvm::dbgs() << "----\n");
+ reset();
+ Tokens = &TokenSource;
+ TokenSource.reset();
+
+ readToken();
+ parseFile();
+ // Create line with eof token.
+ pushToken(FormatTok);
+ addUnwrappedLine();
+
+ for (SmallVectorImpl<UnwrappedLine>::iterator I = Lines.begin(),
+ E = Lines.end();
+ I != E; ++I) {
+ Callback.consumeUnwrappedLine(*I);
+ }
+ Callback.finishRun();
+ Lines.clear();
+ while (!PPLevelBranchIndex.empty() &&
+ PPLevelBranchIndex.back() + 1 == PPLevelBranchCount.back()) {
+ PPLevelBranchIndex.resize(PPLevelBranchIndex.size() - 1);
+ PPLevelBranchCount.resize(PPLevelBranchCount.size() - 1);
+ }
+ if (!PPLevelBranchIndex.empty()) {
+ ++PPLevelBranchIndex.back();
+ assert(PPLevelBranchIndex.size() == PPLevelBranchCount.size());
+ assert(PPLevelBranchIndex.back() <= PPLevelBranchCount.back());
+ }
+ } while (!PPLevelBranchIndex.empty());
- // Create line with eof token.
- pushToken(FormatTok);
- Callback.consumeUnwrappedLine(*Line);
return StructuralError;
}
parsePPDefine();
return;
case tok::pp_if:
- parsePPIf();
+ parsePPIf(/*IfDef=*/false);
break;
case tok::pp_ifdef:
case tok::pp_ifndef:
- parsePPIfdef();
+ parsePPIf(/*IfDef=*/true);
break;
case tok::pp_else:
parsePPElse();
PPStack.push_back(PP_Conditional);
}
-void UnwrappedLineParser::parsePPIf() {
+void UnwrappedLineParser::parsePPIf(bool IfDef) {
+ ++PPBranchLevel;
+ assert(PPBranchLevel >= 0 && PPBranchLevel <= (int)PPLevelBranchIndex.size());
+ if (PPBranchLevel == (int)PPLevelBranchIndex.size()) {
+ PPLevelBranchIndex.push_back(0);
+ PPLevelBranchCount.push_back(0);
+ }
+ PPChainBranchIndex.push(0);
nextToken();
- if ((FormatTok->Tok.isLiteral() &&
- StringRef(FormatTok->Tok.getLiteralData(), FormatTok->Tok.getLength()) ==
- "0") ||
- FormatTok->Tok.is(tok::kw_false)) {
+ bool IsLiteralFalse = (FormatTok->Tok.isLiteral() &&
+ StringRef(FormatTok->Tok.getLiteralData(),
+ FormatTok->Tok.getLength()) == "0") ||
+ FormatTok->Tok.is(tok::kw_false);
+ if ((!IfDef && IsLiteralFalse) || PPLevelBranchIndex[PPBranchLevel] > 0) {
PPStack.push_back(PP_Unreachable);
} else {
pushPPConditional();
parsePPUnknown();
}
-void UnwrappedLineParser::parsePPIfdef() {
- pushPPConditional();
- parsePPUnknown();
-}
-
void UnwrappedLineParser::parsePPElse() {
if (!PPStack.empty())
PPStack.pop_back();
- pushPPConditional();
+ assert(PPBranchLevel < (int)PPLevelBranchIndex.size());
+ if (!PPChainBranchIndex.empty())
+ ++PPChainBranchIndex.top();
+ if (PPBranchLevel >= 0 && !PPChainBranchIndex.empty() &&
+ PPLevelBranchIndex[PPBranchLevel] != PPChainBranchIndex.top()) {
+ PPStack.push_back(PP_Unreachable);
+ } else {
+ pushPPConditional();
+ }
parsePPUnknown();
}
void UnwrappedLineParser::parsePPElIf() { parsePPElse(); }
void UnwrappedLineParser::parsePPEndIf() {
+ assert(PPBranchLevel < (int)PPLevelBranchIndex.size());
+ if (PPBranchLevel >= 0 && !PPChainBranchIndex.empty()) {
+ if (PPChainBranchIndex.top() + 1 > PPLevelBranchCount[PPBranchLevel]) {
+ assert(PPLevelBranchCount[PPBranchLevel] == 0);
+ PPLevelBranchCount[PPBranchLevel] = PPChainBranchIndex.top() + 1;
+ }
+ }
+ --PPBranchLevel;
+ if (!PPChainBranchIndex.empty())
+ PPChainBranchIndex.pop();
if (!PPStack.empty())
PPStack.pop_back();
parsePPUnknown();
public:
virtual ~UnwrappedLineConsumer() {}
virtual void consumeUnwrappedLine(const UnwrappedLine &Line) = 0;
+ virtual void finishRun() = 0;
};
class FormatTokenSource;
bool parse();
private:
+ void reset();
void parseFile();
void parseLevel(bool HasOpeningBrace);
void parseBlock(bool MustBeDeclaration, bool AddLevel = true);
void parseChildBlock();
void parsePPDirective();
void parsePPDefine();
- void parsePPIf();
- void parsePPIfdef();
+ void parsePPIf(bool IfDef);
void parsePPElIf();
void parsePPElse();
void parsePPEndIf();
// Keeps a stack of currently active preprocessor branching directives.
SmallVector<PPBranchKind, 16> PPStack;
+ // The \c UnwrappedLineParser re-parses the code for each combination
+ // of preprocessor branches that can be taken.
+ // To that end, we take the same branch (#if, #else, or one of the #elif
+ // branches) for each nesting level of preprocessor branches.
+ // \c PPBranchLevel stores the current nesting level of preprocessor
+ // branches during one pass over the code.
+ int PPBranchLevel;
+
+ // Contains the current branch (#if, #else or one of the #elif branches)
+ // for each nesting level.
+ SmallVector<int, 8> PPLevelBranchIndex;
+
+ // Contains the maximum number of branches at each nesting level.
+ SmallVector<int, 8> PPLevelBranchCount;
+
+ // Contains the number of branches per nesting level we are currently
+ // in while parsing a preprocessor branch sequence.
+ // This is used to update PPLevelBranchCount at the end of a branch
+ // sequence.
+ std::stack<int> PPChainBranchIndex;
+
friend class ScopedLineState;
};
ContinuesPPDirective(ContinuesPPDirective), IndentLevel(IndentLevel),
Spaces(Spaces) {}
-void WhitespaceManager::replaceWhitespace(const FormatToken &Tok,
- unsigned Newlines,
+void WhitespaceManager::reset() {
+ Changes.clear();
+ Replaces.clear();
+}
+
+void WhitespaceManager::replaceWhitespace(FormatToken &Tok, unsigned Newlines,
unsigned IndentLevel, unsigned Spaces,
unsigned StartOfTokenColumn,
bool InPPDirective) {
+ if (Tok.Finalized)
+ return;
+ Tok.Decision = (Newlines > 0) ? FD_Break : FD_Continue;
Changes.push_back(Change(true, Tok.WhitespaceRange, IndentLevel, Spaces,
StartOfTokenColumn, Newlines, "", "",
Tok.Tok.getKind(), InPPDirective && !Tok.IsFirst));
void WhitespaceManager::addUntouchableToken(const FormatToken &Tok,
bool InPPDirective) {
+ if (Tok.Finalized)
+ return;
Changes.push_back(Change(false, Tok.WhitespaceRange, /*IndentLevel=*/0,
/*Spaces=*/0, Tok.OriginalColumn, Tok.NewlinesBefore,
"", "", Tok.Tok.getKind(),
const FormatToken &Tok, unsigned Offset, unsigned ReplaceChars,
StringRef PreviousPostfix, StringRef CurrentPrefix, bool InPPDirective,
unsigned Newlines, unsigned IndentLevel, unsigned Spaces) {
+ if (Tok.Finalized)
+ return;
Changes.push_back(Change(
true, SourceRange(Tok.getStartOfNonWhitespace().getLocWithOffset(Offset),
Tok.getStartOfNonWhitespace().getLocWithOffset(
bool UseCRLF)
: SourceMgr(SourceMgr), Style(Style), UseCRLF(UseCRLF) {}
+ /// \brief Prepares the \c WhitespaceManager for another run.
+ void reset();
+
/// \brief Replaces the whitespace in front of \p Tok. Only call once for
/// each \c AnnotatedToken.
- void replaceWhitespace(const FormatToken &Tok, unsigned Newlines,
+ void replaceWhitespace(FormatToken &Tok, unsigned Newlines,
unsigned IndentLevel, unsigned Spaces,
unsigned StartOfTokenColumn,
bool InPPDirective = false);
std::string format(llvm::StringRef Code, unsigned Offset, unsigned Length,
const FormatStyle &Style) {
DEBUG(llvm::errs() << "---\n");
+ DEBUG(llvm::errs() << Code << "\n\n");
std::vector<tooling::Range> Ranges(1, tooling::Range(Offset, Length));
tooling::Replacements Replaces = reformat(Style, Code, Ranges);
ReplacementCount = Replaces.size();
JustReplacedNewline = false;
}
}
- return MessedUp;
+ std::string WithoutWhitespace;
+ if (MessedUp[0] != ' ')
+ WithoutWhitespace.push_back(MessedUp[0]);
+ for (unsigned i = 1, e = MessedUp.size(); i != e; ++i) {
+ if (MessedUp[i] != ' ' || MessedUp[i - 1] != ' ')
+ WithoutWhitespace.push_back(MessedUp[i]);
+ }
+ return WithoutWhitespace;
}
FormatStyle getLLVMStyleWithColumns(unsigned ColumnLimit) {
EXPECT_EQ("1 2 3\n", messUp("1\n2\n3\n"));
EXPECT_EQ("a\n//b\nc", messUp("a\n//b\nc"));
EXPECT_EQ("a\n#b\nc", messUp("a\n#b\nc"));
- EXPECT_EQ("a\n#b c d\ne", messUp("a\n#b\\\nc\\\nd\ne"));
+ EXPECT_EQ("a\n#b c d\ne", messUp("a\n#b\\\nc\\\nd\ne"));
}
//===----------------------------------------------------------------------===//
"#endif\n"
"{\n"
"}");
+ verifyFormat("void f() {\n"
+ " if (true)\n"
+ "#ifdef A\n"
+ " f(42);\n"
+ " x();\n"
+ "#else\n"
+ " g();\n"
+ " x();\n"
+ "#endif\n"
+ "}");
+ verifyFormat("void f(param1, param2,\n"
+ " param3,\n"
+ "#ifdef A\n"
+ " param4(param5,\n"
+ "#ifdef A1\n"
+ " param6,\n"
+ "#ifdef A2\n"
+ " param7),\n"
+ "#else\n"
+ " param8),\n"
+ " param9,\n"
+ "#endif\n"
+ " param10,\n"
+ "#endif\n"
+ " param11)\n"
+ "#else\n"
+ " param12)\n"
+ "#endif\n"
+ "{\n"
+ " x();\n"
+ "}",
+ getLLVMStyleWithColumns(28));
}
TEST_F(FormatTest, LayoutBlockInsideParens) {
" << aaaaaaaaaaaaaaaaaaaaaaaaaaaaa;");
verifyFormat("return out << \"somepacket = {\\n\"\n"
- " << \" aaaaaa = \" << pkt.aaaaaa << \"\\n\"\n"
- " << \" bbbb = \" << pkt.bbbb << \"\\n\"\n"
- " << \" cccccc = \" << pkt.cccccc << \"\\n\"\n"
- " << \" ddd = [\" << pkt.ddd << \"]\\n\"\n"
+ " << \" aaaaaa = \" << pkt.aaaaaa << \"\\n\"\n"
+ " << \" bbbb = \" << pkt.bbbb << \"\\n\"\n"
+ " << \" cccccc = \" << pkt.cccccc << \"\\n\"\n"
+ " << \" ddd = [\" << pkt.ddd << \"]\\n\"\n"
" << \"}\";");
verifyFormat("llvm::outs() << \"aaaaaaaaaaaaaaaa: \" << aaaaaaaaaaaaaaaa\n"
TEST_F(FormatTest, DoesNotTouchUnwrappedLinesWithErrors) {
verifyFormat("namespace {\n"
- "class Foo { Foo (\n"
+ "class Foo { Foo (\n"
"};\n"
"} // comment");
}
}
TEST_F(FormatTest, DoNotInterfereWithErrorAndWarning) {
- verifyFormat("#error Leave all white!!!!! space* alone!\n");
- verifyFormat("#warning Leave all white!!!!! space* alone!\n");
+ EXPECT_EQ("#error Leave all white!!!!! space* alone!\n",
+ format("#error Leave all white!!!!! space* alone!\n"));
+ EXPECT_EQ(
+ "#warning Leave all white!!!!! space* alone!\n",
+ format("#warning Leave all white!!!!! space* alone!\n"));
EXPECT_EQ("#error 1", format(" # error 1"));
EXPECT_EQ("#warning 1", format(" # warning 1"));
}
" if (true) continue;\n"
"#endif\n"
" // Comment\n"
- " if (true) continue;",
+ " if (true) continue;\n"
+ "}",
ShortMergedIf);
}