Summary:
The current code would return an incorrect value when a preprocessor
directive is present immediately after the opening brace: this causes
the nanespace end comment fixer to break in some places, for exemple it
would not add the comment in this case:
namespace a {
#define FOO
}
Fixing the computation is simple enough, but it was breaking a feature,
as it would cause comments to be added also when the namespace
declaration was dependant on conditional compilation.
To fix this, a hash of the current preprocessor stack/branches is
computed at the beginning of parseBlock(), so that we explicitely do not
store the OpeningLineIndex when the beginning and end of the block are
not in the same preprocessor conditions.
Tthe hash is computed based on the line, but this could propbably be
improved by using the actual condition, so that clang-format would be
able to match multiple identical #ifdef blocks.
Reviewers: krasimir, djasper
Reviewed By: krasimir
Subscribers: klimek, cfe-commits
Differential Revision: https://reviews.llvm.org/D35483
llvm-svn: 309369
268 lines
9.5 KiB
C++
268 lines
9.5 KiB
C++
//===--- UnwrappedLineParser.h - Format C++ code ----------------*- C++ -*-===//
|
|
//
|
|
// 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 contains the declaration of the UnwrappedLineParser,
|
|
/// which turns a stream of tokens into UnwrappedLines.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef LLVM_CLANG_LIB_FORMAT_UNWRAPPEDLINEPARSER_H
|
|
#define LLVM_CLANG_LIB_FORMAT_UNWRAPPEDLINEPARSER_H
|
|
|
|
#include "FormatToken.h"
|
|
#include "clang/Basic/IdentifierTable.h"
|
|
#include "clang/Format/Format.h"
|
|
#include "llvm/Support/Regex.h"
|
|
#include <list>
|
|
#include <stack>
|
|
|
|
namespace clang {
|
|
namespace format {
|
|
|
|
struct UnwrappedLineNode;
|
|
|
|
/// \brief An unwrapped line is a sequence of \c Token, that we would like to
|
|
/// put on a single line if there was no column limit.
|
|
///
|
|
/// This is used as a main interface between the \c UnwrappedLineParser and the
|
|
/// \c UnwrappedLineFormatter. The key property is that changing the formatting
|
|
/// within an unwrapped line does not affect any other unwrapped lines.
|
|
struct UnwrappedLine {
|
|
UnwrappedLine();
|
|
|
|
// FIXME: Don't use std::list here.
|
|
/// \brief The \c Tokens comprising this \c UnwrappedLine.
|
|
std::list<UnwrappedLineNode> Tokens;
|
|
|
|
/// \brief The indent level of the \c UnwrappedLine.
|
|
unsigned Level;
|
|
|
|
/// \brief Whether this \c UnwrappedLine is part of a preprocessor directive.
|
|
bool InPPDirective;
|
|
|
|
bool MustBeDeclaration;
|
|
|
|
/// \brief If this \c UnwrappedLine closes a block in a sequence of lines,
|
|
/// \c MatchingOpeningBlockLineIndex stores the index of the corresponding
|
|
/// opening line. Otherwise, \c MatchingOpeningBlockLineIndex must be
|
|
/// \c kInvalidIndex.
|
|
size_t MatchingOpeningBlockLineIndex;
|
|
|
|
static const size_t kInvalidIndex = -1;
|
|
};
|
|
|
|
class UnwrappedLineConsumer {
|
|
public:
|
|
virtual ~UnwrappedLineConsumer() {}
|
|
virtual void consumeUnwrappedLine(const UnwrappedLine &Line) = 0;
|
|
virtual void finishRun() = 0;
|
|
};
|
|
|
|
class FormatTokenSource;
|
|
|
|
class UnwrappedLineParser {
|
|
public:
|
|
UnwrappedLineParser(const FormatStyle &Style,
|
|
const AdditionalKeywords &Keywords,
|
|
ArrayRef<FormatToken *> Tokens,
|
|
UnwrappedLineConsumer &Callback);
|
|
|
|
void parse();
|
|
|
|
private:
|
|
void reset();
|
|
void parseFile();
|
|
void parseLevel(bool HasOpeningBrace);
|
|
void parseBlock(bool MustBeDeclaration, bool AddLevel = true,
|
|
bool MunchSemi = true);
|
|
void parseChildBlock();
|
|
void parsePPDirective();
|
|
void parsePPDefine();
|
|
void parsePPIf(bool IfDef);
|
|
void parsePPElIf();
|
|
void parsePPElse();
|
|
void parsePPEndIf();
|
|
void parsePPUnknown();
|
|
void readTokenWithJavaScriptASI();
|
|
void parseStructuralElement();
|
|
bool tryToParseBracedList();
|
|
bool parseBracedList(bool ContinueOnSemicolons = false,
|
|
tok::TokenKind ClosingBraceKind = tok::r_brace);
|
|
void parseParens();
|
|
void parseSquare();
|
|
void parseIfThenElse();
|
|
void parseTryCatch();
|
|
void parseForOrWhileLoop();
|
|
void parseDoWhile();
|
|
void parseLabel();
|
|
void parseCaseLabel();
|
|
void parseSwitch();
|
|
void parseNamespace();
|
|
void parseNew();
|
|
void parseAccessSpecifier();
|
|
bool parseEnum();
|
|
void parseJavaEnumBody();
|
|
// Parses a record (aka class) as a top level element. If ParseAsExpr is true,
|
|
// parses the record as a child block, i.e. if the class declaration is an
|
|
// expression.
|
|
void parseRecord(bool ParseAsExpr = false);
|
|
void parseObjCProtocolList();
|
|
void parseObjCUntilAtEnd();
|
|
void parseObjCInterfaceOrImplementation();
|
|
void parseObjCProtocol();
|
|
void parseJavaScriptEs6ImportExport();
|
|
bool tryToParseLambda();
|
|
bool tryToParseLambdaIntroducer();
|
|
void tryToParseJSFunction();
|
|
void addUnwrappedLine();
|
|
bool eof() const;
|
|
// LevelDifference is the difference of levels after and before the current
|
|
// token. For example:
|
|
// - if the token is '{' and opens a block, LevelDifference is 1.
|
|
// - if the token is '}' and closes a block, LevelDifference is -1.
|
|
void nextToken(int LevelDifference = 0);
|
|
void readToken(int LevelDifference = 0);
|
|
const FormatToken *getPreviousToken();
|
|
|
|
// Decides which comment tokens should be added to the current line and which
|
|
// should be added as comments before the next token.
|
|
//
|
|
// Comments specifies the sequence of comment tokens to analyze. They get
|
|
// either pushed to the current line or added to the comments before the next
|
|
// token.
|
|
//
|
|
// NextTok specifies the next token. A null pointer NextTok is supported, and
|
|
// signifies either the absense of a next token, or that the next token
|
|
// shouldn't be taken into accunt for the analysis.
|
|
void distributeComments(const SmallVectorImpl<FormatToken *> &Comments,
|
|
const FormatToken *NextTok);
|
|
|
|
// Adds the comment preceding the next token to unwrapped lines.
|
|
void flushComments(bool NewlineBeforeNext);
|
|
void pushToken(FormatToken *Tok);
|
|
void calculateBraceTypes(bool ExpectClassBody = false);
|
|
|
|
// Marks a conditional compilation edge (for example, an '#if', '#ifdef',
|
|
// '#else' or merge conflict marker). If 'Unreachable' is true, assumes
|
|
// this branch either cannot be taken (for example '#if false'), or should
|
|
// not be taken in this round.
|
|
void conditionalCompilationCondition(bool Unreachable);
|
|
void conditionalCompilationStart(bool Unreachable);
|
|
void conditionalCompilationAlternative();
|
|
void conditionalCompilationEnd();
|
|
|
|
bool isOnNewLine(const FormatToken &FormatTok);
|
|
|
|
// Compute hash of the current preprocessor branch.
|
|
// This is used to identify the different branches, and thus track if block
|
|
// open and close in the same branch.
|
|
size_t computePPHash() const;
|
|
|
|
// FIXME: We are constantly running into bugs where Line.Level is incorrectly
|
|
// subtracted from beyond 0. Introduce a method to subtract from Line.Level
|
|
// and use that everywhere in the Parser.
|
|
std::unique_ptr<UnwrappedLine> Line;
|
|
|
|
// Comments are sorted into unwrapped lines by whether they are in the same
|
|
// line as the previous token, or not. If not, they belong to the next token.
|
|
// Since the next token might already be in a new unwrapped line, we need to
|
|
// store the comments belonging to that token.
|
|
SmallVector<FormatToken *, 1> CommentsBeforeNextToken;
|
|
FormatToken *FormatTok;
|
|
bool MustBreakBeforeNextToken;
|
|
|
|
// The parsed lines. Only added to through \c CurrentLines.
|
|
SmallVector<UnwrappedLine, 8> Lines;
|
|
|
|
// Preprocessor directives are parsed out-of-order from other unwrapped lines.
|
|
// Thus, we need to keep a list of preprocessor directives to be reported
|
|
// after an unwrapped line that has been started was finished.
|
|
SmallVector<UnwrappedLine, 4> PreprocessorDirectives;
|
|
|
|
// New unwrapped lines are added via CurrentLines.
|
|
// Usually points to \c &Lines. While parsing a preprocessor directive when
|
|
// there is an unfinished previous unwrapped line, will point to
|
|
// \c &PreprocessorDirectives.
|
|
SmallVectorImpl<UnwrappedLine> *CurrentLines;
|
|
|
|
// We store for each line whether it must be a declaration depending on
|
|
// whether we are in a compound statement or not.
|
|
std::vector<bool> DeclarationScopeStack;
|
|
|
|
const FormatStyle &Style;
|
|
const AdditionalKeywords &Keywords;
|
|
|
|
llvm::Regex CommentPragmasRegex;
|
|
|
|
FormatTokenSource *Tokens;
|
|
UnwrappedLineConsumer &Callback;
|
|
|
|
// FIXME: This is a temporary measure until we have reworked the ownership
|
|
// of the format tokens. The goal is to have the actual tokens created and
|
|
// owned outside of and handed into the UnwrappedLineParser.
|
|
ArrayRef<FormatToken *> AllTokens;
|
|
|
|
// Represents preprocessor branch type, so we can find matching
|
|
// #if/#else/#endif directives.
|
|
enum PPBranchKind {
|
|
PP_Conditional, // Any #if, #ifdef, #ifndef, #elif, block outside #if 0
|
|
PP_Unreachable // #if 0 or a conditional preprocessor block inside #if 0
|
|
};
|
|
|
|
struct PPBranch {
|
|
PPBranch(PPBranchKind Kind, size_t Line) : Kind(Kind), Line(Line) {}
|
|
PPBranchKind Kind;
|
|
size_t Line;
|
|
};
|
|
|
|
// Keeps a stack of currently active preprocessor branching directives.
|
|
SmallVector<PPBranch, 16> PPStack;
|
|
|
|
// The \c UnwrappedLineParser re-parses the code for each combination
|
|
// of preprocessor branches that can be taken.
|
|
// To that end, we take the same branch (#if, #else, or one of the #elif
|
|
// branches) for each nesting level of preprocessor branches.
|
|
// \c PPBranchLevel stores the current nesting level of preprocessor
|
|
// branches during one pass over the code.
|
|
int PPBranchLevel;
|
|
|
|
// Contains the current branch (#if, #else or one of the #elif branches)
|
|
// for each nesting level.
|
|
SmallVector<int, 8> PPLevelBranchIndex;
|
|
|
|
// Contains the maximum number of branches at each nesting level.
|
|
SmallVector<int, 8> PPLevelBranchCount;
|
|
|
|
// Contains the number of branches per nesting level we are currently
|
|
// in while parsing a preprocessor branch sequence.
|
|
// This is used to update PPLevelBranchCount at the end of a branch
|
|
// sequence.
|
|
std::stack<int> PPChainBranchIndex;
|
|
|
|
friend class ScopedLineState;
|
|
friend class CompoundStatementIndenter;
|
|
};
|
|
|
|
struct UnwrappedLineNode {
|
|
UnwrappedLineNode() : Tok(nullptr) {}
|
|
UnwrappedLineNode(FormatToken *Tok) : Tok(Tok) {}
|
|
|
|
FormatToken *Tok;
|
|
SmallVector<UnwrappedLine, 0> Children;
|
|
};
|
|
|
|
inline UnwrappedLine::UnwrappedLine() : Level(0), InPPDirective(false),
|
|
MustBeDeclaration(false), MatchingOpeningBlockLineIndex(kInvalidIndex) {}
|
|
|
|
} // end namespace format
|
|
} // end namespace clang
|
|
|
|
#endif
|