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 { 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 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) { 55d56accc7SDimitry Andric if ((CurrentToken->isOneOf(tok::kw_class, tok::kw_struct, 56d56accc7SDimitry Andric tok::kw_union) || 57d56accc7SDimitry Andric (Style.isJavaScript() && 5881ad6265SDimitry 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, 72*bdd1243dSDimitry Andric Style.LineEnding > FormatStyle::LE_CRLF 7304eeddc0SDimitry Andric ? WhitespaceManager::inputUsesCRLF( 7404eeddc0SDimitry Andric Env.getSourceManager().getBufferData(Env.getFileID()), 75*bdd1243dSDimitry Andric Style.LineEnding == FormatStyle::LE_DeriveCRLF) 76*bdd1243dSDimitry 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 14604eeddc0SDimitry Andric if (OperateLine->First->is(tok::comment)) 14704eeddc0SDimitry Andric return true; 14804eeddc0SDimitry Andric 14904eeddc0SDimitry Andric // A single line identifier that is not in the last line. 15004eeddc0SDimitry Andric if (OperateLine->First->is(tok::identifier) && 15104eeddc0SDimitry Andric OperateLine->First == OperateLine->Last && 15204eeddc0SDimitry Andric OperateIndex + 1 < Lines.size()) { 15304eeddc0SDimitry Andric // UnwrappedLineParser's recognition of free-standing macro like 15404eeddc0SDimitry Andric // Q_OBJECT may also recognize some uppercased type names that may be 15504eeddc0SDimitry Andric // used as return type as that kind of macros, which is a bit hard to 15604eeddc0SDimitry Andric // distinguish one from another purely from token patterns. Here, we 15704eeddc0SDimitry Andric // try not to add new lines below those identifiers. 15804eeddc0SDimitry Andric AnnotatedLine *NextLine = Lines[OperateIndex + 1]; 15904eeddc0SDimitry Andric if (NextLine->MightBeFunctionDecl && 16004eeddc0SDimitry Andric NextLine->mightBeFunctionDefinition() && 16104eeddc0SDimitry Andric NextLine->First->NewlinesBefore == 1 && 16281ad6265SDimitry Andric OperateLine->First->is(TT_FunctionLikeOrFreestandingMacro)) { 16304eeddc0SDimitry Andric return true; 16404eeddc0SDimitry Andric } 16581ad6265SDimitry Andric } 16604eeddc0SDimitry Andric 16704eeddc0SDimitry Andric if ((Style.isCSharp() && OperateLine->First->is(TT_AttributeSquare))) 16804eeddc0SDimitry Andric return true; 16904eeddc0SDimitry Andric return false; 17004eeddc0SDimitry Andric }; 17104eeddc0SDimitry Andric 17204eeddc0SDimitry Andric if (HasEnumOnLine() && 17304eeddc0SDimitry Andric !LikelyDefinition(CurrentLine, /*ExcludeEnum=*/true)) { 17404eeddc0SDimitry Andric // We have no scope opening/closing information for enum. 17504eeddc0SDimitry Andric IsDefBlock = true; 17604eeddc0SDimitry Andric OpeningLineIndex = I; 17704eeddc0SDimitry Andric while (OpeningLineIndex > 0 && MayPrecedeDefinition()) 17804eeddc0SDimitry Andric --OpeningLineIndex; 17904eeddc0SDimitry Andric OpeningLine = Lines[OpeningLineIndex]; 18004eeddc0SDimitry Andric TargetLine = OpeningLine; 18104eeddc0SDimitry Andric TargetToken = TargetLine->First; 18204eeddc0SDimitry Andric if (!FollowingOtherOpening()) 18304eeddc0SDimitry Andric InsertReplacement(NewlineCount); 18404eeddc0SDimitry Andric else if (IsNeverStyle) 18504eeddc0SDimitry Andric InsertReplacement(OpeningLineIndex != 0); 18604eeddc0SDimitry Andric TargetLine = CurrentLine; 18704eeddc0SDimitry Andric TargetToken = TargetLine->First; 18804eeddc0SDimitry Andric while (TargetToken && !TargetToken->is(tok::r_brace)) 18904eeddc0SDimitry Andric TargetToken = TargetToken->Next; 19081ad6265SDimitry Andric if (!TargetToken) 19104eeddc0SDimitry Andric while (I < Lines.size() && !Lines[I]->First->is(tok::r_brace)) 19204eeddc0SDimitry Andric ++I; 19304eeddc0SDimitry Andric } else if (CurrentLine->First->closesScope()) { 19404eeddc0SDimitry Andric if (OpeningLineIndex > Lines.size()) 19504eeddc0SDimitry Andric continue; 19604eeddc0SDimitry Andric // Handling the case that opening brace has its own line, with checking 19704eeddc0SDimitry Andric // whether the last line already had an opening brace to guard against 19804eeddc0SDimitry Andric // misrecognition. 19904eeddc0SDimitry Andric if (OpeningLineIndex > 0 && 20004eeddc0SDimitry Andric Lines[OpeningLineIndex]->First->is(tok::l_brace) && 20181ad6265SDimitry Andric Lines[OpeningLineIndex - 1]->Last->isNot(tok::l_brace)) { 20204eeddc0SDimitry Andric --OpeningLineIndex; 20381ad6265SDimitry Andric } 20404eeddc0SDimitry Andric OpeningLine = Lines[OpeningLineIndex]; 20504eeddc0SDimitry Andric // Closing a function definition. 20604eeddc0SDimitry Andric if (LikelyDefinition(OpeningLine)) { 20704eeddc0SDimitry Andric IsDefBlock = true; 20804eeddc0SDimitry Andric while (OpeningLineIndex > 0 && MayPrecedeDefinition()) 20904eeddc0SDimitry Andric --OpeningLineIndex; 21004eeddc0SDimitry Andric OpeningLine = Lines[OpeningLineIndex]; 21104eeddc0SDimitry Andric TargetLine = OpeningLine; 21204eeddc0SDimitry Andric TargetToken = TargetLine->First; 21304eeddc0SDimitry Andric if (!FollowingOtherOpening()) { 21404eeddc0SDimitry Andric // Avoid duplicated replacement. 21504eeddc0SDimitry Andric if (TargetToken->isNot(tok::l_brace)) 21604eeddc0SDimitry Andric InsertReplacement(NewlineCount); 21781ad6265SDimitry Andric } else if (IsNeverStyle) { 21804eeddc0SDimitry Andric InsertReplacement(OpeningLineIndex != 0); 21904eeddc0SDimitry Andric } 22004eeddc0SDimitry Andric } 22181ad6265SDimitry Andric } 22204eeddc0SDimitry Andric 22304eeddc0SDimitry Andric // Not the last token. 22404eeddc0SDimitry Andric if (IsDefBlock && I + 1 < Lines.size()) { 22504eeddc0SDimitry Andric OpeningLineIndex = I + 1; 22604eeddc0SDimitry Andric TargetLine = Lines[OpeningLineIndex]; 22704eeddc0SDimitry Andric TargetToken = TargetLine->First; 22804eeddc0SDimitry Andric 22904eeddc0SDimitry Andric // No empty line for continuously closing scopes. The token will be 23004eeddc0SDimitry Andric // handled in another case if the line following is opening a 23104eeddc0SDimitry Andric // definition. 23204eeddc0SDimitry Andric if (!TargetToken->closesScope() && !IsPPConditional(OpeningLineIndex)) { 23304eeddc0SDimitry Andric // Check whether current line may precede a definition line. 23404eeddc0SDimitry Andric while (OpeningLineIndex + 1 < Lines.size() && 23581ad6265SDimitry Andric MayPrecedeDefinition(/*Direction=*/0)) { 23604eeddc0SDimitry Andric ++OpeningLineIndex; 23781ad6265SDimitry Andric } 23804eeddc0SDimitry Andric TargetLine = Lines[OpeningLineIndex]; 23904eeddc0SDimitry Andric if (!LikelyDefinition(TargetLine)) { 24004eeddc0SDimitry Andric OpeningLineIndex = I + 1; 24104eeddc0SDimitry Andric TargetLine = Lines[I + 1]; 24204eeddc0SDimitry Andric TargetToken = TargetLine->First; 24304eeddc0SDimitry Andric InsertReplacement(NewlineCount); 24404eeddc0SDimitry Andric } 24581ad6265SDimitry Andric } else if (IsNeverStyle) { 24604eeddc0SDimitry Andric InsertReplacement(/*NewlineToInsert=*/1); 24704eeddc0SDimitry Andric } 24804eeddc0SDimitry Andric } 24981ad6265SDimitry Andric } 25081ad6265SDimitry Andric for (const auto &R : Whitespaces.generateReplacements()) { 25104eeddc0SDimitry Andric // The add method returns an Error instance which simulates program exit 25204eeddc0SDimitry Andric // code through overloading boolean operator, thus false here indicates 25304eeddc0SDimitry Andric // success. 25404eeddc0SDimitry Andric if (Result.add(R)) 25504eeddc0SDimitry Andric return; 25604eeddc0SDimitry Andric } 25781ad6265SDimitry Andric } 25804eeddc0SDimitry Andric } // namespace format 25904eeddc0SDimitry Andric } // namespace clang 260