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