xref: /freebsd-src/contrib/llvm-project/clang/lib/Format/DefinitionBlockSeparator.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
104eeddc0SDimitry Andric //===--- DefinitionBlockSeparator.cpp ---------------------------*- C++ -*-===//
204eeddc0SDimitry Andric //
304eeddc0SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
404eeddc0SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
504eeddc0SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
604eeddc0SDimitry Andric //
704eeddc0SDimitry Andric //===----------------------------------------------------------------------===//
804eeddc0SDimitry Andric ///
904eeddc0SDimitry Andric /// \file
1004eeddc0SDimitry Andric /// This file implements DefinitionBlockSeparator, a TokenAnalyzer that inserts
1104eeddc0SDimitry Andric /// or removes empty lines separating definition blocks like classes, structs,
1204eeddc0SDimitry Andric /// functions, enums, and namespaces in between.
1304eeddc0SDimitry Andric ///
1404eeddc0SDimitry Andric //===----------------------------------------------------------------------===//
1504eeddc0SDimitry Andric 
1604eeddc0SDimitry Andric #include "DefinitionBlockSeparator.h"
1704eeddc0SDimitry Andric #include "llvm/Support/Debug.h"
1804eeddc0SDimitry Andric #define DEBUG_TYPE "definition-block-separator"
1904eeddc0SDimitry Andric 
2004eeddc0SDimitry Andric namespace clang {
2104eeddc0SDimitry Andric namespace format {
analyze(TokenAnnotator & Annotator,SmallVectorImpl<AnnotatedLine * > & AnnotatedLines,FormatTokenLexer & Tokens)2204eeddc0SDimitry Andric std::pair<tooling::Replacements, unsigned> DefinitionBlockSeparator::analyze(
2304eeddc0SDimitry Andric     TokenAnnotator &Annotator, SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
2404eeddc0SDimitry Andric     FormatTokenLexer &Tokens) {
2504eeddc0SDimitry Andric   assert(Style.SeparateDefinitionBlocks != FormatStyle::SDS_Leave);
2604eeddc0SDimitry Andric   AffectedRangeMgr.computeAffectedLines(AnnotatedLines);
2704eeddc0SDimitry Andric   tooling::Replacements Result;
2804eeddc0SDimitry Andric   separateBlocks(AnnotatedLines, Result, Tokens);
2904eeddc0SDimitry Andric   return {Result, 0};
3004eeddc0SDimitry Andric }
3104eeddc0SDimitry Andric 
separateBlocks(SmallVectorImpl<AnnotatedLine * > & Lines,tooling::Replacements & Result,FormatTokenLexer & Tokens)3204eeddc0SDimitry Andric void DefinitionBlockSeparator::separateBlocks(
3304eeddc0SDimitry Andric     SmallVectorImpl<AnnotatedLine *> &Lines, tooling::Replacements &Result,
3404eeddc0SDimitry Andric     FormatTokenLexer &Tokens) {
3504eeddc0SDimitry Andric   const bool IsNeverStyle =
3604eeddc0SDimitry Andric       Style.SeparateDefinitionBlocks == FormatStyle::SDS_Never;
3704eeddc0SDimitry Andric   const AdditionalKeywords &ExtraKeywords = Tokens.getKeywords();
38d56accc7SDimitry Andric   auto GetBracketLevelChange = [](const FormatToken *Tok) {
39d56accc7SDimitry Andric     if (Tok->isOneOf(tok::l_brace, tok::l_paren, tok::l_square))
40d56accc7SDimitry Andric       return 1;
41d56accc7SDimitry Andric     if (Tok->isOneOf(tok::r_brace, tok::r_paren, tok::r_square))
42d56accc7SDimitry Andric       return -1;
43d56accc7SDimitry Andric     return 0;
44d56accc7SDimitry Andric   };
45d56accc7SDimitry Andric   auto LikelyDefinition = [&](const AnnotatedLine *Line,
4604eeddc0SDimitry Andric                               bool ExcludeEnum = false) {
4704eeddc0SDimitry Andric     if ((Line->MightBeFunctionDecl && Line->mightBeFunctionDefinition()) ||
4881ad6265SDimitry Andric         Line->startsWithNamespace()) {
4904eeddc0SDimitry Andric       return true;
5081ad6265SDimitry Andric     }
51d56accc7SDimitry Andric     int BracketLevel = 0;
52d56accc7SDimitry Andric     for (const FormatToken *CurrentToken = Line->First; CurrentToken;
53d56accc7SDimitry Andric          CurrentToken = CurrentToken->Next) {
54d56accc7SDimitry Andric       if (BracketLevel == 0) {
5506c3fb27SDimitry Andric         if (CurrentToken->isOneOf(tok::kw_class, tok::kw_struct,
56d56accc7SDimitry Andric                                   tok::kw_union) ||
57d56accc7SDimitry Andric             (Style.isJavaScript() &&
5806c3fb27SDimitry Andric              CurrentToken->is(ExtraKeywords.kw_function))) {
5904eeddc0SDimitry Andric           return true;
6081ad6265SDimitry Andric         }
6104eeddc0SDimitry Andric         if (!ExcludeEnum && CurrentToken->is(tok::kw_enum))
6204eeddc0SDimitry Andric           return true;
63d56accc7SDimitry Andric       }
64d56accc7SDimitry Andric       BracketLevel += GetBracketLevelChange(CurrentToken);
6504eeddc0SDimitry Andric     }
6604eeddc0SDimitry Andric     return false;
6704eeddc0SDimitry Andric   };
6804eeddc0SDimitry Andric   unsigned NewlineCount =
6904eeddc0SDimitry Andric       (Style.SeparateDefinitionBlocks == FormatStyle::SDS_Always ? 1 : 0) + 1;
7004eeddc0SDimitry Andric   WhitespaceManager Whitespaces(
7104eeddc0SDimitry Andric       Env.getSourceManager(), Style,
72bdd1243dSDimitry Andric       Style.LineEnding > FormatStyle::LE_CRLF
7304eeddc0SDimitry Andric           ? WhitespaceManager::inputUsesCRLF(
7404eeddc0SDimitry Andric                 Env.getSourceManager().getBufferData(Env.getFileID()),
75bdd1243dSDimitry Andric                 Style.LineEnding == FormatStyle::LE_DeriveCRLF)
76bdd1243dSDimitry Andric           : Style.LineEnding == FormatStyle::LE_CRLF);
7704eeddc0SDimitry Andric   for (unsigned I = 0; I < Lines.size(); ++I) {
7804eeddc0SDimitry Andric     const auto &CurrentLine = Lines[I];
7904eeddc0SDimitry Andric     if (CurrentLine->InPPDirective)
8004eeddc0SDimitry Andric       continue;
8104eeddc0SDimitry Andric     FormatToken *TargetToken = nullptr;
8204eeddc0SDimitry Andric     AnnotatedLine *TargetLine;
8304eeddc0SDimitry Andric     auto OpeningLineIndex = CurrentLine->MatchingOpeningBlockLineIndex;
8404eeddc0SDimitry Andric     AnnotatedLine *OpeningLine = nullptr;
8504eeddc0SDimitry Andric     const auto IsAccessSpecifierToken = [](const FormatToken *Token) {
8604eeddc0SDimitry Andric       return Token->isAccessSpecifier() || Token->isObjCAccessSpecifier();
8704eeddc0SDimitry Andric     };
8804eeddc0SDimitry Andric     const auto InsertReplacement = [&](const int NewlineToInsert) {
8904eeddc0SDimitry Andric       assert(TargetLine);
9004eeddc0SDimitry Andric       assert(TargetToken);
9104eeddc0SDimitry Andric 
9204eeddc0SDimitry Andric       // Do not handle EOF newlines.
9304eeddc0SDimitry Andric       if (TargetToken->is(tok::eof))
9404eeddc0SDimitry Andric         return;
9504eeddc0SDimitry Andric       if (IsAccessSpecifierToken(TargetToken) ||
9604eeddc0SDimitry Andric           (OpeningLineIndex > 0 &&
9781ad6265SDimitry Andric            IsAccessSpecifierToken(Lines[OpeningLineIndex - 1]->First))) {
9804eeddc0SDimitry Andric         return;
9981ad6265SDimitry Andric       }
10004eeddc0SDimitry Andric       if (!TargetLine->Affected)
10104eeddc0SDimitry Andric         return;
10204eeddc0SDimitry Andric       Whitespaces.replaceWhitespace(*TargetToken, NewlineToInsert,
10304eeddc0SDimitry Andric                                     TargetToken->OriginalColumn,
10404eeddc0SDimitry Andric                                     TargetToken->OriginalColumn);
10504eeddc0SDimitry Andric     };
10604eeddc0SDimitry Andric     const auto IsPPConditional = [&](const size_t LineIndex) {
10704eeddc0SDimitry Andric       const auto &Line = Lines[LineIndex];
10804eeddc0SDimitry Andric       return Line->First->is(tok::hash) && Line->First->Next &&
10904eeddc0SDimitry Andric              Line->First->Next->isOneOf(tok::pp_if, tok::pp_ifdef, tok::pp_else,
11004eeddc0SDimitry Andric                                         tok::pp_ifndef, tok::pp_elifndef,
11104eeddc0SDimitry Andric                                         tok::pp_elifdef, tok::pp_elif,
11204eeddc0SDimitry Andric                                         tok::pp_endif);
11304eeddc0SDimitry Andric     };
11404eeddc0SDimitry Andric     const auto FollowingOtherOpening = [&]() {
11504eeddc0SDimitry Andric       return OpeningLineIndex == 0 ||
11604eeddc0SDimitry Andric              Lines[OpeningLineIndex - 1]->Last->opensScope() ||
11704eeddc0SDimitry Andric              IsPPConditional(OpeningLineIndex - 1);
11804eeddc0SDimitry Andric     };
11904eeddc0SDimitry Andric     const auto HasEnumOnLine = [&]() {
12004eeddc0SDimitry Andric       bool FoundEnumKeyword = false;
121d56accc7SDimitry Andric       int BracketLevel = 0;
122d56accc7SDimitry Andric       for (const FormatToken *CurrentToken = CurrentLine->First; CurrentToken;
123d56accc7SDimitry Andric            CurrentToken = CurrentToken->Next) {
124d56accc7SDimitry Andric         if (BracketLevel == 0) {
12504eeddc0SDimitry Andric           if (CurrentToken->is(tok::kw_enum))
12604eeddc0SDimitry Andric             FoundEnumKeyword = true;
12704eeddc0SDimitry Andric           else if (FoundEnumKeyword && CurrentToken->is(tok::l_brace))
12804eeddc0SDimitry Andric             return true;
129d56accc7SDimitry Andric         }
130d56accc7SDimitry Andric         BracketLevel += GetBracketLevelChange(CurrentToken);
13104eeddc0SDimitry Andric       }
13204eeddc0SDimitry Andric       return FoundEnumKeyword && I + 1 < Lines.size() &&
13304eeddc0SDimitry Andric              Lines[I + 1]->First->is(tok::l_brace);
13404eeddc0SDimitry Andric     };
13504eeddc0SDimitry Andric 
13604eeddc0SDimitry Andric     bool IsDefBlock = false;
13704eeddc0SDimitry Andric     const auto MayPrecedeDefinition = [&](const int Direction = -1) {
13804eeddc0SDimitry Andric       assert(Direction >= -1);
13904eeddc0SDimitry Andric       assert(Direction <= 1);
14004eeddc0SDimitry Andric       const size_t OperateIndex = OpeningLineIndex + Direction;
14104eeddc0SDimitry Andric       assert(OperateIndex < Lines.size());
14204eeddc0SDimitry Andric       const auto &OperateLine = Lines[OperateIndex];
14304eeddc0SDimitry Andric       if (LikelyDefinition(OperateLine))
14404eeddc0SDimitry Andric         return false;
14504eeddc0SDimitry Andric 
146*5f757f3fSDimitry Andric       if (const auto *Tok = OperateLine->First;
147*5f757f3fSDimitry Andric           Tok->is(tok::comment) && !isClangFormatOn(Tok->TokenText)) {
14804eeddc0SDimitry Andric         return true;
149*5f757f3fSDimitry Andric       }
15004eeddc0SDimitry Andric 
15104eeddc0SDimitry Andric       // A single line identifier that is not in the last line.
15204eeddc0SDimitry Andric       if (OperateLine->First->is(tok::identifier) &&
15304eeddc0SDimitry Andric           OperateLine->First == OperateLine->Last &&
15404eeddc0SDimitry Andric           OperateIndex + 1 < Lines.size()) {
15504eeddc0SDimitry Andric         // UnwrappedLineParser's recognition of free-standing macro like
15604eeddc0SDimitry Andric         // Q_OBJECT may also recognize some uppercased type names that may be
15704eeddc0SDimitry Andric         // used as return type as that kind of macros, which is a bit hard to
15804eeddc0SDimitry Andric         // distinguish one from another purely from token patterns. Here, we
15904eeddc0SDimitry Andric         // try not to add new lines below those identifiers.
16004eeddc0SDimitry Andric         AnnotatedLine *NextLine = Lines[OperateIndex + 1];
16104eeddc0SDimitry Andric         if (NextLine->MightBeFunctionDecl &&
16204eeddc0SDimitry Andric             NextLine->mightBeFunctionDefinition() &&
16304eeddc0SDimitry Andric             NextLine->First->NewlinesBefore == 1 &&
16481ad6265SDimitry Andric             OperateLine->First->is(TT_FunctionLikeOrFreestandingMacro)) {
16504eeddc0SDimitry Andric           return true;
16604eeddc0SDimitry Andric         }
16781ad6265SDimitry Andric       }
16804eeddc0SDimitry Andric 
16906c3fb27SDimitry Andric       if (Style.isCSharp() && OperateLine->First->is(TT_AttributeSquare))
17004eeddc0SDimitry Andric         return true;
17104eeddc0SDimitry Andric       return false;
17204eeddc0SDimitry Andric     };
17304eeddc0SDimitry Andric 
17404eeddc0SDimitry Andric     if (HasEnumOnLine() &&
17504eeddc0SDimitry Andric         !LikelyDefinition(CurrentLine, /*ExcludeEnum=*/true)) {
17604eeddc0SDimitry Andric       // We have no scope opening/closing information for enum.
17704eeddc0SDimitry Andric       IsDefBlock = true;
17804eeddc0SDimitry Andric       OpeningLineIndex = I;
17904eeddc0SDimitry Andric       while (OpeningLineIndex > 0 && MayPrecedeDefinition())
18004eeddc0SDimitry Andric         --OpeningLineIndex;
18104eeddc0SDimitry Andric       OpeningLine = Lines[OpeningLineIndex];
18204eeddc0SDimitry Andric       TargetLine = OpeningLine;
18304eeddc0SDimitry Andric       TargetToken = TargetLine->First;
18404eeddc0SDimitry Andric       if (!FollowingOtherOpening())
18504eeddc0SDimitry Andric         InsertReplacement(NewlineCount);
18604eeddc0SDimitry Andric       else if (IsNeverStyle)
18704eeddc0SDimitry Andric         InsertReplacement(OpeningLineIndex != 0);
18804eeddc0SDimitry Andric       TargetLine = CurrentLine;
18904eeddc0SDimitry Andric       TargetToken = TargetLine->First;
190*5f757f3fSDimitry Andric       while (TargetToken && TargetToken->isNot(tok::r_brace))
19104eeddc0SDimitry Andric         TargetToken = TargetToken->Next;
19281ad6265SDimitry Andric       if (!TargetToken)
193*5f757f3fSDimitry Andric         while (I < Lines.size() && Lines[I]->First->isNot(tok::r_brace))
19404eeddc0SDimitry Andric           ++I;
19504eeddc0SDimitry Andric     } else if (CurrentLine->First->closesScope()) {
19604eeddc0SDimitry Andric       if (OpeningLineIndex > Lines.size())
19704eeddc0SDimitry Andric         continue;
19804eeddc0SDimitry Andric       // Handling the case that opening brace has its own line, with checking
19904eeddc0SDimitry Andric       // whether the last line already had an opening brace to guard against
20004eeddc0SDimitry Andric       // misrecognition.
20104eeddc0SDimitry Andric       if (OpeningLineIndex > 0 &&
20204eeddc0SDimitry Andric           Lines[OpeningLineIndex]->First->is(tok::l_brace) &&
20381ad6265SDimitry Andric           Lines[OpeningLineIndex - 1]->Last->isNot(tok::l_brace)) {
20404eeddc0SDimitry Andric         --OpeningLineIndex;
20581ad6265SDimitry Andric       }
20604eeddc0SDimitry Andric       OpeningLine = Lines[OpeningLineIndex];
20704eeddc0SDimitry Andric       // Closing a function definition.
20804eeddc0SDimitry Andric       if (LikelyDefinition(OpeningLine)) {
20904eeddc0SDimitry Andric         IsDefBlock = true;
21004eeddc0SDimitry Andric         while (OpeningLineIndex > 0 && MayPrecedeDefinition())
21104eeddc0SDimitry Andric           --OpeningLineIndex;
21204eeddc0SDimitry Andric         OpeningLine = Lines[OpeningLineIndex];
21304eeddc0SDimitry Andric         TargetLine = OpeningLine;
21404eeddc0SDimitry Andric         TargetToken = TargetLine->First;
21504eeddc0SDimitry Andric         if (!FollowingOtherOpening()) {
21604eeddc0SDimitry Andric           // Avoid duplicated replacement.
21704eeddc0SDimitry Andric           if (TargetToken->isNot(tok::l_brace))
21804eeddc0SDimitry Andric             InsertReplacement(NewlineCount);
21981ad6265SDimitry Andric         } else if (IsNeverStyle) {
22004eeddc0SDimitry Andric           InsertReplacement(OpeningLineIndex != 0);
22104eeddc0SDimitry Andric         }
22204eeddc0SDimitry Andric       }
22381ad6265SDimitry Andric     }
22404eeddc0SDimitry Andric 
22504eeddc0SDimitry Andric     // Not the last token.
22604eeddc0SDimitry Andric     if (IsDefBlock && I + 1 < Lines.size()) {
22704eeddc0SDimitry Andric       OpeningLineIndex = I + 1;
22804eeddc0SDimitry Andric       TargetLine = Lines[OpeningLineIndex];
22904eeddc0SDimitry Andric       TargetToken = TargetLine->First;
23004eeddc0SDimitry Andric 
23104eeddc0SDimitry Andric       // No empty line for continuously closing scopes. The token will be
23204eeddc0SDimitry Andric       // handled in another case if the line following is opening a
23304eeddc0SDimitry Andric       // definition.
23404eeddc0SDimitry Andric       if (!TargetToken->closesScope() && !IsPPConditional(OpeningLineIndex)) {
23504eeddc0SDimitry Andric         // Check whether current line may precede a definition line.
23604eeddc0SDimitry Andric         while (OpeningLineIndex + 1 < Lines.size() &&
23781ad6265SDimitry Andric                MayPrecedeDefinition(/*Direction=*/0)) {
23804eeddc0SDimitry Andric           ++OpeningLineIndex;
23981ad6265SDimitry Andric         }
24004eeddc0SDimitry Andric         TargetLine = Lines[OpeningLineIndex];
24104eeddc0SDimitry Andric         if (!LikelyDefinition(TargetLine)) {
24204eeddc0SDimitry Andric           OpeningLineIndex = I + 1;
24304eeddc0SDimitry Andric           TargetLine = Lines[I + 1];
24404eeddc0SDimitry Andric           TargetToken = TargetLine->First;
24504eeddc0SDimitry Andric           InsertReplacement(NewlineCount);
24604eeddc0SDimitry Andric         }
24781ad6265SDimitry Andric       } else if (IsNeverStyle) {
24804eeddc0SDimitry Andric         InsertReplacement(/*NewlineToInsert=*/1);
24904eeddc0SDimitry Andric       }
25004eeddc0SDimitry Andric     }
25181ad6265SDimitry Andric   }
25281ad6265SDimitry Andric   for (const auto &R : Whitespaces.generateReplacements()) {
25304eeddc0SDimitry Andric     // The add method returns an Error instance which simulates program exit
25404eeddc0SDimitry Andric     // code through overloading boolean operator, thus false here indicates
25504eeddc0SDimitry Andric     // success.
25604eeddc0SDimitry Andric     if (Result.add(R))
25704eeddc0SDimitry Andric       return;
25804eeddc0SDimitry Andric   }
25981ad6265SDimitry Andric }
26004eeddc0SDimitry Andric } // namespace format
26104eeddc0SDimitry Andric } // namespace clang
262