r236974 - Refactor the formatter of clang-format.

Manuel Klimek klimek at google.com
Mon May 11 02:20:10 PDT 2015


I thought I did that :)

On Mon, May 11, 2015 at 11:09 AM Daniel Jasper <djasper at google.com> wrote:

> Generally makes sense to fix the patch description if the content of the
> patch changes significantly ;-).
>
> On Mon, May 11, 2015 at 10:21 AM, Manuel Klimek <klimek at google.com> wrote:
>
>> Author: klimek
>> Date: Mon May 11 03:21:35 2015
>> New Revision: 236974
>>
>> URL: http://llvm.org/viewvc/llvm-project?rev=236974&view=rev
>> Log:
>> Refactor the formatter of clang-format.
>>
>> Pull various parts of the UnwrappedLineFormatter into their own
>> abstractions. NFC.
>>
>> There are two things left for subsequent changes (to keep this
>> reasonably small)
>> - the UnwrappedLineFormatter now has a bad name
>> - the UnwrappedLineFormatter::format function is still too large
>>
>> Modified:
>>     cfe/trunk/lib/Format/Format.cpp
>>     cfe/trunk/lib/Format/UnwrappedLineFormatter.cpp
>>     cfe/trunk/lib/Format/UnwrappedLineFormatter.h
>>
>> Modified: cfe/trunk/lib/Format/Format.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Format/Format.cpp?rev=236974&r1=236973&r2=236974&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/lib/Format/Format.cpp (original)
>> +++ cfe/trunk/lib/Format/Format.cpp Mon May 11 03:21:35 2015
>> @@ -1269,9 +1269,9 @@ public:
>>      ContinuationIndenter Indenter(Style, Tokens.getKeywords(), SourceMgr,
>>                                    Whitespaces, Encoding,
>>                                    BinPackInconclusiveFunctions);
>> -    UnwrappedLineFormatter Formatter(&Indenter, &Whitespaces, Style,
>> -                                     Tokens.getKeywords(),
>> IncompleteFormat);
>> -    Formatter.format(AnnotatedLines, /*DryRun=*/false);
>> +    UnwrappedLineFormatter(&Indenter, &Whitespaces, Style,
>> Tokens.getKeywords(),
>> +                           IncompleteFormat)
>> +        .format(AnnotatedLines);
>>      return Whitespaces.generateReplacements();
>>    }
>>
>>
>> Modified: cfe/trunk/lib/Format/UnwrappedLineFormatter.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Format/UnwrappedLineFormatter.cpp?rev=236974&r1=236973&r2=236974&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/lib/Format/UnwrappedLineFormatter.cpp (original)
>> +++ cfe/trunk/lib/Format/UnwrappedLineFormatter.cpp Mon May 11 03:21:35
>> 2015
>> @@ -151,8 +151,8 @@ private:
>>        return 0;
>>      if (1 + I[1]->Last->TotalLength > Limit)
>>        return 0;
>> -    if (I[1]->First->isOneOf(tok::semi, tok::kw_if, tok::kw_for,
>> -                             tok::kw_while, TT_LineComment))
>> +    if (I[1]->First->isOneOf(tok::semi, tok::kw_if, tok::kw_for,
>> tok::kw_while,
>> +                             TT_LineComment))
>>        return 0;
>>      // Only inline simple if's (no nested if or else).
>>      if (I + 2 != E && Line.First->is(tok::kw_if) &&
>> @@ -161,9 +161,10 @@ private:
>>      return 1;
>>    }
>>
>> -  unsigned tryMergeShortCaseLabels(
>> -      SmallVectorImpl<AnnotatedLine *>::const_iterator I,
>> -      SmallVectorImpl<AnnotatedLine *>::const_iterator E, unsigned
>> Limit) {
>> +  unsigned
>> +  tryMergeShortCaseLabels(SmallVectorImpl<AnnotatedLine
>> *>::const_iterator I,
>> +                          SmallVectorImpl<AnnotatedLine
>> *>::const_iterator E,
>> +                          unsigned Limit) {
>>      if (Limit == 0 || I + 1 == E ||
>>          I[1]->First->isOneOf(tok::kw_case, tok::kw_default))
>>        return 0;
>> @@ -307,40 +308,346 @@ private:
>>    const AdditionalKeywords &Keywords;
>>  };
>>
>> -class NoColumnLimitFormatter {
>> +static void markFinalized(FormatToken *Tok) {
>> +  for (; Tok; Tok = Tok->Next) {
>> +    Tok->Finalized = true;
>> +    for (AnnotatedLine *Child : Tok->Children)
>> +      markFinalized(Child->First);
>> +  }
>> +}
>> +
>> +#ifndef NDEBUG
>> +static void printLineState(const LineState &State) {
>> +  llvm::dbgs() << "State: ";
>> +  for (const ParenState &P : State.Stack) {
>> +    llvm::dbgs() << P.Indent << "|" << P.LastSpace << "|" <<
>> P.NestedBlockIndent
>> +                 << " ";
>> +  }
>> +  llvm::dbgs() << State.NextToken->TokenText << "\n";
>> +}
>> +#endif
>> +
>> +/// \brief Base class for classes that format one \c AnnotatedLine.
>> +class LineFormatter {
>> +public:
>> +  LineFormatter(ContinuationIndenter *Indenter, WhitespaceManager
>> *Whitespaces,
>> +                const FormatStyle &Style,
>> +                UnwrappedLineFormatter *BlockFormatter)
>> +      : Indenter(Indenter), Whitespaces(Whitespaces), Style(Style),
>> +        BlockFormatter(BlockFormatter) {}
>> +  virtual ~LineFormatter() {}
>> +
>> +  /// \brief Formats an \c AnnotatedLine and returns the penalty.
>> +  ///
>> +  /// If \p DryRun is \c false, directly applies the changes.
>> +  virtual unsigned formatLine(const AnnotatedLine &Line, unsigned
>> FirstIndent,
>> +                              bool DryRun) = 0;
>> +
>> +protected:
>> +  /// \brief If the \p State's next token is an r_brace closing a nested
>> block,
>> +  /// format the nested block before it.
>> +  ///
>> +  /// Returns \c true if all children could be placed successfully and
>> adapts
>> +  /// \p Penalty as well as \p State. If \p DryRun is false, also
>> directly
>> +  /// creates changes using \c Whitespaces.
>> +  ///
>> +  /// The crucial idea here is that children always get formatted upon
>> +  /// encountering the closing brace right after the nested block. Now,
>> if we
>> +  /// are currently trying to keep the "}" on the same line (i.e. \p
>> NewLine is
>> +  /// \c false), the entire block has to be kept on the same line (which
>> is only
>> +  /// possible if it fits on the line, only contains a single statement,
>> etc.
>> +  ///
>> +  /// If \p NewLine is true, we format the nested block on separate
>> lines, i.e.
>> +  /// break after the "{", format all lines with correct indentation and
>> the put
>> +  /// the closing "}" on yet another new line.
>> +  ///
>> +  /// This enables us to keep the simple structure of the
>> +  /// \c UnwrappedLineFormatter, where we only have two options for each
>> token:
>> +  /// break or don't break.
>> +  bool formatChildren(LineState &State, bool NewLine, bool DryRun,
>> +                      unsigned &Penalty) {
>> +    const FormatToken *LBrace = State.NextToken->getPreviousNonComment();
>> +    FormatToken &Previous = *State.NextToken->Previous;
>> +    if (!LBrace || LBrace->isNot(tok::l_brace) ||
>> +        LBrace->BlockKind != BK_Block || Previous.Children.size() == 0)
>> +      // The previous token does not open a block. Nothing to do. We
>> don't
>> +      // assert so that we can simply call this function for all tokens.
>> +      return true;
>> +
>> +    if (NewLine) {
>> +      int AdditionalIndent = State.Stack.back().Indent -
>> +                             Previous.Children[0]->Level *
>> Style.IndentWidth;
>> +
>> +      Penalty +=
>> +          BlockFormatter->format(Previous.Children, DryRun,
>> AdditionalIndent,
>> +                                 /*FixBadIndentation=*/true);
>> +      return true;
>> +    }
>> +
>> +    if (Previous.Children[0]->First->MustBreakBefore)
>> +      return false;
>> +
>> +    // Cannot merge multiple statements into a single line.
>> +    if (Previous.Children.size() > 1)
>> +      return false;
>> +
>> +    // Cannot merge into one line if this line ends on a comment.
>> +    if (Previous.is(tok::comment))
>> +      return false;
>> +
>> +    // We can't put the closing "}" on a line with a trailing comment.
>> +    if (Previous.Children[0]->Last->isTrailingComment())
>> +      return false;
>> +
>> +    // If the child line exceeds the column limit, we wouldn't want to
>> merge it.
>> +    // We add +2 for the trailing " }".
>> +    if (Style.ColumnLimit > 0 &&
>> +        Previous.Children[0]->Last->TotalLength + State.Column + 2 >
>> +            Style.ColumnLimit)
>> +      return false;
>> +
>> +    if (!DryRun) {
>> +      Whitespaces->replaceWhitespace(
>> +          *Previous.Children[0]->First,
>> +          /*Newlines=*/0, /*IndentLevel=*/0, /*Spaces=*/1,
>> +          /*StartOfTokenColumn=*/State.Column,
>> State.Line->InPPDirective);
>> +    }
>> +    Penalty += formatLine(*Previous.Children[0], State.Column + 1,
>> DryRun);
>> +
>> +    State.Column += 1 + Previous.Children[0]->Last->TotalLength;
>> +    return true;
>> +  }
>> +
>> +  ContinuationIndenter *Indenter;
>> +
>> +private:
>> +  WhitespaceManager *Whitespaces;
>> +  const FormatStyle &Style;
>> +  UnwrappedLineFormatter *BlockFormatter;
>> +};
>> +
>> +/// \brief Formatter that keeps the existing line breaks.
>> +class NoColumnLimitLineFormatter : public LineFormatter {
>>  public:
>> -  NoColumnLimitFormatter(ContinuationIndenter *Indenter,
>> -                         UnwrappedLineFormatter *Formatter)
>> -      : Indenter(Indenter), Formatter(Formatter) {}
>> -
>> -  /// \brief Formats the line starting at \p State, simply keeping all
>> of the
>> -  /// input's line breaking decisions.
>> -  void format(unsigned FirstIndent, const AnnotatedLine *Line) {
>> +  NoColumnLimitLineFormatter(ContinuationIndenter *Indenter,
>> +                             WhitespaceManager *Whitespaces,
>> +                             const FormatStyle &Style,
>> +                             UnwrappedLineFormatter *BlockFormatter)
>> +      : LineFormatter(Indenter, Whitespaces, Style, BlockFormatter) {}
>> +
>> +  /// \brief Formats the line, simply keeping all of the input's line
>> breaking
>> +  /// decisions.
>> +  unsigned formatLine(const AnnotatedLine &Line, unsigned FirstIndent,
>> +                      bool DryRun) override {
>> +    assert(!DryRun);
>>      LineState State =
>> -        Indenter->getInitialState(FirstIndent, Line, /*DryRun=*/false);
>> +        Indenter->getInitialState(FirstIndent, &Line, /*DryRun=*/false);
>>      while (State.NextToken) {
>>        bool Newline =
>>            Indenter->mustBreak(State) ||
>>            (Indenter->canBreak(State) && State.NextToken->NewlinesBefore
>> > 0);
>>        unsigned Penalty = 0;
>> -      Formatter->formatChildren(State, Newline, /*DryRun=*/false,
>> Penalty);
>> +      formatChildren(State, Newline, /*DryRun=*/false, Penalty);
>>        Indenter->addTokenToState(State, Newline, /*DryRun=*/false);
>>      }
>> +    return 0;
>>    }
>> +};
>>
>> -private:
>> -  ContinuationIndenter *Indenter;
>> -  UnwrappedLineFormatter *Formatter;
>> +/// \brief Formatter that puts all tokens into a single line without
>> breaks.
>> +class NoLineBreakFormatter : public LineFormatter {
>> +public:
>> +  NoLineBreakFormatter(ContinuationIndenter *Indenter,
>> +                       WhitespaceManager *Whitespaces, const FormatStyle
>> &Style,
>> +                       UnwrappedLineFormatter *BlockFormatter)
>> +      : LineFormatter(Indenter, Whitespaces, Style, BlockFormatter) {}
>> +
>> +  /// \brief Puts all tokens into a single line.
>> +  unsigned formatLine(const AnnotatedLine &Line, unsigned FirstIndent,
>> +                      bool DryRun) {
>> +    unsigned Penalty = 0;
>> +    LineState State = Indenter->getInitialState(FirstIndent, &Line,
>> DryRun);
>> +    while (State.NextToken) {
>> +      formatChildren(State, /*Newline=*/false, DryRun, Penalty);
>> +      Indenter->addTokenToState(State, /*Newline=*/false, DryRun);
>> +    }
>> +    return Penalty;
>> +  }
>>  };
>>
>> +/// \brief Finds the best way to break lines.
>> +class OptimizingLineFormatter : public LineFormatter {
>> +public:
>> +  OptimizingLineFormatter(ContinuationIndenter *Indenter,
>> +                          WhitespaceManager *Whitespaces,
>> +                          const FormatStyle &Style,
>> +                          UnwrappedLineFormatter *BlockFormatter)
>> +      : LineFormatter(Indenter, Whitespaces, Style, BlockFormatter) {}
>> +
>> +  /// \brief Formats the line by finding the best line breaks with line
>> lengths
>> +  /// below the column limit.
>> +  unsigned formatLine(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 (State.Line->Type == LT_ObjCMethodDecl)
>> +      State.Stack.back().BreakBeforeParameter = true;
>>
>> -static void markFinalized(FormatToken *Tok) {
>> -  for (; Tok; Tok = Tok->Next) {
>> -    Tok->Finalized = true;
>> -    for (AnnotatedLine *Child : Tok->Children)
>> -      markFinalized(Child->First);
>> +    // Find best solution in solution space.
>> +    return analyzeSolutionSpace(State, DryRun);
>> +  }
>> +
>> +private:
>> +  struct CompareLineStatePointers {
>> +    bool operator()(LineState *obj1, LineState *obj2) const {
>> +      return *obj1 < *obj2;
>> +    }
>> +  };
>> +
>> +  /// \brief A pair of <penalty, count> that is used to prioritize the
>> BFS on.
>> +  ///
>> +  /// In case of equal penalties, we want to prefer states that were
>> inserted
>> +  /// first. During state generation we make sure that we insert states
>> first
>> +  /// that break the line as late as possible.
>> +  typedef std::pair<unsigned, unsigned> OrderedPenalty;
>> +
>> +  /// \brief An edge in the solution space from \c Previous->State to \c
>> State,
>> +  /// inserting a newline dependent on the \c NewLine.
>> +  struct StateNode {
>> +    StateNode(const LineState &State, bool NewLine, StateNode *Previous)
>> +        : State(State), NewLine(NewLine), Previous(Previous) {}
>> +    LineState State;
>> +    bool NewLine;
>> +    StateNode *Previous;
>> +  };
>> +
>> +  /// \brief An item in the prioritized BFS search queue. The \c
>> StateNode's
>> +  /// \c State has the given \c OrderedPenalty.
>> +  typedef std::pair<OrderedPenalty, StateNode *> QueueItem;
>> +
>> +  /// \brief The BFS queue type.
>> +  typedef std::priority_queue<QueueItem, std::vector<QueueItem>,
>> +                              std::greater<QueueItem>> QueueType;
>> +
>> +  /// \brief Analyze the entire solution space starting from \p
>> InitialState.
>> +  ///
>> +  /// This implements a variant of Dijkstra's algorithm on the graph
>> that spans
>> +  /// the solution space (\c LineStates are the nodes). The algorithm
>> tries to
>> +  /// find the shortest path (the one with lowest penalty) from \p
>> InitialState
>> +  /// to a state where all tokens are placed. Returns the penalty.
>> +  ///
>> +  /// If \p DryRun is \c false, directly applies the changes.
>> +  unsigned analyzeSolutionSpace(LineState &InitialState, bool DryRun) {
>> +    std::set<LineState *, CompareLineStatePointers> 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,
>> nullptr);
>> +    Queue.push(QueueItem(OrderedPenalty(0, Count), Node));
>> +    ++Count;
>> +
>> +    unsigned Penalty = 0;
>> +
>> +    // While not empty, take first element and follow edges.
>> +    while (!Queue.empty()) {
>> +      Penalty = Queue.top().first.first;
>> +      StateNode *Node = Queue.top().second;
>> +      if (!Node->State.NextToken) {
>> +        DEBUG(llvm::dbgs() << "\n---\nPenalty for line: " << Penalty <<
>> "\n");
>> +        break;
>> +      }
>> +      Queue.pop();
>> +
>> +      // Cut off the analysis of certain solutions if the analysis gets
>> too
>> +      // complex. See description of IgnoreStackForComparison.
>> +      if (Count > 10000)
>> +        Node->State.IgnoreStackForComparison = true;
>> +
>> +      if (!Seen.insert(&Node->State).second)
>> +        // State already examined with lower penalty.
>> +        continue;
>> +
>> +      FormatDecision LastFormat = Node->State.NextToken->Decision;
>> +      if (LastFormat == FD_Unformatted || LastFormat == FD_Continue)
>> +        addNextStateToQueue(Penalty, Node, /*NewLine=*/false, &Count,
>> &Queue);
>> +      if (LastFormat == FD_Unformatted || LastFormat == FD_Break)
>> +        addNextStateToQueue(Penalty, Node, /*NewLine=*/true, &Count,
>> &Queue);
>> +    }
>> +
>> +    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)
>> +      reconstructPath(InitialState, Queue.top().second);
>> +
>> +    DEBUG(llvm::dbgs() << "Total number of analyzed states: " << Count
>> << "\n");
>> +    DEBUG(llvm::dbgs() << "---\n");
>> +
>> +    return Penalty;
>> +  }
>> +
>> +  /// \brief Add the following state to the analysis queue \c Queue.
>> +  ///
>> +  /// 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, unsigned *Count, QueueType
>> *Queue) {
>> +    if (NewLine && !Indenter->canBreak(PreviousNode->State))
>> +      return;
>> +    if (!NewLine && Indenter->mustBreak(PreviousNode->State))
>> +      return;
>> +
>> +    StateNode *Node = new (Allocator.Allocate())
>> +        StateNode(PreviousNode->State, NewLine, PreviousNode);
>> +    if (!formatChildren(Node->State, NewLine, /*DryRun=*/true, Penalty))
>> +      return;
>> +
>> +    Penalty += Indenter->addTokenToState(Node->State, NewLine, true);
>> +
>> +    Queue->push(QueueItem(OrderedPenalty(Penalty, *Count), Node));
>> +    ++(*Count);
>> +  }
>> +
>> +  /// \brief Applies the best formatting by reconstructing the path in
>> the
>> +  /// solution space that leads to \c Best.
>> +  void reconstructPath(LineState &State, StateNode *Best) {
>> +    std::deque<StateNode *> Path;
>> +    // We do not need a break before the initial token.
>> +    while (Best->Previous) {
>> +      Path.push_front(Best);
>> +      Best = Best->Previous;
>> +    }
>> +    for (std::deque<StateNode *>::iterator I = Path.begin(), E =
>> Path.end();
>> +         I != E; ++I) {
>> +      unsigned Penalty = 0;
>> +      formatChildren(State, (*I)->NewLine, /*DryRun=*/false, Penalty);
>> +      Penalty += Indenter->addTokenToState(State, (*I)->NewLine, false);
>> +
>> +      DEBUG({
>> +        printLineState((*I)->Previous->State);
>> +        if ((*I)->NewLine) {
>> +          llvm::dbgs() << "Penalty for placing "
>> +                       << (*I)->Previous->State.NextToken->Tok.getName()
>> << ": "
>> +                       << Penalty << "\n";
>> +        }
>> +      });
>> +    }
>>    }
>> -}
>> +
>> +  llvm::SpecificBumpPtrAllocator<StateNode> Allocator;
>> +};
>>
>>  } // namespace
>>
>> @@ -431,17 +738,14 @@ UnwrappedLineFormatter::format(const Sma
>>
>>        if (TheLine.Last->TotalLength + Indent <= ColumnLimit ||
>>            TheLine.Type == LT_ImportStatement) {
>> -        LineState State = Indenter->getInitialState(Indent, &TheLine,
>> DryRun);
>> -        while (State.NextToken) {
>> -          formatChildren(State, /*Newline=*/false, DryRun, Penalty);
>> -          Indenter->addTokenToState(State, /*Newline=*/false, DryRun);
>> -        }
>> +        Penalty += NoLineBreakFormatter(Indenter, Whitespaces, Style,
>> this)
>> +                       .formatLine(TheLine, Indent, DryRun);
>>        } else if (Style.ColumnLimit == 0) {
>> -        NoColumnLimitFormatter Formatter(Indenter, this);
>> -        if (!DryRun)
>> -          Formatter.format(Indent, &TheLine);
>> +        NoColumnLimitLineFormatter(Indenter, Whitespaces, Style, this)
>> +            .formatLine(TheLine, Indent, DryRun);
>>        } else {
>> -        Penalty += format(TheLine, Indent, DryRun);
>> +        Penalty += OptimizingLineFormatter(Indenter, Whitespaces, Style,
>> this)
>> +                       .formatLine(TheLine, Indent, DryRun);
>>        }
>>
>>        if (!TheLine.InPPDirective)
>> @@ -486,19 +790,6 @@ UnwrappedLineFormatter::format(const Sma
>>    return Penalty;
>>  }
>>
>> -unsigned UnwrappedLineFormatter::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 (State.Line->Type == LT_ObjCMethodDecl)
>> -    State.Stack.back().BreakBeforeParameter = true;
>> -
>> -  // Find best solution in solution space.
>> -  return analyzeSolutionSpace(State, DryRun);
>> -}
>> -
>>  void UnwrappedLineFormatter::formatFirstToken(FormatToken &RootToken,
>>                                                const AnnotatedLine
>> *PreviousLine,
>>                                                unsigned IndentLevel,
>> @@ -567,174 +858,5 @@ void UnwrappedLineFormatter::join(Annota
>>    }
>>  }
>>
>> -unsigned UnwrappedLineFormatter::analyzeSolutionSpace(LineState
>> &InitialState,
>> -                                                      bool DryRun) {
>> -  std::set<LineState *, CompareLineStatePointers> 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, nullptr);
>> -  Queue.push(QueueItem(OrderedPenalty(0, Count), Node));
>> -  ++Count;
>> -
>> -  unsigned Penalty = 0;
>> -
>> -  // While not empty, take first element and follow edges.
>> -  while (!Queue.empty()) {
>> -    Penalty = Queue.top().first.first;
>> -    StateNode *Node = Queue.top().second;
>> -    if (!Node->State.NextToken) {
>> -      DEBUG(llvm::dbgs() << "\n---\nPenalty for line: " << Penalty <<
>> "\n");
>> -      break;
>> -    }
>> -    Queue.pop();
>> -
>> -    // Cut off the analysis of certain solutions if the analysis gets too
>> -    // complex. See description of IgnoreStackForComparison.
>> -    if (Count > 10000)
>> -      Node->State.IgnoreStackForComparison = true;
>> -
>> -    if (!Seen.insert(&Node->State).second)
>> -      // State already examined with lower penalty.
>> -      continue;
>> -
>> -    FormatDecision LastFormat = Node->State.NextToken->Decision;
>> -    if (LastFormat == FD_Unformatted || LastFormat == FD_Continue)
>> -      addNextStateToQueue(Penalty, Node, /*NewLine=*/false, &Count,
>> &Queue);
>> -    if (LastFormat == FD_Unformatted || LastFormat == FD_Break)
>> -      addNextStateToQueue(Penalty, Node, /*NewLine=*/true, &Count,
>> &Queue);
>> -  }
>> -
>> -  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)
>> -    reconstructPath(InitialState, Queue.top().second);
>> -
>> -  DEBUG(llvm::dbgs() << "Total number of analyzed states: " << Count <<
>> "\n");
>> -  DEBUG(llvm::dbgs() << "---\n");
>> -
>> -  return Penalty;
>> -}
>> -
>> -#ifndef NDEBUG
>> -static void printLineState(const LineState &State) {
>> -  llvm::dbgs() << "State: ";
>> -  for (const ParenState &P : State.Stack) {
>> -    llvm::dbgs() << P.Indent << "|" << P.LastSpace << "|" <<
>> P.NestedBlockIndent
>> -                 << " ";
>> -  }
>> -  llvm::dbgs() << State.NextToken->TokenText << "\n";
>> -}
>> -#endif
>> -
>> -void UnwrappedLineFormatter::reconstructPath(LineState &State,
>> -                                             StateNode *Current) {
>> -  std::deque<StateNode *> Path;
>> -  // We do not need a break before the initial token.
>> -  while (Current->Previous) {
>> -    Path.push_front(Current);
>> -    Current = Current->Previous;
>> -  }
>> -  for (std::deque<StateNode *>::iterator I = Path.begin(), E =
>> Path.end();
>> -       I != E; ++I) {
>> -    unsigned Penalty = 0;
>> -    formatChildren(State, (*I)->NewLine, /*DryRun=*/false, Penalty);
>> -    Penalty += Indenter->addTokenToState(State, (*I)->NewLine, false);
>> -
>> -    DEBUG({
>> -      printLineState((*I)->Previous->State);
>> -      if ((*I)->NewLine) {
>> -        llvm::dbgs() << "Penalty for placing "
>> -                     << (*I)->Previous->State.NextToken->Tok.getName()
>> << ": "
>> -                     << Penalty << "\n";
>> -      }
>> -    });
>> -  }
>> -}
>> -
>> -void UnwrappedLineFormatter::addNextStateToQueue(unsigned Penalty,
>> -                                                 StateNode *PreviousNode,
>> -                                                 bool NewLine, unsigned
>> *Count,
>> -                                                 QueueType *Queue) {
>> -  if (NewLine && !Indenter->canBreak(PreviousNode->State))
>> -    return;
>> -  if (!NewLine && Indenter->mustBreak(PreviousNode->State))
>> -    return;
>> -
>> -  StateNode *Node = new (Allocator.Allocate())
>> -      StateNode(PreviousNode->State, NewLine, PreviousNode);
>> -  if (!formatChildren(Node->State, NewLine, /*DryRun=*/true, Penalty))
>> -    return;
>> -
>> -  Penalty += Indenter->addTokenToState(Node->State, NewLine, true);
>> -
>> -  Queue->push(QueueItem(OrderedPenalty(Penalty, *Count), Node));
>> -  ++(*Count);
>> -}
>> -
>> -bool UnwrappedLineFormatter::formatChildren(LineState &State, bool
>> NewLine,
>> -                                            bool DryRun, unsigned
>> &Penalty) {
>> -  const FormatToken *LBrace = State.NextToken->getPreviousNonComment();
>> -  FormatToken &Previous = *State.NextToken->Previous;
>> -  if (!LBrace || LBrace->isNot(tok::l_brace) || LBrace->BlockKind !=
>> BK_Block ||
>> -      Previous.Children.size() == 0)
>> -    // The previous token does not open a block. Nothing to do. We don't
>> -    // assert so that we can simply call this function for all tokens.
>> -    return true;
>> -
>> -  if (NewLine) {
>> -    int AdditionalIndent = State.Stack.back().Indent -
>> -                           Previous.Children[0]->Level *
>> Style.IndentWidth;
>> -
>> -    Penalty += format(Previous.Children, DryRun, AdditionalIndent,
>> -                      /*FixBadIndentation=*/true);
>> -    return true;
>> -  }
>> -
>> -  if (Previous.Children[0]->First->MustBreakBefore)
>> -    return false;
>> -
>> -  // Cannot merge multiple statements into a single line.
>> -  if (Previous.Children.size() > 1)
>> -    return false;
>> -
>> -  // Cannot merge into one line if this line ends on a comment.
>> -  if (Previous.is(tok::comment))
>> -    return false;
>> -
>> -  // We can't put the closing "}" on a line with a trailing comment.
>> -  if (Previous.Children[0]->Last->isTrailingComment())
>> -    return false;
>> -
>> -  // If the child line exceeds the column limit, we wouldn't want to
>> merge it.
>> -  // We add +2 for the trailing " }".
>> -  if (Style.ColumnLimit > 0 &&
>> -      Previous.Children[0]->Last->TotalLength + State.Column + 2 >
>> -          Style.ColumnLimit)
>> -    return false;
>> -
>> -  if (!DryRun) {
>> -    Whitespaces->replaceWhitespace(
>> -        *Previous.Children[0]->First,
>> -        /*Newlines=*/0, /*IndentLevel=*/0, /*Spaces=*/1,
>> -        /*StartOfTokenColumn=*/State.Column, State.Line->InPPDirective);
>> -  }
>> -  Penalty += format(*Previous.Children[0], State.Column + 1, DryRun);
>> -
>> -  State.Column += 1 + Previous.Children[0]->Last->TotalLength;
>> -  return true;
>> -}
>> -
>>  } // namespace format
>>  } // namespace clang
>>
>> Modified: cfe/trunk/lib/Format/UnwrappedLineFormatter.h
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Format/UnwrappedLineFormatter.h?rev=236974&r1=236973&r2=236974&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/lib/Format/UnwrappedLineFormatter.h (original)
>> +++ cfe/trunk/lib/Format/UnwrappedLineFormatter.h Mon May 11 03:21:35 2015
>> @@ -38,65 +38,12 @@ public:
>>        : Indenter(Indenter), Whitespaces(Whitespaces), Style(Style),
>>          Keywords(Keywords), IncompleteFormat(IncompleteFormat) {}
>>
>> -  unsigned format(const SmallVectorImpl<AnnotatedLine *> &Lines, bool
>> DryRun,
>> -                  int AdditionalIndent = 0, bool FixBadIndentation =
>> false);
>> -
>> -
>> -  /// \brief If the \p State's next token is an r_brace closing a nested
>> block,
>> -  /// format the nested block before it.
>> -  ///
>> -  /// Returns \c true if all children could be placed successfully and
>> adapts
>> -  /// \p Penalty as well as \p State. If \p DryRun is false, also
>> directly
>> -  /// creates changes using \c Whitespaces.
>> -  ///
>> -  /// The crucial idea here is that children always get formatted upon
>> -  /// encountering the closing brace right after the nested block. Now,
>> if we
>> -  /// are currently trying to keep the "}" on the same line (i.e. \p
>> NewLine is
>> -  /// \c false), the entire block has to be kept on the same line (which
>> is only
>> -  /// possible if it fits on the line, only contains a single statement,
>> etc.
>> -  ///
>> -  /// If \p NewLine is true, we format the nested block on separate
>> lines, i.e.
>> -  /// break after the "{", format all lines with correct indentation and
>> the put
>> -  /// the closing "}" on yet another new line.
>> -  ///
>> -  /// This enables us to keep the simple structure of the
>> -  /// \c UnwrappedLineFormatter, where we only have two options for each
>> token:
>> -  /// break or don't break.
>> -  bool formatChildren(LineState &State, bool NewLine, bool DryRun,
>> -                      unsigned &Penalty);
>> +  /// \brief Format the current block and return the penalty.
>> +  unsigned format(const SmallVectorImpl<AnnotatedLine *> &Lines,
>> +                  bool DryRun = false, int AdditionalIndent = 0,
>> +                  bool FixBadIndentation = false);
>>
>>  private:
>> -  /// \brief Formats an \c AnnotatedLine and returns the penalty.
>> -  ///
>> -  /// If \p DryRun is \c false, directly applies the changes.
>> -  unsigned format(const AnnotatedLine &Line, unsigned FirstIndent,
>> -                  bool DryRun);
>> -
>> -  /// \brief An edge in the solution space from \c Previous->State to \c
>> State,
>> -  /// inserting a newline dependent on the \c NewLine.
>> -  struct StateNode {
>> -    StateNode(const LineState &State, bool NewLine, StateNode *Previous)
>> -        : State(State), NewLine(NewLine), Previous(Previous) {}
>> -    LineState State;
>> -    bool NewLine;
>> -    StateNode *Previous;
>> -  };
>> -
>> -  /// \brief A pair of <penalty, count> that is used to prioritize the
>> BFS on.
>> -  ///
>> -  /// In case of equal penalties, we want to prefer states that were
>> inserted
>> -  /// first. During state generation we make sure that we insert states
>> first
>> -  /// that break the line as late as possible.
>> -  typedef std::pair<unsigned, unsigned> OrderedPenalty;
>> -
>> -  /// \brief An item in the prioritized BFS search queue. The \c
>> StateNode's
>> -  /// \c State has the given \c OrderedPenalty.
>> -  typedef std::pair<OrderedPenalty, StateNode *> QueueItem;
>> -
>> -  /// \brief The BFS queue type.
>> -  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
>> @@ -133,44 +80,16 @@ private:
>>      return Style.ColumnLimit - (InPPDirective ? 2 : 0);
>>    }
>>
>> -  struct CompareLineStatePointers {
>> -    bool operator()(LineState *obj1, LineState *obj2) const {
>> -      return *obj1 < *obj2;
>> -    }
>> -  };
>> -
>> -  /// \brief Analyze the entire solution space starting from \p
>> InitialState.
>> -  ///
>> -  /// This implements a variant of Dijkstra's algorithm on the graph
>> that spans
>> -  /// the solution space (\c LineStates are the nodes). The algorithm
>> tries to
>> -  /// find the shortest path (the one with lowest penalty) from \p
>> InitialState
>> -  /// to a state where all tokens are placed. Returns the penalty.
>> -  ///
>> -  /// If \p DryRun is \c false, directly applies the changes.
>> -  unsigned analyzeSolutionSpace(LineState &InitialState, bool DryRun =
>> false);
>> -
>> -  void reconstructPath(LineState &State, StateNode *Current);
>> -
>> -  /// \brief Add the following state to the analysis queue \c Queue.
>> -  ///
>> -  /// 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, unsigned *Count, QueueType
>> *Queue);
>> -
>> -  ContinuationIndenter *Indenter;
>> -  WhitespaceManager *Whitespaces;
>> -  FormatStyle Style;
>> -  const AdditionalKeywords &Keywords;
>> -
>> -  llvm::SpecificBumpPtrAllocator<StateNode> Allocator;
>> -
>>    // Cache to store the penalty of formatting a vector of AnnotatedLines
>>    // starting from a specific additional offset. Improves performance if
>> there
>>    // are many nested blocks.
>>    std::map<std::pair<const SmallVectorImpl<AnnotatedLine *> *, unsigned>,
>>             unsigned> PenaltyCache;
>>
>> +  ContinuationIndenter *Indenter;
>> +  WhitespaceManager *Whitespaces;
>> +  const FormatStyle &Style;
>> +  const AdditionalKeywords &Keywords;
>>    bool *IncompleteFormat;
>>  };
>>  } // end namespace format
>>
>>
>> _______________________________________________
>> 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/20150511/18fa28ba/attachment.html>


More information about the cfe-commits mailing list