r194090 - clang-format: Allow line merging and partial formatting of nested blocks
jahanian
fjahanian at apple.com
Tue Nov 5 13:01:59 PST 2013
This patch causes this failure on a VS buildbot machine.
style-on-command-line.cpp
- Fariborz
On Nov 5, 2013, at 11:10 AM, Daniel Jasper <djasper at google.com> wrote:
> Author: djasper
> Date: Tue Nov 5 13:10:03 2013
> New Revision: 194090
>
> URL: http://llvm.org/viewvc/llvm-project?rev=194090&view=rev
> Log:
> clang-format: Allow line merging and partial formatting of nested blocks
>
> Before, clang-format would always format entire nested blocks, which
> can be unwanted e.g. for long DEBUG({...}) statements. Also
> clang-format would not allow to merge lines in nested blocks (e.g. to
> put "if (a) return;" on one line in Google style).
>
> This is the first step of several refactorings mostly focussing on the
> additional functionality (by reusing the "format many lines" code to
> format the children of a nested block). The next steps are:
> * Pull out the line merging into its own class.
> * Seperate the formatting of many lines from the formatting of a single
> line (and the analysis of the solution space).
>
> Modified:
> cfe/trunk/lib/Format/Format.cpp
> cfe/trunk/lib/Format/TokenAnnotator.cpp
> cfe/trunk/unittests/Format/FormatTest.cpp
>
> Modified: cfe/trunk/lib/Format/Format.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Format/Format.cpp?rev=194090&r1=194089&r2=194090&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Format/Format.cpp (original)
> +++ cfe/trunk/lib/Format/Format.cpp Tue Nov 5 13:10:03 2013
> @@ -372,28 +372,148 @@ private:
>
> class UnwrappedLineFormatter {
> public:
> - UnwrappedLineFormatter(ContinuationIndenter *Indenter,
> + UnwrappedLineFormatter(SourceManager &SourceMgr,
> + SmallVectorImpl<CharSourceRange> &Ranges,
> + ContinuationIndenter *Indenter,
> WhitespaceManager *Whitespaces,
> - const FormatStyle &Style, const AnnotatedLine &Line)
> - : Indenter(Indenter), Whitespaces(Whitespaces), Style(Style), Line(Line),
> - Count(0) {}
> + const FormatStyle &Style)
> + : SourceMgr(SourceMgr), Ranges(Ranges), Indenter(Indenter),
> + Whitespaces(Whitespaces), Style(Style) {}
> +
> + unsigned format(SmallVectorImpl<AnnotatedLine *> &Lines, bool DryRun,
> + int AdditionalIndent = 0) {
> + assert(!Lines.empty());
> + unsigned Penalty = 0;
> + std::vector<int> IndentForLevel;
> + for (unsigned i = 0, e = Lines[0]->Level; i != e; ++i)
> + IndentForLevel.push_back(Style.IndentWidth * i + AdditionalIndent);
> + bool PreviousLineWasTouched = false;
> + const AnnotatedLine *PreviousLine = NULL;
> + bool FormatPPDirective = false;
> + for (SmallVectorImpl<AnnotatedLine *>::iterator I = Lines.begin(),
> + E = Lines.end();
> + I != E; ++I) {
> + const AnnotatedLine &TheLine = **I;
> + const FormatToken *FirstTok = TheLine.First;
> + int Offset = getIndentOffset(*FirstTok);
> +
> + // Check whether this line is part of a formatted preprocessor directive.
> + if (FirstTok->HasUnescapedNewline)
> + FormatPPDirective = false;
> + if (!FormatPPDirective && TheLine.InPPDirective &&
> + (touchesLine(TheLine) || touchesPPDirective(I + 1, E)))
> + FormatPPDirective = true;
> +
> + // Determine indent and try to merge multiple unwrapped lines.
> + while (IndentForLevel.size() <= TheLine.Level)
> + IndentForLevel.push_back(-1);
> + IndentForLevel.resize(TheLine.Level + 1);
> + unsigned Indent = getIndent(IndentForLevel, TheLine.Level);
> + if (static_cast<int>(Indent) + Offset >= 0)
> + Indent += Offset;
> + if (!DryRun)
> + tryFitMultipleLinesInOne(Indent, I, E);
> +
> + bool WasMoved = PreviousLineWasTouched && FirstTok->NewlinesBefore == 0;
> + if (TheLine.First->is(tok::eof)) {
> + if (PreviousLineWasTouched && !DryRun) {
> + unsigned Newlines = std::min(FirstTok->NewlinesBefore, 1u);
> + Whitespaces->replaceWhitespace(*TheLine.First, Newlines,
> + /*IndentLevel=*/0, /*Spaces=*/0,
> + /*TargetColumn=*/0);
> + }
> + } else if (TheLine.Type != LT_Invalid &&
> + (WasMoved || FormatPPDirective || touchesLine(TheLine))) {
> + unsigned LevelIndent =
> + getIndent(IndentForLevel, TheLine.Level);
> + if (FirstTok->WhitespaceRange.isValid()) {
> + if (!DryRun)
> + formatFirstToken(*TheLine.First, PreviousLine, TheLine.Level,
> + Indent, TheLine.InPPDirective);
> + } else {
> + Indent = LevelIndent = FirstTok->OriginalColumn;
> + }
> +
> + // If everything fits on a single line, just put it there.
> + unsigned ColumnLimit = Style.ColumnLimit;
> + if (I + 1 != E) {
> + AnnotatedLine *NextLine = *(I + 1);
> + if (NextLine->InPPDirective && !NextLine->First->HasUnescapedNewline)
> + ColumnLimit = getColumnLimit(TheLine.InPPDirective);
> + }
> +
> + if (TheLine.Last->TotalLength + Indent <= ColumnLimit) {
> + LineState State = Indenter->getInitialState(Indent, &TheLine, DryRun);
> + while (State.NextToken != NULL)
> + Indenter->addTokenToState(State, /*Newline=*/false, DryRun);
> + } else if (Style.ColumnLimit == 0) {
> + NoColumnLimitFormatter Formatter(Indenter);
> + if (!DryRun)
> + Formatter.format(Indent, &TheLine);
> + } else {
> + Penalty += format(TheLine, Indent, DryRun);
> + }
> +
> + IndentForLevel[TheLine.Level] = LevelIndent;
> + PreviousLineWasTouched = true;
> + } else {
> + // Format the first token if necessary, and notify the WhitespaceManager
> + // about the unchanged whitespace.
> + for (FormatToken *Tok = TheLine.First; Tok != NULL; Tok = Tok->Next) {
> + if (Tok == TheLine.First &&
> + (Tok->NewlinesBefore > 0 || Tok->IsFirst)) {
> + unsigned LevelIndent = Tok->OriginalColumn;
> + if (!DryRun) {
> + // Remove trailing whitespace of the previous line if it was
> + // touched.
> + if (PreviousLineWasTouched || touchesEmptyLineBefore(TheLine)) {
> + formatFirstToken(*Tok, PreviousLine, TheLine.Level, LevelIndent,
> + TheLine.InPPDirective);
> + } else {
> + Whitespaces->addUntouchableToken(*Tok, TheLine.InPPDirective);
> + }
> + }
> +
> + if (static_cast<int>(LevelIndent) - Offset >= 0)
> + LevelIndent -= Offset;
> + if (Tok->isNot(tok::comment))
> + IndentForLevel[TheLine.Level] = LevelIndent;
> + } else if (!DryRun) {
> + Whitespaces->addUntouchableToken(*Tok, TheLine.InPPDirective);
> + }
> + }
> + // If we did not reformat this unwrapped line, the column at the end of
> + // the last token is unchanged - thus, we can calculate the end of the
> + // last token.
> + PreviousLineWasTouched = false;
> + }
> + if (!DryRun) {
> + for (FormatToken *Tok = TheLine.First; Tok != NULL; Tok = Tok->Next) {
> + Tok->Finalized = true;
> + }
> + }
> + PreviousLine = *I;
> + }
> + return Penalty;
> + }
>
> - /// \brief Formats an \c UnwrappedLine and returns the penalty.
> +private:
> + /// \brief Formats an \c AnnotatedLine and returns the penalty.
> ///
> /// If \p DryRun is \c false, directly applies the changes.
> - unsigned format(unsigned FirstIndent, bool DryRun = false) {
> + unsigned format(const AnnotatedLine &Line, unsigned FirstIndent,
> + bool DryRun) {
> LineState State = Indenter->getInitialState(FirstIndent, &Line, DryRun);
>
> // If the ObjC method declaration does not fit on a line, we should format
> // it with one arg per line.
> - if (Line.Type == LT_ObjCMethodDecl)
> + if (State.Line->Type == LT_ObjCMethodDecl)
> State.Stack.back().BreakBeforeParameter = true;
>
> // Find best solution in solution space.
> return analyzeSolutionSpace(State, DryRun);
> }
>
> -private:
> /// \brief An edge in the solution space from \c Previous->State to \c State,
> /// inserting a newline dependent on the \c NewLine.
> struct StateNode {
> @@ -419,6 +539,262 @@ private:
> typedef std::priority_queue<QueueItem, std::vector<QueueItem>,
> std::greater<QueueItem> > QueueType;
>
> + /// \brief Get the offset of the line relatively to the level.
> + ///
> + /// For example, 'public:' labels in classes are offset by 1 or 2
> + /// characters to the left from their level.
> + int getIndentOffset(const FormatToken &RootToken) {
> + if (RootToken.isAccessSpecifier(false) || RootToken.isObjCAccessSpecifier())
> + return Style.AccessModifierOffset;
> + return 0;
> + }
> +
> + /// \brief Add a new line and the required indent before the first Token
> + /// of the \c UnwrappedLine if there was no structural parsing error.
> + void formatFirstToken(FormatToken &RootToken,
> + const AnnotatedLine *PreviousLine, unsigned IndentLevel,
> + unsigned Indent, bool InPPDirective) {
> + unsigned Newlines =
> + std::min(RootToken.NewlinesBefore, Style.MaxEmptyLinesToKeep + 1);
> + // Remove empty lines before "}" where applicable.
> + if (RootToken.is(tok::r_brace) &&
> + (!RootToken.Next ||
> + (RootToken.Next->is(tok::semi) && !RootToken.Next->Next)))
> + Newlines = std::min(Newlines, 1u);
> + if (Newlines == 0 && !RootToken.IsFirst)
> + Newlines = 1;
> +
> + // Insert extra new line before access specifiers.
> + if (PreviousLine && PreviousLine->Last->isOneOf(tok::semi, tok::r_brace) &&
> + RootToken.isAccessSpecifier() && RootToken.NewlinesBefore == 1)
> + ++Newlines;
> +
> + // Remove empty lines after access specifiers.
> + if (PreviousLine && PreviousLine->First->isAccessSpecifier())
> + Newlines = std::min(1u, Newlines);
> +
> + Whitespaces->replaceWhitespace(
> + RootToken, Newlines, IndentLevel, Indent, Indent,
> + InPPDirective && !RootToken.HasUnescapedNewline);
> + }
> +
> + /// \brief Get the indent of \p Level from \p IndentForLevel.
> + ///
> + /// \p IndentForLevel must contain the indent for the level \c l
> + /// at \p IndentForLevel[l], or a value < 0 if the indent for
> + /// that level is unknown.
> + unsigned getIndent(const std::vector<int> IndentForLevel, unsigned Level) {
> + if (IndentForLevel[Level] != -1)
> + return IndentForLevel[Level];
> + if (Level == 0)
> + return 0;
> + return getIndent(IndentForLevel, Level - 1) + Style.IndentWidth;
> + }
> +
> + /// \brief Tries to merge lines into one.
> + ///
> + /// This will change \c Line and \c AnnotatedLine to contain the merged line,
> + /// if possible; note that \c I will be incremented when lines are merged.
> + void tryFitMultipleLinesInOne(unsigned Indent,
> + SmallVectorImpl<AnnotatedLine *>::iterator &I,
> + SmallVectorImpl<AnnotatedLine *>::iterator E) {
> + // We can never merge stuff if there are trailing line comments.
> + AnnotatedLine *TheLine = *I;
> + if (TheLine->Last->Type == TT_LineComment)
> + return;
> +
> + if (Indent > Style.ColumnLimit)
> + return;
> +
> + unsigned Limit = Style.ColumnLimit - Indent;
> + // If we already exceed the column limit, we set 'Limit' to 0. The different
> + // tryMerge..() functions can then decide whether to still do merging.
> + Limit = TheLine->Last->TotalLength > Limit
> + ? 0
> + : Limit - TheLine->Last->TotalLength;
> +
> + if (I + 1 == E || (*(I + 1))->Type == LT_Invalid)
> + return;
> +
> + if (TheLine->Last->is(tok::l_brace)) {
> + tryMergeSimpleBlock(I, E, Limit);
> + } else if (Style.AllowShortIfStatementsOnASingleLine &&
> + TheLine->First->is(tok::kw_if)) {
> + tryMergeSimpleControlStatement(I, E, Limit);
> + } else if (Style.AllowShortLoopsOnASingleLine &&
> + TheLine->First->isOneOf(tok::kw_for, tok::kw_while)) {
> + tryMergeSimpleControlStatement(I, E, Limit);
> + } else if (TheLine->InPPDirective && (TheLine->First->HasUnescapedNewline ||
> + TheLine->First->IsFirst)) {
> + tryMergeSimplePPDirective(I, E, Limit);
> + }
> + }
> +
> + void tryMergeSimplePPDirective(SmallVectorImpl<AnnotatedLine *>::iterator &I,
> + SmallVectorImpl<AnnotatedLine *>::iterator E,
> + unsigned Limit) {
> + if (Limit == 0)
> + return;
> + AnnotatedLine &Line = **I;
> + if (!(*(I + 1))->InPPDirective || (*(I + 1))->First->HasUnescapedNewline)
> + return;
> + if (I + 2 != E && (*(I + 2))->InPPDirective &&
> + !(*(I + 2))->First->HasUnescapedNewline)
> + return;
> + if (1 + (*(I + 1))->Last->TotalLength > Limit)
> + return;
> + join(Line, **(++I));
> + }
> +
> + void
> + tryMergeSimpleControlStatement(SmallVectorImpl<AnnotatedLine *>::iterator &I,
> + SmallVectorImpl<AnnotatedLine *>::iterator E,
> + unsigned Limit) {
> + if (Limit == 0)
> + return;
> + if (Style.BreakBeforeBraces == FormatStyle::BS_Allman &&
> + (*(I + 1))->First->is(tok::l_brace))
> + return;
> + if ((*(I + 1))->InPPDirective != (*I)->InPPDirective ||
> + ((*(I + 1))->InPPDirective && (*(I + 1))->First->HasUnescapedNewline))
> + return;
> + AnnotatedLine &Line = **I;
> + if (Line.Last->isNot(tok::r_paren))
> + return;
> + if (1 + (*(I + 1))->Last->TotalLength > Limit)
> + return;
> + if ((*(I + 1))->First->isOneOf(tok::semi, tok::kw_if, tok::kw_for,
> + tok::kw_while) ||
> + (*(I + 1))->First->Type == TT_LineComment)
> + return;
> + // Only inline simple if's (no nested if or else).
> + if (I + 2 != E && Line.First->is(tok::kw_if) &&
> + (*(I + 2))->First->is(tok::kw_else))
> + return;
> + join(Line, **(++I));
> + }
> +
> + void tryMergeSimpleBlock(SmallVectorImpl<AnnotatedLine *>::iterator &I,
> + SmallVectorImpl<AnnotatedLine *>::iterator E,
> + unsigned Limit) {
> + // No merging if the brace already is on the next line.
> + if (Style.BreakBeforeBraces != FormatStyle::BS_Attach)
> + return;
> +
> + // First, check that the current line allows merging. This is the case if
> + // we're not in a control flow statement and the last token is an opening
> + // brace.
> + AnnotatedLine &Line = **I;
> + if (Line.First->isOneOf(tok::kw_if, tok::kw_while, tok::kw_do, tok::r_brace,
> + tok::kw_else, tok::kw_try, tok::kw_catch,
> + tok::kw_for,
> + // This gets rid of all ObjC @ keywords and methods.
> + tok::at, tok::minus, tok::plus))
> + return;
> +
> + FormatToken *Tok = (*(I + 1))->First;
> + if (Tok->is(tok::r_brace) && !Tok->MustBreakBefore &&
> + (Tok->getNextNonComment() == NULL ||
> + Tok->getNextNonComment()->is(tok::semi))) {
> + // We merge empty blocks even if the line exceeds the column limit.
> + Tok->SpacesRequiredBefore = 0;
> + Tok->CanBreakBefore = true;
> + join(Line, **(I + 1));
> + I += 1;
> + } else if (Limit != 0 && Line.First->isNot(tok::kw_namespace)) {
> + // Check that we still have three lines and they fit into the limit.
> + if (I + 2 == E || (*(I + 2))->Type == LT_Invalid ||
> + !nextTwoLinesFitInto(I, Limit))
> + return;
> +
> + // Second, check that the next line does not contain any braces - if it
> + // does, readability declines when putting it into a single line.
> + if ((*(I + 1))->Last->Type == TT_LineComment || Tok->MustBreakBefore)
> + return;
> + do {
> + if (Tok->isOneOf(tok::l_brace, tok::r_brace))
> + return;
> + Tok = Tok->Next;
> + } while (Tok != NULL);
> +
> + // Last, check that the third line contains a single closing brace.
> + Tok = (*(I + 2))->First;
> + if (Tok->getNextNonComment() != NULL || Tok->isNot(tok::r_brace) ||
> + Tok->MustBreakBefore)
> + return;
> +
> + join(Line, **(I + 1));
> + join(Line, **(I + 2));
> + I += 2;
> + }
> + }
> +
> + bool nextTwoLinesFitInto(SmallVectorImpl<AnnotatedLine *>::iterator I,
> + unsigned Limit) {
> + return 1 + (*(I + 1))->Last->TotalLength + 1 +
> + (*(I + 2))->Last->TotalLength <=
> + Limit;
> + }
> +
> + void join(AnnotatedLine &A, const AnnotatedLine &B) {
> + assert(!A.Last->Next);
> + assert(!B.First->Previous);
> + A.Last->Next = B.First;
> + B.First->Previous = A.Last;
> + unsigned LengthA = A.Last->TotalLength + B.First->SpacesRequiredBefore;
> + for (FormatToken *Tok = B.First; Tok; Tok = Tok->Next) {
> + Tok->TotalLength += LengthA;
> + A.Last = Tok;
> + }
> + }
> +
> + unsigned getColumnLimit(bool InPPDirective) const {
> + // In preprocessor directives reserve two chars for trailing " \"
> + return Style.ColumnLimit - (InPPDirective ? 2 : 0);
> + }
> +
> + bool touchesRanges(const CharSourceRange &Range) {
> + for (SmallVectorImpl<CharSourceRange>::const_iterator I = Ranges.begin(),
> + E = Ranges.end();
> + I != E; ++I) {
> + if (!SourceMgr.isBeforeInTranslationUnit(Range.getEnd(), I->getBegin()) &&
> + !SourceMgr.isBeforeInTranslationUnit(I->getEnd(), Range.getBegin()))
> + return true;
> + }
> + return false;
> + }
> +
> + bool touchesLine(const AnnotatedLine &TheLine) {
> + const FormatToken *First = TheLine.First;
> + const FormatToken *Last = TheLine.Last;
> + CharSourceRange LineRange = CharSourceRange::getCharRange(
> + First->WhitespaceRange.getBegin().getLocWithOffset(
> + First->LastNewlineOffset),
> + Last->getStartOfNonWhitespace().getLocWithOffset(
> + Last->TokenText.size() - 1));
> + return touchesRanges(LineRange);
> + }
> +
> + bool touchesPPDirective(SmallVectorImpl<AnnotatedLine *>::const_iterator I,
> + SmallVectorImpl<AnnotatedLine *>::const_iterator E) {
> + for (; I != E; ++I) {
> + if ((*I)->First->HasUnescapedNewline)
> + return false;
> + if (touchesLine(**I))
> + return true;
> + }
> + return false;
> + }
> +
> + bool touchesEmptyLineBefore(const AnnotatedLine &TheLine) {
> + const FormatToken *First = TheLine.First;
> + CharSourceRange LineRange = CharSourceRange::getCharRange(
> + First->WhitespaceRange.getBegin(),
> + First->WhitespaceRange.getBegin().getLocWithOffset(
> + First->LastNewlineOffset));
> + return touchesRanges(LineRange);
> + }
> +
> /// \brief Analyze the entire solution space starting from \p InitialState.
> ///
> /// This implements a variant of Dijkstra's algorithm on the graph that spans
> @@ -430,6 +806,11 @@ private:
> unsigned analyzeSolutionSpace(LineState &InitialState, bool DryRun = false) {
> std::set<LineState> Seen;
>
> + // Increasing count of \c StateNode items we have created. This is used to
> + // create a deterministic order independent of the container.
> + unsigned Count = 0;
> + QueueType Queue;
> +
> // Insert start element into queue.
> StateNode *Node =
> new (Allocator.Allocate()) StateNode(InitialState, false, NULL);
> @@ -459,9 +840,9 @@ private:
>
> FormatDecision LastFormat = Node->State.NextToken->Decision;
> if (LastFormat == FD_Unformatted || LastFormat == FD_Continue)
> - addNextStateToQueue(Penalty, Node, /*NewLine=*/false);
> + addNextStateToQueue(Penalty, Node, /*NewLine=*/false, &Count, &Queue);
> if (LastFormat == FD_Unformatted || LastFormat == FD_Break)
> - addNextStateToQueue(Penalty, Node, /*NewLine=*/true);
> + addNextStateToQueue(Penalty, Node, /*NewLine=*/true, &Count, &Queue);
> }
>
> if (Queue.empty()) {
> @@ -509,7 +890,7 @@ private:
> /// Assume the current state is \p PreviousNode and has been reached with a
> /// penalty of \p Penalty. Insert a line break if \p NewLine is \c true.
> void addNextStateToQueue(unsigned Penalty, StateNode *PreviousNode,
> - bool NewLine) {
> + bool NewLine, unsigned *Count, QueueType *Queue) {
> if (NewLine && !Indenter->canBreak(PreviousNode->State))
> return;
> if (!NewLine && Indenter->mustBreak(PreviousNode->State))
> @@ -522,8 +903,8 @@ private:
>
> Penalty += Indenter->addTokenToState(Node->State, NewLine, true);
>
> - Queue.push(QueueItem(OrderedPenalty(Penalty, Count), Node));
> - ++Count;
> + Queue->push(QueueItem(OrderedPenalty(Penalty, *Count), Node));
> + ++(*Count);
> }
>
> /// \brief If the \p State's next token is an r_brace closing a nested block,
> @@ -548,7 +929,7 @@ private:
> /// break or don't break.
> bool formatChildren(LineState &State, bool NewLine, bool DryRun,
> unsigned &Penalty) {
> - const FormatToken &Previous = *State.NextToken->Previous;
> + FormatToken &Previous = *State.NextToken->Previous;
> const FormatToken *LBrace = State.NextToken->getPreviousNonComment();
> if (!LBrace || LBrace->isNot(tok::l_brace) ||
> LBrace->BlockKind != BK_Block || Previous.Children.size() == 0)
> @@ -557,29 +938,15 @@ private:
> return true;
>
> if (NewLine) {
> - unsigned ParentIndent = State.Stack.back().Indent;
> - for (SmallVector<AnnotatedLine *, 1>::const_iterator
> - I = Previous.Children.begin(),
> - E = Previous.Children.end();
> - I != E; ++I) {
> - unsigned Indent =
> - ParentIndent + ((*I)->Level - Line.Level - 1) * Style.IndentWidth;
> - if (!DryRun) {
> - unsigned Newlines = std::min((*I)->First->NewlinesBefore,
> - Style.MaxEmptyLinesToKeep + 1);
> - Newlines = std::max(1u, Newlines);
> - Whitespaces->replaceWhitespace(
> - *(*I)->First, Newlines, (*I)->Level, /*Spaces=*/Indent,
> - /*StartOfTokenColumn=*/Indent, Line.InPPDirective);
> - }
> - UnwrappedLineFormatter Formatter(Indenter, Whitespaces, Style, **I);
> - Penalty += Formatter.format(Indent, DryRun);
> - }
> + int AdditionalIndent = State.Stack.back().Indent -
> + Previous.Children[0]->Level * Style.IndentWidth;
> + Penalty += format(Previous.Children, DryRun, AdditionalIndent);
> return true;
> }
>
> + // Cannot merge multiple statements into a single line.
> if (Previous.Children.size() > 1)
> - return false; // Cannot merge multiple statements into a single line.
> + return false;
>
> // We can't put the closing "}" on a line with a trailing comment.
> if (Previous.Children[0]->Last->isTrailingComment())
> @@ -590,25 +957,20 @@ private:
> *Previous.Children[0]->First,
> /*Newlines=*/0, /*IndentLevel=*/0, /*Spaces=*/1,
> /*StartOfTokenColumn=*/State.Column, State.Line->InPPDirective);
> - UnwrappedLineFormatter Formatter(Indenter, Whitespaces, Style,
> - *Previous.Children[0]);
> - Penalty += Formatter.format(State.Column + 1, DryRun);
> }
> + Penalty += format(*Previous.Children[0], State.Column + 1, DryRun);
>
> State.Column += 1 + Previous.Children[0]->Last->TotalLength;
> return true;
> }
>
> + SourceManager &SourceMgr;
> + SmallVectorImpl<CharSourceRange> &Ranges;
> ContinuationIndenter *Indenter;
> WhitespaceManager *Whitespaces;
> FormatStyle Style;
> - const AnnotatedLine &Line;
>
> llvm::SpecificBumpPtrAllocator<StateNode> Allocator;
> - QueueType Queue;
> - // Increasing count of \c StateNode items we have created. This is used
> - // to create a deterministic order independent of the container.
> - unsigned Count;
> };
>
> class FormatTokenLexer {
> @@ -831,7 +1193,7 @@ public:
> const std::vector<CharSourceRange> &Ranges)
> : Style(Style), Lex(Lex), SourceMgr(SourceMgr),
> Whitespaces(SourceMgr, Style, inputUsesCRLF(Lex.getBuffer())),
> - Ranges(Ranges), UnwrappedLines(1),
> + Ranges(Ranges.begin(), Ranges.end()), UnwrappedLines(1),
> Encoding(encoding::detectEncoding(Lex.getBuffer())) {
> DEBUG(llvm::dbgs() << "File encoding: "
> << (Encoding == encoding::Encoding_UTF8 ? "UTF8"
> @@ -875,120 +1237,20 @@ public:
> 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(AnnotatedLines);
> - for (unsigned i = 0, e = AnnotatedLines.size(); i != e; ++i) {
> - Annotator.calculateFormattingInformation(*AnnotatedLines[i]);
> - }
> -
> - Annotator.setCommentLineLevels(AnnotatedLines);
> -
> - std::vector<int> IndentForLevel;
> - bool PreviousLineWasTouched = false;
> - const AnnotatedLine *PreviousLine = NULL;
> - bool FormatPPDirective = false;
> - for (SmallVectorImpl<AnnotatedLine *>::iterator I = AnnotatedLines.begin(),
> - E = AnnotatedLines.end();
> - I != E; ++I) {
> - const AnnotatedLine &TheLine = **I;
> - const FormatToken *FirstTok = TheLine.First;
> - int Offset = getIndentOffset(*TheLine.First);
> -
> - // Check whether this line is part of a formatted preprocessor directive.
> - if (FirstTok->HasUnescapedNewline)
> - FormatPPDirective = false;
> - if (!FormatPPDirective && TheLine.InPPDirective &&
> - (touchesLine(TheLine) || touchesPPDirective(I + 1, E)))
> - FormatPPDirective = true;
> -
> - // Determine indent and try to merge multiple unwrapped lines.
> - while (IndentForLevel.size() <= TheLine.Level)
> - IndentForLevel.push_back(-1);
> - IndentForLevel.resize(TheLine.Level + 1);
> - unsigned Indent = getIndent(IndentForLevel, TheLine.Level);
> - if (static_cast<int>(Indent) + Offset >= 0)
> - Indent += Offset;
> - tryFitMultipleLinesInOne(Indent, I, E);
> -
> - bool WasMoved = PreviousLineWasTouched && FirstTok->NewlinesBefore == 0;
> - if (TheLine.First->is(tok::eof)) {
> - if (PreviousLineWasTouched) {
> - unsigned Newlines = std::min(FirstTok->NewlinesBefore, 1u);
> - Whitespaces.replaceWhitespace(*TheLine.First, Newlines,
> - /*IndentLevel=*/0, /*Spaces=*/0,
> - /*TargetColumn=*/0);
> - }
> - } else if (TheLine.Type != LT_Invalid &&
> - (WasMoved || FormatPPDirective || touchesLine(TheLine))) {
> - unsigned LevelIndent = getIndent(IndentForLevel, TheLine.Level);
> - if (FirstTok->WhitespaceRange.isValid()) {
> - formatFirstToken(*TheLine.First, PreviousLine, TheLine.Level, Indent,
> - TheLine.InPPDirective);
> - } else {
> - Indent = LevelIndent = FirstTok->OriginalColumn;
> - }
> - ContinuationIndenter Indenter(Style, SourceMgr, Whitespaces, Encoding,
> - BinPackInconclusiveFunctions);
> -
> - // If everything fits on a single line, just put it there.
> - unsigned ColumnLimit = Style.ColumnLimit;
> - AnnotatedLine *NextLine = *(I + 1);
> - if ((I + 1) != E && NextLine->InPPDirective &&
> - !NextLine->First->HasUnescapedNewline)
> - ColumnLimit = getColumnLimit(TheLine.InPPDirective);
> -
> - if (TheLine.Last->TotalLength + Indent <= ColumnLimit) {
> - LineState State =
> - Indenter.getInitialState(Indent, &TheLine, /*DryRun=*/false);
> - while (State.NextToken != NULL)
> - Indenter.addTokenToState(State, false, false);
> - } else if (Style.ColumnLimit == 0) {
> - NoColumnLimitFormatter Formatter(&Indenter);
> - Formatter.format(Indent, &TheLine);
> - } else {
> - UnwrappedLineFormatter Formatter(&Indenter, &Whitespaces, Style,
> - TheLine);
> - Formatter.format(Indent);
> - }
> -
> - IndentForLevel[TheLine.Level] = LevelIndent;
> - PreviousLineWasTouched = true;
> - } else {
> - // Format the first token if necessary, and notify the WhitespaceManager
> - // about the unchanged whitespace.
> - for (FormatToken *Tok = TheLine.First; Tok != NULL; Tok = Tok->Next) {
> - if (Tok == TheLine.First &&
> - (Tok->NewlinesBefore > 0 || Tok->IsFirst)) {
> - unsigned LevelIndent = Tok->OriginalColumn;
> - // Remove trailing whitespace of the previous line if it was
> - // touched.
> - if (PreviousLineWasTouched || touchesEmptyLineBefore(TheLine)) {
> - formatFirstToken(*Tok, PreviousLine, TheLine.Level, LevelIndent,
> - TheLine.InPPDirective);
> - } else {
> - Whitespaces.addUntouchableToken(*Tok, TheLine.InPPDirective);
> - }
> -
> - if (static_cast<int>(LevelIndent) - Offset >= 0)
> - LevelIndent -= Offset;
> - if (Tok->isNot(tok::comment))
> - IndentForLevel[TheLine.Level] = LevelIndent;
> - } else {
> - Whitespaces.addUntouchableToken(*Tok, TheLine.InPPDirective);
> - }
> - }
> - // If we did not reformat this unwrapped line, the column at the end of
> - // the last token is unchanged - thus, we can calculate the end of the
> - // last token.
> - PreviousLineWasTouched = false;
> - }
> - for (FormatToken *Tok = TheLine.First; Tok != NULL; Tok = Tok->Next) {
> - Tok->Finalized = true;
> - }
> - PreviousLine = *I;
> + for (unsigned i = 0, e = AnnotatedLines.size(); i != e; ++i) {
> + Annotator.annotate(*AnnotatedLines[i]);
> + }
> + deriveLocalStyle(AnnotatedLines);
> + for (unsigned i = 0, e = AnnotatedLines.size(); i != e; ++i) {
> + Annotator.calculateFormattingInformation(*AnnotatedLines[i]);
> }
> +
> + Annotator.setCommentLineLevels(AnnotatedLines);
> + ContinuationIndenter Indenter(Style, SourceMgr, Whitespaces, Encoding,
> + BinPackInconclusiveFunctions);
> + UnwrappedLineFormatter Formatter(SourceMgr, Ranges, &Indenter, &Whitespaces,
> + Style);
> + Formatter.format(AnnotatedLines, /*DryRun=*/false);
> return Whitespaces.generateReplacements();
> }
>
> @@ -1051,228 +1313,6 @@ private:
> HasBinPackedFunction || !HasOnePerLineFunction;
> }
>
> - /// \brief Get the indent of \p Level from \p IndentForLevel.
> - ///
> - /// \p IndentForLevel must contain the indent for the level \c l
> - /// at \p IndentForLevel[l], or a value < 0 if the indent for
> - /// that level is unknown.
> - unsigned getIndent(const std::vector<int> IndentForLevel, unsigned Level) {
> - if (IndentForLevel[Level] != -1)
> - return IndentForLevel[Level];
> - if (Level == 0)
> - return 0;
> - return getIndent(IndentForLevel, Level - 1) + Style.IndentWidth;
> - }
> -
> - /// \brief Get the offset of the line relatively to the level.
> - ///
> - /// For example, 'public:' labels in classes are offset by 1 or 2
> - /// characters to the left from their level.
> - int getIndentOffset(const FormatToken &RootToken) {
> - if (RootToken.isAccessSpecifier(false) || RootToken.isObjCAccessSpecifier())
> - return Style.AccessModifierOffset;
> - return 0;
> - }
> -
> - /// \brief Tries to merge lines into one.
> - ///
> - /// This will change \c Line and \c AnnotatedLine to contain the merged line,
> - /// if possible; note that \c I will be incremented when lines are merged.
> - void tryFitMultipleLinesInOne(unsigned Indent,
> - SmallVectorImpl<AnnotatedLine *>::iterator &I,
> - SmallVectorImpl<AnnotatedLine *>::iterator E) {
> - // We can never merge stuff if there are trailing line comments.
> - AnnotatedLine *TheLine = *I;
> - if (TheLine->Last->Type == TT_LineComment)
> - return;
> -
> - if (Indent > Style.ColumnLimit)
> - return;
> -
> - unsigned Limit = Style.ColumnLimit - Indent;
> - // If we already exceed the column limit, we set 'Limit' to 0. The different
> - // tryMerge..() functions can then decide whether to still do merging.
> - Limit = TheLine->Last->TotalLength > Limit
> - ? 0
> - : Limit - TheLine->Last->TotalLength;
> -
> - if (I + 1 == E || (*(I + 1))->Type == LT_Invalid)
> - return;
> -
> - if (TheLine->Last->is(tok::l_brace)) {
> - tryMergeSimpleBlock(I, E, Limit);
> - } else if (Style.AllowShortIfStatementsOnASingleLine &&
> - TheLine->First->is(tok::kw_if)) {
> - tryMergeSimpleControlStatement(I, E, Limit);
> - } else if (Style.AllowShortLoopsOnASingleLine &&
> - TheLine->First->isOneOf(tok::kw_for, tok::kw_while)) {
> - tryMergeSimpleControlStatement(I, E, Limit);
> - } else if (TheLine->InPPDirective && (TheLine->First->HasUnescapedNewline ||
> - TheLine->First->IsFirst)) {
> - tryMergeSimplePPDirective(I, E, Limit);
> - }
> - }
> -
> - void tryMergeSimplePPDirective(SmallVectorImpl<AnnotatedLine *>::iterator &I,
> - SmallVectorImpl<AnnotatedLine *>::iterator E,
> - unsigned Limit) {
> - if (Limit == 0)
> - return;
> - AnnotatedLine &Line = **I;
> - if (!(*(I + 1))->InPPDirective || (*(I + 1))->First->HasUnescapedNewline)
> - return;
> - if (I + 2 != E && (*(I + 2))->InPPDirective &&
> - !(*(I + 2))->First->HasUnescapedNewline)
> - return;
> - if (1 + (*(I + 1))->Last->TotalLength > Limit)
> - return;
> - join(Line, **(++I));
> - }
> -
> - void
> - tryMergeSimpleControlStatement(SmallVectorImpl<AnnotatedLine *>::iterator &I,
> - SmallVectorImpl<AnnotatedLine *>::iterator E,
> - unsigned Limit) {
> - if (Limit == 0)
> - return;
> - if (Style.BreakBeforeBraces == FormatStyle::BS_Allman &&
> - (*(I + 1))->First->is(tok::l_brace))
> - return;
> - if ((*(I + 1))->InPPDirective != (*I)->InPPDirective ||
> - ((*(I + 1))->InPPDirective && (*(I + 1))->First->HasUnescapedNewline))
> - return;
> - AnnotatedLine &Line = **I;
> - if (Line.Last->isNot(tok::r_paren))
> - return;
> - if (1 + (*(I + 1))->Last->TotalLength > Limit)
> - return;
> - if ((*(I + 1))->First->isOneOf(tok::semi, tok::kw_if, tok::kw_for,
> - tok::kw_while) ||
> - (*(I + 1))->First->Type == TT_LineComment)
> - return;
> - // Only inline simple if's (no nested if or else).
> - if (I + 2 != E && Line.First->is(tok::kw_if) &&
> - (*(I + 2))->First->is(tok::kw_else))
> - return;
> - join(Line, **(++I));
> - }
> -
> - void tryMergeSimpleBlock(SmallVectorImpl<AnnotatedLine *>::iterator &I,
> - SmallVectorImpl<AnnotatedLine *>::iterator E,
> - unsigned Limit) {
> - // No merging if the brace already is on the next line.
> - if (Style.BreakBeforeBraces != FormatStyle::BS_Attach)
> - return;
> -
> - // First, check that the current line allows merging. This is the case if
> - // we're not in a control flow statement and the last token is an opening
> - // brace.
> - AnnotatedLine &Line = **I;
> - if (Line.First->isOneOf(tok::kw_if, tok::kw_while, tok::kw_do, tok::r_brace,
> - tok::kw_else, tok::kw_try, tok::kw_catch,
> - tok::kw_for,
> - // This gets rid of all ObjC @ keywords and methods.
> - tok::at, tok::minus, tok::plus))
> - return;
> -
> - FormatToken *Tok = (*(I + 1))->First;
> - if (Tok->is(tok::r_brace) && !Tok->MustBreakBefore &&
> - (Tok->getNextNonComment() == NULL ||
> - Tok->getNextNonComment()->is(tok::semi))) {
> - // We merge empty blocks even if the line exceeds the column limit.
> - Tok->SpacesRequiredBefore = 0;
> - Tok->CanBreakBefore = true;
> - join(Line, **(I + 1));
> - I += 1;
> - } else if (Limit != 0 && Line.First->isNot(tok::kw_namespace)) {
> - // Check that we still have three lines and they fit into the limit.
> - if (I + 2 == E || (*(I + 2))->Type == LT_Invalid ||
> - !nextTwoLinesFitInto(I, Limit))
> - return;
> -
> - // Second, check that the next line does not contain any braces - if it
> - // does, readability declines when putting it into a single line.
> - if ((*(I + 1))->Last->Type == TT_LineComment || Tok->MustBreakBefore)
> - return;
> - do {
> - if (Tok->isOneOf(tok::l_brace, tok::r_brace))
> - return;
> - Tok = Tok->Next;
> - } while (Tok != NULL);
> -
> - // Last, check that the third line contains a single closing brace.
> - Tok = (*(I + 2))->First;
> - if (Tok->getNextNonComment() != NULL || Tok->isNot(tok::r_brace) ||
> - Tok->MustBreakBefore)
> - return;
> -
> - join(Line, **(I + 1));
> - join(Line, **(I + 2));
> - I += 2;
> - }
> - }
> -
> - bool nextTwoLinesFitInto(SmallVectorImpl<AnnotatedLine *>::iterator I,
> - unsigned Limit) {
> - return 1 + (*(I + 1))->Last->TotalLength + 1 +
> - (*(I + 2))->Last->TotalLength <=
> - Limit;
> - }
> -
> - void join(AnnotatedLine &A, const AnnotatedLine &B) {
> - assert(!A.Last->Next);
> - assert(!B.First->Previous);
> - A.Last->Next = B.First;
> - B.First->Previous = A.Last;
> - unsigned LengthA = A.Last->TotalLength + B.First->SpacesRequiredBefore;
> - for (FormatToken *Tok = B.First; Tok; Tok = Tok->Next) {
> - Tok->TotalLength += LengthA;
> - A.Last = Tok;
> - }
> - }
> -
> - bool touchesRanges(const CharSourceRange &Range) {
> - for (unsigned i = 0, e = Ranges.size(); i != e; ++i) {
> - if (!SourceMgr.isBeforeInTranslationUnit(Range.getEnd(),
> - Ranges[i].getBegin()) &&
> - !SourceMgr.isBeforeInTranslationUnit(Ranges[i].getEnd(),
> - Range.getBegin()))
> - return true;
> - }
> - return false;
> - }
> -
> - bool touchesLine(const AnnotatedLine &TheLine) {
> - const FormatToken *First = TheLine.First;
> - const FormatToken *Last = TheLine.Last;
> - CharSourceRange LineRange = CharSourceRange::getCharRange(
> - First->WhitespaceRange.getBegin().getLocWithOffset(
> - First->LastNewlineOffset),
> - Last->getStartOfNonWhitespace().getLocWithOffset(
> - Last->TokenText.size() - 1));
> - return touchesRanges(LineRange);
> - }
> -
> - bool touchesPPDirective(SmallVectorImpl<AnnotatedLine *>::iterator I,
> - SmallVectorImpl<AnnotatedLine *>::iterator E) {
> - for (; I != E; ++I) {
> - if ((*I)->First->HasUnescapedNewline)
> - return false;
> - if (touchesLine(**I))
> - return true;
> - }
> - return false;
> - }
> -
> - bool touchesEmptyLineBefore(const AnnotatedLine &TheLine) {
> - const FormatToken *First = TheLine.First;
> - CharSourceRange LineRange = CharSourceRange::getCharRange(
> - First->WhitespaceRange.getBegin(),
> - First->WhitespaceRange.getBegin().getLocWithOffset(
> - First->LastNewlineOffset));
> - return touchesRanges(LineRange);
> - }
> -
> virtual void consumeUnwrappedLine(const UnwrappedLine &TheLine) {
> assert(!UnwrappedLines.empty());
> UnwrappedLines.back().push_back(TheLine);
> @@ -1282,45 +1322,11 @@ private:
> 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.
> - void formatFirstToken(FormatToken &RootToken,
> - const AnnotatedLine *PreviousLine, unsigned IndentLevel,
> - unsigned Indent, bool InPPDirective) {
> - unsigned Newlines =
> - std::min(RootToken.NewlinesBefore, Style.MaxEmptyLinesToKeep + 1);
> - // Remove empty lines before "}" where applicable.
> - if (RootToken.is(tok::r_brace) &&
> - (!RootToken.Next ||
> - (RootToken.Next->is(tok::semi) && !RootToken.Next->Next)))
> - Newlines = std::min(Newlines, 1u);
> - if (Newlines == 0 && !RootToken.IsFirst)
> - Newlines = 1;
> -
> - // Insert extra new line before access specifiers.
> - if (PreviousLine && PreviousLine->Last->isOneOf(tok::semi, tok::r_brace) &&
> - RootToken.isAccessSpecifier() && RootToken.NewlinesBefore == 1)
> - ++Newlines;
> -
> - // Remove empty lines after access specifiers.
> - if (PreviousLine && PreviousLine->First->isAccessSpecifier())
> - Newlines = std::min(1u, Newlines);
> -
> - Whitespaces.replaceWhitespace(
> - RootToken, Newlines, IndentLevel, Indent, Indent,
> - InPPDirective && !RootToken.HasUnescapedNewline);
> - }
> -
> - unsigned getColumnLimit(bool InPPDirective) const {
> - // In preprocessor directives reserve two chars for trailing " \"
> - return Style.ColumnLimit - (InPPDirective ? 2 : 0);
> - }
> -
> FormatStyle Style;
> Lexer &Lex;
> SourceManager &SourceMgr;
> WhitespaceManager Whitespaces;
> - std::vector<CharSourceRange> Ranges;
> + SmallVector<CharSourceRange, 8> Ranges;
> SmallVector<SmallVector<UnwrappedLine, 16>, 2> UnwrappedLines;
>
> encoding::Encoding Encoding;
>
> Modified: cfe/trunk/lib/Format/TokenAnnotator.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Format/TokenAnnotator.cpp?rev=194090&r1=194089&r2=194090&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Format/TokenAnnotator.cpp (original)
> +++ cfe/trunk/lib/Format/TokenAnnotator.cpp Tue Nov 5 13:10:03 2013
> @@ -1013,22 +1013,21 @@ private:
>
> void
> TokenAnnotator::setCommentLineLevels(SmallVectorImpl<AnnotatedLine *> &Lines) {
> - if (Lines.empty())
> - return;
> -
> const AnnotatedLine *NextNonCommentLine = NULL;
> - for (unsigned i = Lines.size() - 1; i > 0; --i) {
> - if (NextNonCommentLine && Lines[i]->First->is(tok::comment) &&
> - !Lines[i]->First->Next)
> - Lines[i]->Level = NextNonCommentLine->Level;
> + for (SmallVectorImpl<AnnotatedLine *>::reverse_iterator I = Lines.rbegin(),
> + E = Lines.rend();
> + I != E; ++I) {
> + if (NextNonCommentLine && (*I)->First->is(tok::comment) &&
> + (*I)->First->Next == NULL)
> + (*I)->Level = NextNonCommentLine->Level;
> else
> - NextNonCommentLine =
> - Lines[i]->First->isNot(tok::r_brace) ? Lines[i] : NULL;
> + NextNonCommentLine = (*I)->First->isNot(tok::r_brace) ? (*I) : NULL;
> +
> + setCommentLineLevels((*I)->Children);
> }
> }
>
> void TokenAnnotator::annotate(AnnotatedLine &Line) {
> - setCommentLineLevels(Line.Children);
> for (SmallVectorImpl<AnnotatedLine *>::iterator I = Line.Children.begin(),
> E = Line.Children.end();
> I != E; ++I) {
>
> Modified: cfe/trunk/unittests/Format/FormatTest.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Format/FormatTest.cpp?rev=194090&r1=194089&r2=194090&view=diff
> ==============================================================================
> --- cfe/trunk/unittests/Format/FormatTest.cpp (original)
> +++ cfe/trunk/unittests/Format/FormatTest.cpp Tue Nov 5 13:10:03 2013
> @@ -2447,6 +2447,45 @@ TEST_F(FormatTest, LayoutNestedBlocks) {
> " // comment\n"
> " int j;\n"
> "});"));
> +
> + verifyFormat("DEBUG({\n"
> + " if (a)\n"
> + " return;\n"
> + "});");
> + verifyGoogleFormat("DEBUG({\n"
> + " if (a) return;\n"
> + "});");
> + FormatStyle Style = getGoogleStyle();
> + Style.ColumnLimit = 45;
> + verifyFormat("Debug(aaaaa, {\n"
> + " if (aaaaaaaaaaaaaaaaaaaaaaaa)\n"
> + " return;\n"
> + " },\n"
> + " a);", Style);
> +
> + EXPECT_EQ("Debug({\n"
> + " if (aaaaaaaaaaaaaaaaaaaaaaaa)\n"
> + " return;\n"
> + " },\n"
> + " a);",
> + format("Debug({\n"
> + " if (aaaaaaaaaaaaaaaaaaaaaaaa)\n"
> + " return;\n"
> + " },\n"
> + " a);",
> + 50, 1, getLLVMStyle()));
> +}
> +
> +TEST_F(FormatTest, IndividualStatementsOfNestedBlocks) {
> + EXPECT_EQ("DEBUG({\n"
> + " int i;\n"
> + " int j;\n"
> + "});",
> + format("DEBUG( {\n"
> + " int i;\n"
> + " int j;\n"
> + "} ) ;",
> + 40, 1, getLLVMStyle()));
> }
>
> TEST_F(FormatTest, PutEmptyBlocksIntoOneLine) {
>
>
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20131105/27dd87e0/attachment.html>
More information about the cfe-commits
mailing list