Skip to content

Commit

Permalink
[clang-format] Fixup #include guard indents after parseFile()
Browse files Browse the repository at this point in the history
Summary:
When a preprocessor indent closes after the last line of normal code we do not
correctly fixup include guard indents. For example:

  #ifndef HEADER_H
  #define HEADER_H
  #if 1
  int i;
  #  define A 0
  #endif
  #endif

incorrectly reformats to:

  #ifndef HEADER_H
  #define HEADER_H
  #if 1
  int i;
  #    define A 0
  #  endif
  #endif

To resolve this issue we must fixup levels after parseFile(). Delaying
the fixup introduces a new state, so consolidate include guard search
state into an enum.

Reviewers: krasimir, klimek

Reviewed By: krasimir

Subscribers: cfe-commits

Differential Revision: https://reviews.llvm.org/D42035

llvm-svn: 324238
  • Loading branch information
mzeren-vmw committed Feb 5, 2018
1 parent 0a1ff46 commit 0dc13cd
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 26 deletions.
57 changes: 35 additions & 22 deletions clang/lib/Format/UnwrappedLineParser.cpp
Expand Up @@ -234,14 +234,15 @@ UnwrappedLineParser::UnwrappedLineParser(const FormatStyle &Style,
CurrentLines(&Lines), Style(Style), Keywords(Keywords),
CommentPragmasRegex(Style.CommentPragmas), Tokens(nullptr),
Callback(Callback), AllTokens(Tokens), PPBranchLevel(-1),
IfNdefCondition(nullptr), FoundIncludeGuardStart(false),
IncludeGuardRejected(false), FirstStartColumn(FirstStartColumn) {}
IncludeGuard(Style.IndentPPDirectives == FormatStyle::PPDIS_None
? IG_Rejected
: IG_Inited),
IncludeGuardToken(nullptr), FirstStartColumn(FirstStartColumn) {}

void UnwrappedLineParser::reset() {
PPBranchLevel = -1;
IfNdefCondition = nullptr;
FoundIncludeGuardStart = false;
IncludeGuardRejected = false;
IncludeGuard = IG_Inited;
IncludeGuardToken = nullptr;
Line.reset(new UnwrappedLine);
CommentsBeforeNextToken.clear();
FormatTok = nullptr;
Expand All @@ -264,6 +265,14 @@ void UnwrappedLineParser::parse() {

readToken();
parseFile();

// If we found an include guard then all preprocessor directives (other than
// the guard) are over-indented by one.
if (IncludeGuard == IG_Found)
for (auto &Line : Lines)
if (Line.InPPDirective && Line.Level > 0)
--Line.Level;

// Create line with eof token.
pushToken(FormatTok);
addUnwrappedLine();
Expand Down Expand Up @@ -724,26 +733,28 @@ void UnwrappedLineParser::parsePPIf(bool IfDef) {
// If there's a #ifndef on the first line, and the only lines before it are
// comments, it could be an include guard.
bool MaybeIncludeGuard = IfNDef;
if (!IncludeGuardRejected && !FoundIncludeGuardStart && MaybeIncludeGuard) {
if (IncludeGuard == IG_Inited && MaybeIncludeGuard) {
for (auto &Line : Lines) {
if (!Line.Tokens.front().Tok->is(tok::comment)) {
MaybeIncludeGuard = false;
IncludeGuardRejected = true;
IncludeGuard = IG_Rejected;
break;
}
}
}
--PPBranchLevel;
parsePPUnknown();
++PPBranchLevel;
if (!IncludeGuardRejected && !FoundIncludeGuardStart && MaybeIncludeGuard)
IfNdefCondition = IfCondition;
if (IncludeGuard == IG_Inited && MaybeIncludeGuard) {
IncludeGuard = IG_IfNdefed;
IncludeGuardToken = IfCondition;
}
}

void UnwrappedLineParser::parsePPElse() {
// If a potential include guard has an #else, it's not an include guard.
if (FoundIncludeGuardStart && PPBranchLevel == 0)
FoundIncludeGuardStart = false;
if (IncludeGuard == IG_Defined && PPBranchLevel == 0)
IncludeGuard = IG_Rejected;
conditionalCompilationAlternative();
if (PPBranchLevel > -1)
--PPBranchLevel;
Expand All @@ -757,34 +768,37 @@ void UnwrappedLineParser::parsePPEndIf() {
conditionalCompilationEnd();
parsePPUnknown();
// If the #endif of a potential include guard is the last thing in the file,
// then we count it as a real include guard and subtract one from every
// preprocessor indent.
// then we found an include guard.
unsigned TokenPosition = Tokens->getPosition();
FormatToken *PeekNext = AllTokens[TokenPosition];
if (FoundIncludeGuardStart && PPBranchLevel == -1 && PeekNext->is(tok::eof) &&
if (IncludeGuard == IG_Defined && PPBranchLevel == -1 &&
PeekNext->is(tok::eof) &&
Style.IndentPPDirectives != FormatStyle::PPDIS_None)
for (auto &Line : Lines)
if (Line.InPPDirective && Line.Level > 0)
--Line.Level;
IncludeGuard = IG_Found;
}

void UnwrappedLineParser::parsePPDefine() {
nextToken();

if (FormatTok->Tok.getKind() != tok::identifier) {
IncludeGuard = IG_Rejected;
IncludeGuardToken = nullptr;
parsePPUnknown();
return;
}
if (IfNdefCondition && IfNdefCondition->TokenText == FormatTok->TokenText) {
FoundIncludeGuardStart = true;

if (IncludeGuard == IG_IfNdefed &&
IncludeGuardToken->TokenText == FormatTok->TokenText) {
IncludeGuard = IG_Defined;
IncludeGuardToken = nullptr;
for (auto &Line : Lines) {
if (!Line.Tokens.front().Tok->isOneOf(tok::comment, tok::hash)) {
FoundIncludeGuardStart = false;
IncludeGuard = IG_Rejected;
break;
}
}
}
IfNdefCondition = nullptr;

nextToken();
if (FormatTok->Tok.getKind() == tok::l_paren &&
FormatTok->WhitespaceRange.getBegin() ==
Expand All @@ -811,7 +825,6 @@ void UnwrappedLineParser::parsePPUnknown() {
if (Style.IndentPPDirectives == FormatStyle::PPDIS_AfterHash)
Line->Level += PPBranchLevel + 1;
addUnwrappedLine();
IfNdefCondition = nullptr;
}

// Here we blacklist certain tokens that are not usually the first token in an
Expand Down
21 changes: 17 additions & 4 deletions clang/lib/Format/UnwrappedLineParser.h
Expand Up @@ -248,10 +248,23 @@ class UnwrappedLineParser {
// sequence.
std::stack<int> PPChainBranchIndex;

// Contains the #ifndef condition for a potential include guard.
FormatToken *IfNdefCondition;
bool FoundIncludeGuardStart;
bool IncludeGuardRejected;
// Include guard search state. Used to fixup preprocessor indent levels
// so that include guards do not participate in indentation.
enum IncludeGuardState {
IG_Inited,
IG_IfNdefed,
IG_Defined,
IG_Found,
IG_Rejected,
};

// Current state of include guard search.
IncludeGuardState IncludeGuard;

// Points to the #ifndef condition for a potential include guard. Null unless
// IncludeGuardState == IG_IfNdefed.
FormatToken *IncludeGuardToken;

// Contains the first start column where the source begins. This is zero for
// normal source code and may be nonzero when formatting a code fragment that
// does not start at the beginning of the file.
Expand Down
14 changes: 14 additions & 0 deletions clang/unittests/Format/FormatTest.cpp
Expand Up @@ -2547,6 +2547,20 @@ TEST_F(FormatTest, IndentPreprocessorDirectives) {
"#elif FOO\n"
"#endif",
Style);
// Non-identifier #define after potential include guard.
verifyFormat("#ifndef FOO\n"
"# define 1\n"
"#endif\n",
Style);
// #if closes past last non-preprocessor line.
verifyFormat("#ifndef FOO\n"
"#define FOO\n"
"#if 1\n"
"int i;\n"
"# define A 0\n"
"#endif\n"
"#endif\n",
Style);
// FIXME: This doesn't handle the case where there's code between the
// #ifndef and #define but all other conditions hold. This is because when
// the #define line is parsed, UnwrappedLineParser::Lines doesn't hold the
Expand Down

0 comments on commit 0dc13cd

Please sign in to comment.