mirror of
https://github.com/llvm/llvm-project.git
synced 2025-05-10 16:36:05 +00:00

1. Never avoid bin packing in static initializers as this can lead to terrible results. 2. If an element has to be broken over multiple lines, break after the following comma. This should be a step forward, but there are still many cases especially with nested static initializers that we handle badly. More patches will follow. llvm-svn: 174061
1071 lines
41 KiB
C++
1071 lines
41 KiB
C++
//===--- Format.cpp - Format C++ code -------------------------------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
///
|
|
/// \file
|
|
/// \brief This file implements functions declared in Format.h. This will be
|
|
/// split into separate files as we go.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#define DEBUG_TYPE "format-formatter"
|
|
|
|
#include "TokenAnnotator.h"
|
|
#include "UnwrappedLineParser.h"
|
|
#include "clang/Basic/Diagnostic.h"
|
|
#include "clang/Basic/OperatorPrecedence.h"
|
|
#include "clang/Basic/SourceManager.h"
|
|
#include "clang/Format/Format.h"
|
|
#include "clang/Frontend/TextDiagnosticPrinter.h"
|
|
#include "clang/Lex/Lexer.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include <string>
|
|
|
|
// Uncomment to get debug output from tests:
|
|
// #define DEBUG_WITH_TYPE(T, X) do { X; } while(0)
|
|
|
|
namespace clang {
|
|
namespace format {
|
|
|
|
FormatStyle getLLVMStyle() {
|
|
FormatStyle LLVMStyle;
|
|
LLVMStyle.ColumnLimit = 80;
|
|
LLVMStyle.MaxEmptyLinesToKeep = 1;
|
|
LLVMStyle.PointerAndReferenceBindToType = false;
|
|
LLVMStyle.AccessModifierOffset = -2;
|
|
LLVMStyle.SplitTemplateClosingGreater = true;
|
|
LLVMStyle.IndentCaseLabels = false;
|
|
LLVMStyle.SpacesBeforeTrailingComments = 1;
|
|
LLVMStyle.BinPackParameters = true;
|
|
LLVMStyle.AllowAllParametersOfDeclarationOnNextLine = true;
|
|
LLVMStyle.AllowReturnTypeOnItsOwnLine = true;
|
|
LLVMStyle.ConstructorInitializerAllOnOneLineOrOnePerLine = false;
|
|
LLVMStyle.AllowShortIfStatementsOnASingleLine = false;
|
|
LLVMStyle.ObjCSpaceBeforeProtocolList = true;
|
|
return LLVMStyle;
|
|
}
|
|
|
|
FormatStyle getGoogleStyle() {
|
|
FormatStyle GoogleStyle;
|
|
GoogleStyle.ColumnLimit = 80;
|
|
GoogleStyle.MaxEmptyLinesToKeep = 1;
|
|
GoogleStyle.PointerAndReferenceBindToType = true;
|
|
GoogleStyle.AccessModifierOffset = -1;
|
|
GoogleStyle.SplitTemplateClosingGreater = false;
|
|
GoogleStyle.IndentCaseLabels = true;
|
|
GoogleStyle.SpacesBeforeTrailingComments = 2;
|
|
GoogleStyle.BinPackParameters = false;
|
|
GoogleStyle.AllowAllParametersOfDeclarationOnNextLine = true;
|
|
GoogleStyle.AllowReturnTypeOnItsOwnLine = false;
|
|
GoogleStyle.ConstructorInitializerAllOnOneLineOrOnePerLine = true;
|
|
GoogleStyle.AllowShortIfStatementsOnASingleLine = false;
|
|
GoogleStyle.ObjCSpaceBeforeProtocolList = false;
|
|
return GoogleStyle;
|
|
}
|
|
|
|
FormatStyle getChromiumStyle() {
|
|
FormatStyle ChromiumStyle = getGoogleStyle();
|
|
ChromiumStyle.AllowAllParametersOfDeclarationOnNextLine = false;
|
|
ChromiumStyle.SplitTemplateClosingGreater = true;
|
|
return ChromiumStyle;
|
|
}
|
|
|
|
struct OptimizationParameters {
|
|
unsigned PenaltyIndentLevel;
|
|
unsigned PenaltyExcessCharacter;
|
|
};
|
|
|
|
/// \brief Manages the whitespaces around tokens and their replacements.
|
|
///
|
|
/// This includes special handling for certain constructs, e.g. the alignment of
|
|
/// trailing line comments.
|
|
class WhitespaceManager {
|
|
public:
|
|
WhitespaceManager(SourceManager &SourceMgr) : SourceMgr(SourceMgr) {}
|
|
|
|
/// \brief Replaces the whitespace in front of \p Tok. Only call once for
|
|
/// each \c AnnotatedToken.
|
|
void replaceWhitespace(const AnnotatedToken &Tok, unsigned NewLines,
|
|
unsigned Spaces, unsigned WhitespaceStartColumn,
|
|
const FormatStyle &Style) {
|
|
// 2+ newlines mean an empty line separating logic scopes.
|
|
if (NewLines >= 2)
|
|
alignComments();
|
|
|
|
// Align line comments if they are trailing or if they continue other
|
|
// trailing comments.
|
|
if (Tok.Type == TT_LineComment &&
|
|
(Tok.Parent != NULL || !Comments.empty())) {
|
|
if (Style.ColumnLimit >=
|
|
Spaces + WhitespaceStartColumn + Tok.FormatTok.TokenLength) {
|
|
Comments.push_back(StoredComment());
|
|
Comments.back().Tok = Tok.FormatTok;
|
|
Comments.back().Spaces = Spaces;
|
|
Comments.back().NewLines = NewLines;
|
|
Comments.back().MinColumn = WhitespaceStartColumn + Spaces;
|
|
Comments.back().MaxColumn =
|
|
Style.ColumnLimit - Spaces - Tok.FormatTok.TokenLength;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// If this line does not have a trailing comment, align the stored comments.
|
|
if (Tok.Children.empty() && Tok.Type != TT_LineComment)
|
|
alignComments();
|
|
storeReplacement(Tok.FormatTok,
|
|
std::string(NewLines, '\n') + std::string(Spaces, ' '));
|
|
}
|
|
|
|
/// \brief Like \c replaceWhitespace, but additionally adds right-aligned
|
|
/// backslashes to escape newlines inside a preprocessor directive.
|
|
///
|
|
/// This function and \c replaceWhitespace have the same behavior if
|
|
/// \c Newlines == 0.
|
|
void replacePPWhitespace(const AnnotatedToken &Tok, unsigned NewLines,
|
|
unsigned Spaces, unsigned WhitespaceStartColumn,
|
|
const FormatStyle &Style) {
|
|
std::string NewLineText;
|
|
if (NewLines > 0) {
|
|
unsigned Offset =
|
|
std::min<int>(Style.ColumnLimit - 1, WhitespaceStartColumn);
|
|
for (unsigned i = 0; i < NewLines; ++i) {
|
|
NewLineText += std::string(Style.ColumnLimit - Offset - 1, ' ');
|
|
NewLineText += "\\\n";
|
|
Offset = 0;
|
|
}
|
|
}
|
|
storeReplacement(Tok.FormatTok, NewLineText + std::string(Spaces, ' '));
|
|
}
|
|
|
|
/// \brief Returns all the \c Replacements created during formatting.
|
|
const tooling::Replacements &generateReplacements() {
|
|
alignComments();
|
|
return Replaces;
|
|
}
|
|
|
|
private:
|
|
/// \brief Structure to store a comment for later layout and alignment.
|
|
struct StoredComment {
|
|
FormatToken Tok;
|
|
unsigned MinColumn;
|
|
unsigned MaxColumn;
|
|
unsigned NewLines;
|
|
unsigned Spaces;
|
|
};
|
|
SmallVector<StoredComment, 16> Comments;
|
|
typedef SmallVector<StoredComment, 16>::iterator comment_iterator;
|
|
|
|
/// \brief Try to align all stashed comments.
|
|
void alignComments() {
|
|
unsigned MinColumn = 0;
|
|
unsigned MaxColumn = UINT_MAX;
|
|
comment_iterator Start = Comments.begin();
|
|
for (comment_iterator I = Comments.begin(), E = Comments.end(); I != E;
|
|
++I) {
|
|
if (I->MinColumn > MaxColumn || I->MaxColumn < MinColumn) {
|
|
alignComments(Start, I, MinColumn);
|
|
MinColumn = I->MinColumn;
|
|
MaxColumn = I->MaxColumn;
|
|
Start = I;
|
|
} else {
|
|
MinColumn = std::max(MinColumn, I->MinColumn);
|
|
MaxColumn = std::min(MaxColumn, I->MaxColumn);
|
|
}
|
|
}
|
|
alignComments(Start, Comments.end(), MinColumn);
|
|
Comments.clear();
|
|
}
|
|
|
|
/// \brief Put all the comments between \p I and \p E into \p Column.
|
|
void alignComments(comment_iterator I, comment_iterator E, unsigned Column) {
|
|
while (I != E) {
|
|
unsigned Spaces = I->Spaces + Column - I->MinColumn;
|
|
storeReplacement(I->Tok, std::string(I->NewLines, '\n') +
|
|
std::string(Spaces, ' '));
|
|
++I;
|
|
}
|
|
}
|
|
|
|
/// \brief Stores \p Text as the replacement for the whitespace in front of
|
|
/// \p Tok.
|
|
void storeReplacement(const FormatToken &Tok, const std::string Text) {
|
|
// Don't create a replacement, if it does not change anything.
|
|
if (StringRef(SourceMgr.getCharacterData(Tok.WhiteSpaceStart),
|
|
Tok.WhiteSpaceLength) == Text)
|
|
return;
|
|
|
|
Replaces.insert(tooling::Replacement(SourceMgr, Tok.WhiteSpaceStart,
|
|
Tok.WhiteSpaceLength, Text));
|
|
}
|
|
|
|
SourceManager &SourceMgr;
|
|
tooling::Replacements Replaces;
|
|
};
|
|
|
|
class UnwrappedLineFormatter {
|
|
public:
|
|
UnwrappedLineFormatter(const FormatStyle &Style, SourceManager &SourceMgr,
|
|
const AnnotatedLine &Line, unsigned FirstIndent,
|
|
const AnnotatedToken &RootToken,
|
|
WhitespaceManager &Whitespaces, bool StructuralError)
|
|
: Style(Style), SourceMgr(SourceMgr), Line(Line),
|
|
FirstIndent(FirstIndent), RootToken(RootToken),
|
|
Whitespaces(Whitespaces) {
|
|
Parameters.PenaltyIndentLevel = 20;
|
|
Parameters.PenaltyExcessCharacter = 1000000;
|
|
}
|
|
|
|
/// \brief Formats an \c UnwrappedLine.
|
|
///
|
|
/// \returns The column after the last token in the last line of the
|
|
/// \c UnwrappedLine.
|
|
unsigned format() {
|
|
// Initialize state dependent on indent.
|
|
LineState State;
|
|
State.Column = FirstIndent;
|
|
State.NextToken = &RootToken;
|
|
State.Stack.push_back(ParenState(FirstIndent + 4, FirstIndent,
|
|
!Style.BinPackParameters));
|
|
State.VariablePos = 0;
|
|
State.LineContainsContinuedForLoopSection = false;
|
|
|
|
DEBUG({
|
|
DebugTokenState(*State.NextToken);
|
|
});
|
|
|
|
// The first token has already been indented and thus consumed.
|
|
moveStateToNextToken(State);
|
|
|
|
// Start iterating at 1 as we have correctly formatted of Token #0 above.
|
|
while (State.NextToken != NULL) {
|
|
if (State.NextToken->Type == TT_ImplicitStringLiteral) {
|
|
// Calculating the column is important for aligning trailing comments.
|
|
// FIXME: This does not seem to happen in conjunction with escaped
|
|
// newlines. If it does, fix!
|
|
State.Column += State.NextToken->FormatTok.WhiteSpaceLength +
|
|
State.NextToken->FormatTok.TokenLength;
|
|
State.NextToken = State.NextToken->Children.empty()
|
|
? NULL : &State.NextToken->Children[0];
|
|
} else if (Line.Last->TotalLength <= getColumnLimit() - FirstIndent) {
|
|
addTokenToState(false, false, State);
|
|
} else {
|
|
unsigned NoBreak = calcPenalty(State, false, UINT_MAX);
|
|
unsigned Break = calcPenalty(State, true, NoBreak);
|
|
DEBUG({
|
|
if (Break < NoBreak)
|
|
llvm::errs() << "\n";
|
|
else
|
|
llvm::errs() << " ";
|
|
llvm::errs() << "<";
|
|
DebugPenalty(Break, Break < NoBreak);
|
|
llvm::errs() << "/";
|
|
DebugPenalty(NoBreak, !(Break < NoBreak));
|
|
llvm::errs() << "> ";
|
|
DebugTokenState(*State.NextToken);
|
|
});
|
|
addTokenToState(Break < NoBreak, false, State);
|
|
if (State.NextToken != NULL &&
|
|
State.NextToken->Parent->Type == TT_CtorInitializerColon) {
|
|
if (Style.ConstructorInitializerAllOnOneLineOrOnePerLine &&
|
|
Line.Last->TotalLength > getColumnLimit() - State.Column - 1)
|
|
State.Stack.back().BreakAfterComma = true;
|
|
}
|
|
}
|
|
}
|
|
DEBUG(llvm::errs() << "\n");
|
|
return State.Column;
|
|
}
|
|
|
|
private:
|
|
void DebugTokenState(const AnnotatedToken &AnnotatedTok) {
|
|
const Token &Tok = AnnotatedTok.FormatTok.Tok;
|
|
llvm::errs() << StringRef(SourceMgr.getCharacterData(Tok.getLocation()),
|
|
Tok.getLength());
|
|
llvm::errs();
|
|
}
|
|
|
|
void DebugPenalty(unsigned Penalty, bool Winner) {
|
|
llvm::errs().changeColor(Winner ? raw_ostream::GREEN : raw_ostream::RED);
|
|
if (Penalty == UINT_MAX)
|
|
llvm::errs() << "MAX";
|
|
else
|
|
llvm::errs() << Penalty;
|
|
llvm::errs().resetColor();
|
|
}
|
|
|
|
struct ParenState {
|
|
ParenState(unsigned Indent, unsigned LastSpace, bool AvoidBinPacking)
|
|
: Indent(Indent), LastSpace(LastSpace), AssignmentColumn(0),
|
|
FirstLessLess(0), BreakBeforeClosingBrace(false), QuestionColumn(0),
|
|
AvoidBinPacking(AvoidBinPacking), BreakAfterComma(false),
|
|
HasMultiParameterLine(false) {
|
|
}
|
|
|
|
/// \brief The position to which a specific parenthesis level needs to be
|
|
/// indented.
|
|
unsigned Indent;
|
|
|
|
/// \brief The position of the last space on each level.
|
|
///
|
|
/// Used e.g. to break like:
|
|
/// functionCall(Parameter, otherCall(
|
|
/// OtherParameter));
|
|
unsigned LastSpace;
|
|
|
|
/// \brief This is the column of the first token after an assignment.
|
|
unsigned AssignmentColumn;
|
|
|
|
/// \brief The position the first "<<" operator encountered on each level.
|
|
///
|
|
/// Used to align "<<" operators. 0 if no such operator has been encountered
|
|
/// on a level.
|
|
unsigned FirstLessLess;
|
|
|
|
/// \brief Whether a newline needs to be inserted before the block's closing
|
|
/// brace.
|
|
///
|
|
/// We only want to insert a newline before the closing brace if there also
|
|
/// was a newline after the beginning left brace.
|
|
bool BreakBeforeClosingBrace;
|
|
|
|
/// \brief The column of a \c ? in a conditional expression;
|
|
unsigned QuestionColumn;
|
|
|
|
/// \brief Avoid bin packing, i.e. multiple parameters/elements on multiple
|
|
/// lines, in this context.
|
|
bool AvoidBinPacking;
|
|
|
|
/// \brief Break after the next comma (or all the commas in this context if
|
|
/// \c AvoidBinPacking is \c true).
|
|
bool BreakAfterComma;
|
|
|
|
/// \brief This context already has a line with more than one parameter.
|
|
bool HasMultiParameterLine;
|
|
|
|
bool operator<(const ParenState &Other) const {
|
|
if (Indent != Other.Indent)
|
|
return Indent < Other.Indent;
|
|
if (LastSpace != Other.LastSpace)
|
|
return LastSpace < Other.LastSpace;
|
|
if (AssignmentColumn != Other.AssignmentColumn)
|
|
return AssignmentColumn < Other.AssignmentColumn;
|
|
if (FirstLessLess != Other.FirstLessLess)
|
|
return FirstLessLess < Other.FirstLessLess;
|
|
if (BreakBeforeClosingBrace != Other.BreakBeforeClosingBrace)
|
|
return BreakBeforeClosingBrace;
|
|
if (QuestionColumn != Other.QuestionColumn)
|
|
return QuestionColumn < Other.QuestionColumn;
|
|
if (AvoidBinPacking != Other.AvoidBinPacking)
|
|
return AvoidBinPacking;
|
|
if (BreakAfterComma != Other.BreakAfterComma)
|
|
return BreakAfterComma;
|
|
if (HasMultiParameterLine != Other.HasMultiParameterLine)
|
|
return HasMultiParameterLine;
|
|
return false;
|
|
}
|
|
};
|
|
|
|
/// \brief The current state when indenting a unwrapped line.
|
|
///
|
|
/// As the indenting tries different combinations this is copied by value.
|
|
struct LineState {
|
|
/// \brief The number of used columns in the current line.
|
|
unsigned Column;
|
|
|
|
/// \brief The token that needs to be next formatted.
|
|
const AnnotatedToken *NextToken;
|
|
|
|
/// \brief The column of the first variable name in a variable declaration.
|
|
///
|
|
/// Used to align further variables if necessary.
|
|
unsigned VariablePos;
|
|
|
|
/// \brief \c true if this line contains a continued for-loop section.
|
|
bool LineContainsContinuedForLoopSection;
|
|
|
|
/// \brief A stack keeping track of properties applying to parenthesis
|
|
/// levels.
|
|
std::vector<ParenState> Stack;
|
|
|
|
/// \brief Comparison operator to be able to used \c LineState in \c map.
|
|
bool operator<(const LineState &Other) const {
|
|
if (Other.NextToken != NextToken)
|
|
return Other.NextToken > NextToken;
|
|
if (Other.Column != Column)
|
|
return Other.Column > Column;
|
|
if (Other.VariablePos != VariablePos)
|
|
return Other.VariablePos < VariablePos;
|
|
if (Other.LineContainsContinuedForLoopSection !=
|
|
LineContainsContinuedForLoopSection)
|
|
return LineContainsContinuedForLoopSection;
|
|
return Other.Stack < Stack;
|
|
}
|
|
};
|
|
|
|
/// \brief Appends the next token to \p State and updates information
|
|
/// necessary for indentation.
|
|
///
|
|
/// Puts the token on the current line if \p Newline is \c true and adds a
|
|
/// line break and necessary indentation otherwise.
|
|
///
|
|
/// If \p DryRun is \c false, also creates and stores the required
|
|
/// \c Replacement.
|
|
void addTokenToState(bool Newline, bool DryRun, LineState &State) {
|
|
const AnnotatedToken &Current = *State.NextToken;
|
|
const AnnotatedToken &Previous = *State.NextToken->Parent;
|
|
assert(State.Stack.size());
|
|
unsigned ParenLevel = State.Stack.size() - 1;
|
|
|
|
if (Newline) {
|
|
unsigned WhitespaceStartColumn = State.Column;
|
|
if (Current.is(tok::r_brace)) {
|
|
State.Column = Line.Level * 2;
|
|
} else if (Current.is(tok::string_literal) &&
|
|
Previous.is(tok::string_literal)) {
|
|
State.Column = State.Column - Previous.FormatTok.TokenLength;
|
|
} else if (Current.is(tok::lessless) &&
|
|
State.Stack[ParenLevel].FirstLessLess != 0) {
|
|
State.Column = State.Stack[ParenLevel].FirstLessLess;
|
|
} else if (ParenLevel != 0 &&
|
|
(Previous.is(tok::equal) || Previous.is(tok::coloncolon) ||
|
|
Current.is(tok::period) || Current.is(tok::arrow) ||
|
|
Current.is(tok::question))) {
|
|
// Indent and extra 4 spaces after if we know the current expression is
|
|
// continued. Don't do that on the top level, as we already indent 4
|
|
// there.
|
|
State.Column = std::max(State.Stack.back().LastSpace,
|
|
State.Stack.back().Indent) + 4;
|
|
} else if (Current.Type == TT_ConditionalExpr) {
|
|
State.Column = State.Stack.back().QuestionColumn;
|
|
} else if (Previous.is(tok::comma) && State.VariablePos != 0 &&
|
|
((RootToken.is(tok::kw_for) && ParenLevel == 1) ||
|
|
ParenLevel == 0)) {
|
|
State.Column = State.VariablePos;
|
|
} else if (State.NextToken->Parent->ClosesTemplateDeclaration ||
|
|
Current.Type == TT_StartOfName) {
|
|
State.Column = State.Stack[ParenLevel].Indent - 4;
|
|
} else if (Previous.Type == TT_BinaryOperator &&
|
|
State.Stack.back().AssignmentColumn != 0) {
|
|
State.Column = State.Stack.back().AssignmentColumn;
|
|
} else {
|
|
State.Column = State.Stack[ParenLevel].Indent;
|
|
}
|
|
|
|
if (Previous.is(tok::comma) && !State.Stack.back().AvoidBinPacking)
|
|
State.Stack.back().BreakAfterComma = false;
|
|
|
|
if (RootToken.is(tok::kw_for))
|
|
State.LineContainsContinuedForLoopSection = Previous.isNot(tok::semi);
|
|
|
|
if (!DryRun) {
|
|
if (!Line.InPPDirective)
|
|
Whitespaces.replaceWhitespace(Current, 1, State.Column,
|
|
WhitespaceStartColumn, Style);
|
|
else
|
|
Whitespaces.replacePPWhitespace(Current, 1, State.Column,
|
|
WhitespaceStartColumn, Style);
|
|
}
|
|
|
|
State.Stack[ParenLevel].LastSpace = State.Column;
|
|
if (Current.is(tok::colon) && Current.Type != TT_ConditionalExpr)
|
|
State.Stack[ParenLevel].Indent += 2;
|
|
} else {
|
|
if (Current.is(tok::equal) &&
|
|
(RootToken.is(tok::kw_for) || ParenLevel == 0))
|
|
State.VariablePos = State.Column - Previous.FormatTok.TokenLength;
|
|
|
|
unsigned Spaces = State.NextToken->SpaceRequiredBefore ? 1 : 0;
|
|
if (State.NextToken->Type == TT_LineComment)
|
|
Spaces = Style.SpacesBeforeTrailingComments;
|
|
|
|
if (!DryRun)
|
|
Whitespaces.replaceWhitespace(Current, 0, Spaces, State.Column, Style);
|
|
|
|
// FIXME: Do we need to do this for assignments nested in other
|
|
// expressions?
|
|
if (RootToken.isNot(tok::kw_for) && ParenLevel == 0 &&
|
|
(getPrecedence(Previous) == prec::Assignment ||
|
|
Previous.is(tok::kw_return)))
|
|
State.Stack.back().AssignmentColumn = State.Column + Spaces;
|
|
if (Current.Type != TT_LineComment &&
|
|
(Previous.is(tok::l_paren) || Previous.is(tok::l_brace) ||
|
|
State.NextToken->Parent->Type == TT_TemplateOpener))
|
|
State.Stack[ParenLevel].Indent = State.Column + Spaces;
|
|
if (Previous.is(tok::comma) && Current.Type != TT_LineComment)
|
|
State.Stack[ParenLevel].HasMultiParameterLine = true;
|
|
|
|
State.Column += Spaces;
|
|
if (Current.is(tok::l_paren) && Previous.is(tok::kw_if))
|
|
// Treat the condition inside an if as if it was a second function
|
|
// parameter, i.e. let nested calls have an indent of 4.
|
|
State.Stack.back().LastSpace = State.Column + 1; // 1 is length of "(".
|
|
else if (Previous.is(tok::comma) && ParenLevel != 0)
|
|
// Top-level spaces are exempt as that mostly leads to better results.
|
|
State.Stack.back().LastSpace = State.Column;
|
|
else if ((Previous.Type == TT_BinaryOperator ||
|
|
Previous.Type == TT_ConditionalExpr ||
|
|
Previous.Type == TT_CtorInitializerColon) &&
|
|
getPrecedence(Previous) != prec::Assignment)
|
|
State.Stack.back().LastSpace = State.Column;
|
|
else if (Previous.ParameterCount > 1 &&
|
|
(Previous.is(tok::l_paren) || Previous.is(tok::l_square) ||
|
|
Previous.is(tok::l_brace) ||
|
|
Previous.Type == TT_TemplateOpener))
|
|
// If this function has multiple parameters, indent nested calls from
|
|
// the start of the first parameter.
|
|
State.Stack.back().LastSpace = State.Column;
|
|
}
|
|
|
|
// If we break after an {, we should also break before the corresponding }.
|
|
if (Newline && Previous.is(tok::l_brace))
|
|
State.Stack.back().BreakBeforeClosingBrace = true;
|
|
|
|
if (State.Stack.back().AvoidBinPacking && Newline) {
|
|
// If we are breaking after '(', '{', '<', this is not bin packing unless
|
|
// AllowAllParametersOfDeclarationOnNextLine is false.
|
|
if ((Previous.isNot(tok::l_paren) && Previous.isNot(tok::l_brace) &&
|
|
Previous.Type != TT_TemplateOpener) ||
|
|
(!Style.AllowAllParametersOfDeclarationOnNextLine &&
|
|
Line.MustBeDeclaration))
|
|
State.Stack.back().BreakAfterComma = true;
|
|
|
|
// Any break on this level means that the parent level has been broken
|
|
// and we need to avoid bin packing there.
|
|
for (unsigned i = 0, e = State.Stack.size() - 1; i != e; ++i) {
|
|
State.Stack[i].BreakAfterComma = true;
|
|
}
|
|
}
|
|
|
|
moveStateToNextToken(State);
|
|
}
|
|
|
|
/// \brief Mark the next token as consumed in \p State and modify its stacks
|
|
/// accordingly.
|
|
void moveStateToNextToken(LineState &State) {
|
|
const AnnotatedToken &Current = *State.NextToken;
|
|
assert(State.Stack.size());
|
|
|
|
if (Current.is(tok::lessless) && State.Stack.back().FirstLessLess == 0)
|
|
State.Stack.back().FirstLessLess = State.Column;
|
|
if (Current.is(tok::question))
|
|
State.Stack.back().QuestionColumn = State.Column;
|
|
if (Current.is(tok::l_brace) && Current.MatchingParen != NULL &&
|
|
Current.Children[0].isNot(tok::l_brace) &&
|
|
!Current.MatchingParen->MustBreakBefore) {
|
|
AnnotatedToken *End = Current.MatchingParen;
|
|
while (!End->Children.empty() && !End->Children[0].CanBreakBefore) {
|
|
End = &End->Children[0];
|
|
}
|
|
unsigned Length = End->TotalLength - Current.TotalLength + 1;
|
|
if (Length + State.Column > getColumnLimit())
|
|
State.Stack.back().BreakAfterComma = true;
|
|
}
|
|
|
|
// If we encounter an opening (, [, { or <, we add a level to our stacks to
|
|
// prepare for the following tokens.
|
|
if (Current.is(tok::l_paren) || Current.is(tok::l_square) ||
|
|
Current.is(tok::l_brace) ||
|
|
State.NextToken->Type == TT_TemplateOpener) {
|
|
unsigned NewIndent;
|
|
bool AvoidBinPacking;
|
|
if (Current.is(tok::l_brace)) {
|
|
NewIndent = 2 + State.Stack.back().LastSpace;
|
|
AvoidBinPacking = false;
|
|
} else {
|
|
NewIndent = 4 + State.Stack.back().LastSpace;
|
|
AvoidBinPacking = !Style.BinPackParameters;
|
|
}
|
|
State.Stack.push_back(ParenState(NewIndent, State.Stack.back().LastSpace,
|
|
AvoidBinPacking));
|
|
}
|
|
|
|
// If we encounter a closing ), ], } or >, we can remove a level from our
|
|
// stacks.
|
|
if (Current.is(tok::r_paren) || Current.is(tok::r_square) ||
|
|
(Current.is(tok::r_brace) && State.NextToken != &RootToken) ||
|
|
State.NextToken->Type == TT_TemplateCloser) {
|
|
State.Stack.pop_back();
|
|
}
|
|
|
|
if (State.NextToken->Children.empty())
|
|
State.NextToken = NULL;
|
|
else
|
|
State.NextToken = &State.NextToken->Children[0];
|
|
|
|
State.Column += Current.FormatTok.TokenLength;
|
|
}
|
|
|
|
unsigned getColumnLimit() {
|
|
return Style.ColumnLimit - (Line.InPPDirective ? 1 : 0);
|
|
}
|
|
|
|
/// \brief Calculate the number of lines needed to format the remaining part
|
|
/// of the unwrapped line.
|
|
///
|
|
/// Assumes the formatting so far has led to
|
|
/// the \c LineSta \p State. If \p NewLine is set, a new line will be
|
|
/// added after the previous token.
|
|
///
|
|
/// \param StopAt is used for optimization. If we can determine that we'll
|
|
/// definitely need at least \p StopAt additional lines, we already know of a
|
|
/// better solution.
|
|
unsigned calcPenalty(LineState State, bool NewLine, unsigned StopAt) {
|
|
// We are at the end of the unwrapped line, so we don't need any more lines.
|
|
if (State.NextToken == NULL)
|
|
return 0;
|
|
|
|
if (!NewLine && State.NextToken->MustBreakBefore)
|
|
return UINT_MAX;
|
|
if (NewLine && !State.NextToken->CanBreakBefore &&
|
|
!(State.NextToken->is(tok::r_brace) &&
|
|
State.Stack.back().BreakBeforeClosingBrace))
|
|
return UINT_MAX;
|
|
if (!NewLine && State.NextToken->is(tok::r_brace) &&
|
|
State.Stack.back().BreakBeforeClosingBrace)
|
|
return UINT_MAX;
|
|
if (!NewLine && State.NextToken->Parent->is(tok::semi) &&
|
|
State.LineContainsContinuedForLoopSection)
|
|
return UINT_MAX;
|
|
if (!NewLine && State.NextToken->Parent->is(tok::comma) &&
|
|
State.NextToken->Type != TT_LineComment &&
|
|
State.Stack.back().BreakAfterComma)
|
|
return UINT_MAX;
|
|
// Trying to insert a parameter on a new line if there are already more than
|
|
// one parameter on the current line is bin packing.
|
|
if (NewLine && State.NextToken->Parent->is(tok::comma) &&
|
|
State.Stack.back().HasMultiParameterLine &&
|
|
State.Stack.back().AvoidBinPacking)
|
|
return UINT_MAX;
|
|
if (!NewLine && (State.NextToken->Type == TT_CtorInitializerColon ||
|
|
(State.NextToken->Parent->ClosesTemplateDeclaration &&
|
|
State.Stack.size() == 1)))
|
|
return UINT_MAX;
|
|
|
|
unsigned CurrentPenalty = 0;
|
|
if (NewLine)
|
|
CurrentPenalty += Parameters.PenaltyIndentLevel * State.Stack.size() +
|
|
State.NextToken->SplitPenalty;
|
|
|
|
addTokenToState(NewLine, true, State);
|
|
|
|
// Exceeding column limit is bad, assign penalty.
|
|
if (State.Column > getColumnLimit()) {
|
|
unsigned ExcessCharacters = State.Column - getColumnLimit();
|
|
CurrentPenalty += Parameters.PenaltyExcessCharacter * ExcessCharacters;
|
|
}
|
|
|
|
if (StopAt <= CurrentPenalty)
|
|
return UINT_MAX;
|
|
StopAt -= CurrentPenalty;
|
|
StateMap::iterator I = Memory.find(State);
|
|
if (I != Memory.end()) {
|
|
// If this state has already been examined, we can safely return the
|
|
// previous result if we
|
|
// - have not hit the optimatization (and thus returned UINT_MAX) OR
|
|
// - are now computing for a smaller or equal StopAt.
|
|
unsigned SavedResult = I->second.first;
|
|
unsigned SavedStopAt = I->second.second;
|
|
if (SavedResult != UINT_MAX)
|
|
return SavedResult + CurrentPenalty;
|
|
else if (StopAt <= SavedStopAt)
|
|
return UINT_MAX;
|
|
}
|
|
|
|
unsigned NoBreak = calcPenalty(State, false, StopAt);
|
|
unsigned WithBreak = calcPenalty(State, true, std::min(StopAt, NoBreak));
|
|
unsigned Result = std::min(NoBreak, WithBreak);
|
|
|
|
// We have to store 'Result' without adding 'CurrentPenalty' as the latter
|
|
// can depend on 'NewLine'.
|
|
Memory[State] = std::pair<unsigned, unsigned>(Result, StopAt);
|
|
|
|
return Result == UINT_MAX ? UINT_MAX : Result + CurrentPenalty;
|
|
}
|
|
|
|
FormatStyle Style;
|
|
SourceManager &SourceMgr;
|
|
const AnnotatedLine &Line;
|
|
const unsigned FirstIndent;
|
|
const AnnotatedToken &RootToken;
|
|
WhitespaceManager &Whitespaces;
|
|
|
|
// A map from an indent state to a pair (Result, Used-StopAt).
|
|
typedef std::map<LineState, std::pair<unsigned, unsigned> > StateMap;
|
|
StateMap Memory;
|
|
|
|
OptimizationParameters Parameters;
|
|
};
|
|
|
|
class LexerBasedFormatTokenSource : public FormatTokenSource {
|
|
public:
|
|
LexerBasedFormatTokenSource(Lexer &Lex, SourceManager &SourceMgr)
|
|
: GreaterStashed(false), Lex(Lex), SourceMgr(SourceMgr),
|
|
IdentTable(Lex.getLangOpts()) {
|
|
Lex.SetKeepWhitespaceMode(true);
|
|
}
|
|
|
|
virtual FormatToken getNextToken() {
|
|
if (GreaterStashed) {
|
|
FormatTok.NewlinesBefore = 0;
|
|
FormatTok.WhiteSpaceStart =
|
|
FormatTok.Tok.getLocation().getLocWithOffset(1);
|
|
FormatTok.WhiteSpaceLength = 0;
|
|
GreaterStashed = false;
|
|
return FormatTok;
|
|
}
|
|
|
|
FormatTok = FormatToken();
|
|
Lex.LexFromRawLexer(FormatTok.Tok);
|
|
StringRef Text = rawTokenText(FormatTok.Tok);
|
|
FormatTok.WhiteSpaceStart = FormatTok.Tok.getLocation();
|
|
if (SourceMgr.getFileOffset(FormatTok.WhiteSpaceStart) == 0)
|
|
FormatTok.IsFirst = true;
|
|
|
|
// Consume and record whitespace until we find a significant token.
|
|
while (FormatTok.Tok.is(tok::unknown)) {
|
|
FormatTok.NewlinesBefore += Text.count('\n');
|
|
FormatTok.HasUnescapedNewline =
|
|
Text.count("\\\n") != FormatTok.NewlinesBefore;
|
|
FormatTok.WhiteSpaceLength += FormatTok.Tok.getLength();
|
|
|
|
if (FormatTok.Tok.is(tok::eof))
|
|
return FormatTok;
|
|
Lex.LexFromRawLexer(FormatTok.Tok);
|
|
Text = rawTokenText(FormatTok.Tok);
|
|
}
|
|
|
|
// Now FormatTok is the next non-whitespace token.
|
|
FormatTok.TokenLength = Text.size();
|
|
|
|
// In case the token starts with escaped newlines, we want to
|
|
// take them into account as whitespace - this pattern is quite frequent
|
|
// in macro definitions.
|
|
// FIXME: What do we want to do with other escaped spaces, and escaped
|
|
// spaces or newlines in the middle of tokens?
|
|
// FIXME: Add a more explicit test.
|
|
unsigned i = 0;
|
|
while (i + 1 < Text.size() && Text[i] == '\\' && Text[i + 1] == '\n') {
|
|
// FIXME: ++FormatTok.NewlinesBefore is missing...
|
|
FormatTok.WhiteSpaceLength += 2;
|
|
FormatTok.TokenLength -= 2;
|
|
i += 2;
|
|
}
|
|
|
|
if (FormatTok.Tok.is(tok::raw_identifier)) {
|
|
IdentifierInfo &Info = IdentTable.get(Text);
|
|
FormatTok.Tok.setIdentifierInfo(&Info);
|
|
FormatTok.Tok.setKind(Info.getTokenID());
|
|
}
|
|
|
|
if (FormatTok.Tok.is(tok::greatergreater)) {
|
|
FormatTok.Tok.setKind(tok::greater);
|
|
GreaterStashed = true;
|
|
}
|
|
|
|
return FormatTok;
|
|
}
|
|
|
|
private:
|
|
FormatToken FormatTok;
|
|
bool GreaterStashed;
|
|
Lexer &Lex;
|
|
SourceManager &SourceMgr;
|
|
IdentifierTable IdentTable;
|
|
|
|
/// Returns the text of \c FormatTok.
|
|
StringRef rawTokenText(Token &Tok) {
|
|
return StringRef(SourceMgr.getCharacterData(Tok.getLocation()),
|
|
Tok.getLength());
|
|
}
|
|
};
|
|
|
|
class Formatter : public UnwrappedLineConsumer {
|
|
public:
|
|
Formatter(DiagnosticsEngine &Diag, const FormatStyle &Style, Lexer &Lex,
|
|
SourceManager &SourceMgr,
|
|
const std::vector<CharSourceRange> &Ranges)
|
|
: Diag(Diag), Style(Style), Lex(Lex), SourceMgr(SourceMgr),
|
|
Whitespaces(SourceMgr), Ranges(Ranges) {
|
|
}
|
|
|
|
virtual ~Formatter() {}
|
|
|
|
tooling::Replacements format() {
|
|
LexerBasedFormatTokenSource Tokens(Lex, SourceMgr);
|
|
UnwrappedLineParser Parser(Diag, Style, Tokens, *this);
|
|
StructuralError = Parser.parse();
|
|
unsigned PreviousEndOfLineColumn = 0;
|
|
for (unsigned i = 0, e = AnnotatedLines.size(); i != e; ++i) {
|
|
TokenAnnotator Annotator(Style, SourceMgr, Lex, AnnotatedLines[i]);
|
|
Annotator.annotate();
|
|
}
|
|
for (std::vector<AnnotatedLine>::iterator I = AnnotatedLines.begin(),
|
|
E = AnnotatedLines.end();
|
|
I != E; ++I) {
|
|
const AnnotatedLine &TheLine = *I;
|
|
if (touchesRanges(TheLine) && TheLine.Type != LT_Invalid) {
|
|
unsigned Indent =
|
|
formatFirstToken(TheLine.First, TheLine.Level,
|
|
TheLine.InPPDirective, PreviousEndOfLineColumn);
|
|
tryFitMultipleLinesInOne(Indent, I, E);
|
|
UnwrappedLineFormatter Formatter(Style, SourceMgr, TheLine, Indent,
|
|
TheLine.First, Whitespaces,
|
|
StructuralError);
|
|
PreviousEndOfLineColumn = Formatter.format();
|
|
} else {
|
|
// 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, and return the result.
|
|
PreviousEndOfLineColumn =
|
|
SourceMgr.getSpellingColumnNumber(
|
|
TheLine.Last->FormatTok.Tok.getLocation()) +
|
|
Lex.MeasureTokenLength(TheLine.Last->FormatTok.Tok.getLocation(),
|
|
SourceMgr, Lex.getLangOpts()) - 1;
|
|
}
|
|
}
|
|
return Whitespaces.generateReplacements();
|
|
}
|
|
|
|
private:
|
|
/// \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.
|
|
///
|
|
/// Returns whether the resulting \c Line can fit in a single line.
|
|
void tryFitMultipleLinesInOne(unsigned Indent,
|
|
std::vector<AnnotatedLine>::iterator &I,
|
|
std::vector<AnnotatedLine>::iterator E) {
|
|
unsigned Limit = Style.ColumnLimit - (I->InPPDirective ? 1 : 0) - Indent;
|
|
|
|
// We can never merge stuff if there are trailing line comments.
|
|
if (I->Last->Type == TT_LineComment)
|
|
return;
|
|
|
|
// Check whether the UnwrappedLine can be put onto a single line. If
|
|
// so, this is bound to be the optimal solution (by definition) and we
|
|
// don't need to analyze the entire solution space.
|
|
if (I->Last->TotalLength > Limit)
|
|
return;
|
|
Limit -= I->Last->TotalLength;
|
|
|
|
if (I + 1 == E || (I + 1)->Type == LT_Invalid)
|
|
return;
|
|
|
|
if (I->Last->is(tok::l_brace)) {
|
|
tryMergeSimpleBlock(I, E, Limit);
|
|
} else if (I->First.is(tok::kw_if)) {
|
|
tryMergeSimpleIf(I, E, Limit);
|
|
} else if (I->InPPDirective && (I->First.FormatTok.HasUnescapedNewline ||
|
|
I->First.FormatTok.IsFirst)) {
|
|
tryMergeSimplePPDirective(I, E, Limit);
|
|
}
|
|
return;
|
|
}
|
|
|
|
void tryMergeSimplePPDirective(std::vector<AnnotatedLine>::iterator &I,
|
|
std::vector<AnnotatedLine>::iterator E,
|
|
unsigned Limit) {
|
|
AnnotatedLine &Line = *I;
|
|
if (!(I + 1)->InPPDirective || (I + 1)->First.FormatTok.HasUnescapedNewline)
|
|
return;
|
|
if (I + 2 != E && (I + 2)->InPPDirective &&
|
|
!(I + 2)->First.FormatTok.HasUnescapedNewline)
|
|
return;
|
|
if (1 + (I + 1)->Last->TotalLength > Limit)
|
|
return;
|
|
join(Line, *(++I));
|
|
}
|
|
|
|
void tryMergeSimpleIf(std::vector<AnnotatedLine>::iterator &I,
|
|
std::vector<AnnotatedLine>::iterator E,
|
|
unsigned Limit) {
|
|
if (!Style.AllowShortIfStatementsOnASingleLine)
|
|
return;
|
|
if ((I + 1)->InPPDirective != I->InPPDirective ||
|
|
((I + 1)->InPPDirective &&
|
|
(I + 1)->First.FormatTok.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.is(tok::kw_if) || (I + 1)->First.Type == TT_LineComment)
|
|
return;
|
|
// Only inline simple if's (no nested if or else).
|
|
if (I + 2 != E && (I + 2)->First.is(tok::kw_else))
|
|
return;
|
|
join(Line, *(++I));
|
|
}
|
|
|
|
void tryMergeSimpleBlock(std::vector<AnnotatedLine>::iterator &I,
|
|
std::vector<AnnotatedLine>::iterator E,
|
|
unsigned Limit) {
|
|
// 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;
|
|
bool AllowedTokens =
|
|
Line.First.isNot(tok::kw_if) && Line.First.isNot(tok::kw_while) &&
|
|
Line.First.isNot(tok::kw_do) && Line.First.isNot(tok::r_brace) &&
|
|
Line.First.isNot(tok::kw_else) && Line.First.isNot(tok::kw_try) &&
|
|
Line.First.isNot(tok::kw_catch) && Line.First.isNot(tok::kw_for) &&
|
|
// This gets rid of all ObjC @ keywords and methods.
|
|
Line.First.isNot(tok::at) && Line.First.isNot(tok::minus) &&
|
|
Line.First.isNot(tok::plus);
|
|
if (!AllowedTokens)
|
|
return;
|
|
|
|
AnnotatedToken *Tok = &(I + 1)->First;
|
|
if (Tok->Children.empty() && Tok->is(tok::r_brace) &&
|
|
!Tok->MustBreakBefore && Tok->TotalLength <= Limit) {
|
|
Tok->SpaceRequiredBefore = false;
|
|
join(Line, *(I + 1));
|
|
I += 1;
|
|
} else {
|
|
// 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->is(tok::l_brace) || Tok->is(tok::r_brace))
|
|
return;
|
|
Tok = Tok->Children.empty() ? NULL : &Tok->Children.back();
|
|
} while (Tok != NULL);
|
|
|
|
// Last, check that the third line contains a single closing brace.
|
|
Tok = &(I + 2)->First;
|
|
if (!Tok->Children.empty() || Tok->isNot(tok::r_brace) ||
|
|
Tok->MustBreakBefore)
|
|
return;
|
|
|
|
join(Line, *(I + 1));
|
|
join(Line, *(I + 2));
|
|
I += 2;
|
|
}
|
|
}
|
|
|
|
bool nextTwoLinesFitInto(std::vector<AnnotatedLine>::iterator I,
|
|
unsigned Limit) {
|
|
return 1 + (I + 1)->Last->TotalLength + 1 + (I + 2)->Last->TotalLength <=
|
|
Limit;
|
|
}
|
|
|
|
void join(AnnotatedLine &A, const AnnotatedLine &B) {
|
|
A.Last->Children.push_back(B.First);
|
|
while (!A.Last->Children.empty()) {
|
|
A.Last->Children[0].Parent = A.Last;
|
|
A.Last = &A.Last->Children[0];
|
|
}
|
|
}
|
|
|
|
bool touchesRanges(const AnnotatedLine &TheLine) {
|
|
const FormatToken *First = &TheLine.First.FormatTok;
|
|
const FormatToken *Last = &TheLine.Last->FormatTok;
|
|
CharSourceRange LineRange = CharSourceRange::getTokenRange(
|
|
First->Tok.getLocation(), Last->Tok.getLocation());
|
|
for (unsigned i = 0, e = Ranges.size(); i != e; ++i) {
|
|
if (!SourceMgr.isBeforeInTranslationUnit(LineRange.getEnd(),
|
|
Ranges[i].getBegin()) &&
|
|
!SourceMgr.isBeforeInTranslationUnit(Ranges[i].getEnd(),
|
|
LineRange.getBegin()))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
virtual void consumeUnwrappedLine(const UnwrappedLine &TheLine) {
|
|
AnnotatedLines.push_back(AnnotatedLine(TheLine));
|
|
}
|
|
|
|
/// \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.
|
|
unsigned formatFirstToken(const AnnotatedToken &RootToken, unsigned Level,
|
|
bool InPPDirective,
|
|
unsigned PreviousEndOfLineColumn) {
|
|
const FormatToken &Tok = RootToken.FormatTok;
|
|
if (!Tok.WhiteSpaceStart.isValid() || StructuralError)
|
|
return SourceMgr.getSpellingColumnNumber(Tok.Tok.getLocation()) - 1;
|
|
|
|
unsigned Newlines =
|
|
std::min(Tok.NewlinesBefore, Style.MaxEmptyLinesToKeep + 1);
|
|
if (Newlines == 0 && !Tok.IsFirst)
|
|
Newlines = 1;
|
|
unsigned Indent = Level * 2;
|
|
|
|
bool IsAccessModifier = false;
|
|
if (RootToken.is(tok::kw_public) || RootToken.is(tok::kw_protected) ||
|
|
RootToken.is(tok::kw_private))
|
|
IsAccessModifier = true;
|
|
else if (RootToken.is(tok::at) && !RootToken.Children.empty() &&
|
|
(RootToken.Children[0].isObjCAtKeyword(tok::objc_public) ||
|
|
RootToken.Children[0].isObjCAtKeyword(tok::objc_protected) ||
|
|
RootToken.Children[0].isObjCAtKeyword(tok::objc_package) ||
|
|
RootToken.Children[0].isObjCAtKeyword(tok::objc_private)))
|
|
IsAccessModifier = true;
|
|
|
|
if (IsAccessModifier &&
|
|
static_cast<int>(Indent) + Style.AccessModifierOffset >= 0)
|
|
Indent += Style.AccessModifierOffset;
|
|
if (!InPPDirective || Tok.HasUnescapedNewline) {
|
|
Whitespaces.replaceWhitespace(RootToken, Newlines, Indent, 0, Style);
|
|
} else {
|
|
Whitespaces.replacePPWhitespace(RootToken, Newlines, Indent,
|
|
PreviousEndOfLineColumn, Style);
|
|
}
|
|
return Indent;
|
|
}
|
|
|
|
DiagnosticsEngine &Diag;
|
|
FormatStyle Style;
|
|
Lexer &Lex;
|
|
SourceManager &SourceMgr;
|
|
WhitespaceManager Whitespaces;
|
|
std::vector<CharSourceRange> Ranges;
|
|
std::vector<AnnotatedLine> AnnotatedLines;
|
|
bool StructuralError;
|
|
};
|
|
|
|
tooling::Replacements
|
|
reformat(const FormatStyle &Style, Lexer &Lex, SourceManager &SourceMgr,
|
|
std::vector<CharSourceRange> Ranges, DiagnosticConsumer *DiagClient) {
|
|
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
|
|
OwningPtr<DiagnosticConsumer> DiagPrinter;
|
|
if (DiagClient == 0) {
|
|
DiagPrinter.reset(new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts));
|
|
DiagPrinter->BeginSourceFile(Lex.getLangOpts(), Lex.getPP());
|
|
DiagClient = DiagPrinter.get();
|
|
}
|
|
DiagnosticsEngine Diagnostics(
|
|
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
|
|
DiagClient, false);
|
|
Diagnostics.setSourceManager(&SourceMgr);
|
|
Formatter formatter(Diagnostics, Style, Lex, SourceMgr, Ranges);
|
|
return formatter.format();
|
|
}
|
|
|
|
LangOptions getFormattingLangOpts() {
|
|
LangOptions LangOpts;
|
|
LangOpts.CPlusPlus = 1;
|
|
LangOpts.CPlusPlus11 = 1;
|
|
LangOpts.Bool = 1;
|
|
LangOpts.ObjC1 = 1;
|
|
LangOpts.ObjC2 = 1;
|
|
return LangOpts;
|
|
}
|
|
|
|
} // namespace format
|
|
} // namespace clang
|