1 //===--- DefinitionBlockSeparator.cpp ---------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 /// 9 /// \file 10 /// This file implements DefinitionBlockSeparator, a TokenAnalyzer that inserts 11 /// or removes empty lines separating definition blocks like classes, structs, 12 /// functions, enums, and namespaces in between. 13 /// 14 //===----------------------------------------------------------------------===// 15 16 #include "DefinitionBlockSeparator.h" 17 #include "llvm/Support/Debug.h" 18 #define DEBUG_TYPE "definition-block-separator" 19 20 namespace clang { 21 namespace format { 22 std::pair<tooling::Replacements, unsigned> DefinitionBlockSeparator::analyze( 23 TokenAnnotator &Annotator, SmallVectorImpl<AnnotatedLine *> &AnnotatedLines, 24 FormatTokenLexer &Tokens) { 25 assert(Style.SeparateDefinitionBlocks != FormatStyle::SDS_Leave); 26 AffectedRangeMgr.computeAffectedLines(AnnotatedLines); 27 tooling::Replacements Result; 28 separateBlocks(AnnotatedLines, Result, Tokens); 29 return {Result, 0}; 30 } 31 32 void DefinitionBlockSeparator::separateBlocks( 33 SmallVectorImpl<AnnotatedLine *> &Lines, tooling::Replacements &Result, 34 FormatTokenLexer &Tokens) { 35 const bool IsNeverStyle = 36 Style.SeparateDefinitionBlocks == FormatStyle::SDS_Never; 37 const AdditionalKeywords &ExtraKeywords = Tokens.getKeywords(); 38 auto GetBracketLevelChange = [](const FormatToken *Tok) { 39 if (Tok->isOneOf(tok::l_brace, tok::l_paren, tok::l_square)) 40 return 1; 41 if (Tok->isOneOf(tok::r_brace, tok::r_paren, tok::r_square)) 42 return -1; 43 return 0; 44 }; 45 auto LikelyDefinition = [&](const AnnotatedLine *Line, 46 bool ExcludeEnum = false) { 47 if ((Line->MightBeFunctionDecl && Line->mightBeFunctionDefinition()) || 48 Line->startsWithNamespace()) 49 return true; 50 int BracketLevel = 0; 51 for (const FormatToken *CurrentToken = Line->First; CurrentToken; 52 CurrentToken = CurrentToken->Next) { 53 if (BracketLevel == 0) { 54 if ((CurrentToken->isOneOf(tok::kw_class, tok::kw_struct, 55 tok::kw_union) || 56 (Style.isJavaScript() && 57 CurrentToken->is(ExtraKeywords.kw_function)))) 58 return true; 59 if (!ExcludeEnum && CurrentToken->is(tok::kw_enum)) 60 return true; 61 } 62 BracketLevel += GetBracketLevelChange(CurrentToken); 63 } 64 return false; 65 }; 66 unsigned NewlineCount = 67 (Style.SeparateDefinitionBlocks == FormatStyle::SDS_Always ? 1 : 0) + 1; 68 WhitespaceManager Whitespaces( 69 Env.getSourceManager(), Style, 70 Style.DeriveLineEnding 71 ? WhitespaceManager::inputUsesCRLF( 72 Env.getSourceManager().getBufferData(Env.getFileID()), 73 Style.UseCRLF) 74 : Style.UseCRLF); 75 for (unsigned I = 0; I < Lines.size(); ++I) { 76 const auto &CurrentLine = Lines[I]; 77 if (CurrentLine->InPPDirective) 78 continue; 79 FormatToken *TargetToken = nullptr; 80 AnnotatedLine *TargetLine; 81 auto OpeningLineIndex = CurrentLine->MatchingOpeningBlockLineIndex; 82 AnnotatedLine *OpeningLine = nullptr; 83 const auto IsAccessSpecifierToken = [](const FormatToken *Token) { 84 return Token->isAccessSpecifier() || Token->isObjCAccessSpecifier(); 85 }; 86 const auto InsertReplacement = [&](const int NewlineToInsert) { 87 assert(TargetLine); 88 assert(TargetToken); 89 90 // Do not handle EOF newlines. 91 if (TargetToken->is(tok::eof)) 92 return; 93 if (IsAccessSpecifierToken(TargetToken) || 94 (OpeningLineIndex > 0 && 95 IsAccessSpecifierToken(Lines[OpeningLineIndex - 1]->First))) 96 return; 97 if (!TargetLine->Affected) 98 return; 99 Whitespaces.replaceWhitespace(*TargetToken, NewlineToInsert, 100 TargetToken->OriginalColumn, 101 TargetToken->OriginalColumn); 102 }; 103 const auto IsPPConditional = [&](const size_t LineIndex) { 104 const auto &Line = Lines[LineIndex]; 105 return Line->First->is(tok::hash) && Line->First->Next && 106 Line->First->Next->isOneOf(tok::pp_if, tok::pp_ifdef, tok::pp_else, 107 tok::pp_ifndef, tok::pp_elifndef, 108 tok::pp_elifdef, tok::pp_elif, 109 tok::pp_endif); 110 }; 111 const auto FollowingOtherOpening = [&]() { 112 return OpeningLineIndex == 0 || 113 Lines[OpeningLineIndex - 1]->Last->opensScope() || 114 IsPPConditional(OpeningLineIndex - 1); 115 }; 116 const auto HasEnumOnLine = [&]() { 117 bool FoundEnumKeyword = false; 118 int BracketLevel = 0; 119 for (const FormatToken *CurrentToken = CurrentLine->First; CurrentToken; 120 CurrentToken = CurrentToken->Next) { 121 if (BracketLevel == 0) { 122 if (CurrentToken->is(tok::kw_enum)) 123 FoundEnumKeyword = true; 124 else if (FoundEnumKeyword && CurrentToken->is(tok::l_brace)) 125 return true; 126 } 127 BracketLevel += GetBracketLevelChange(CurrentToken); 128 } 129 return FoundEnumKeyword && I + 1 < Lines.size() && 130 Lines[I + 1]->First->is(tok::l_brace); 131 }; 132 133 bool IsDefBlock = false; 134 const auto MayPrecedeDefinition = [&](const int Direction = -1) { 135 assert(Direction >= -1); 136 assert(Direction <= 1); 137 const size_t OperateIndex = OpeningLineIndex + Direction; 138 assert(OperateIndex < Lines.size()); 139 const auto &OperateLine = Lines[OperateIndex]; 140 if (LikelyDefinition(OperateLine)) 141 return false; 142 143 if (OperateLine->First->is(tok::comment)) 144 return true; 145 146 // A single line identifier that is not in the last line. 147 if (OperateLine->First->is(tok::identifier) && 148 OperateLine->First == OperateLine->Last && 149 OperateIndex + 1 < Lines.size()) { 150 // UnwrappedLineParser's recognition of free-standing macro like 151 // Q_OBJECT may also recognize some uppercased type names that may be 152 // used as return type as that kind of macros, which is a bit hard to 153 // distinguish one from another purely from token patterns. Here, we 154 // try not to add new lines below those identifiers. 155 AnnotatedLine *NextLine = Lines[OperateIndex + 1]; 156 if (NextLine->MightBeFunctionDecl && 157 NextLine->mightBeFunctionDefinition() && 158 NextLine->First->NewlinesBefore == 1 && 159 OperateLine->First->is(TT_FunctionLikeOrFreestandingMacro)) 160 return true; 161 } 162 163 if ((Style.isCSharp() && OperateLine->First->is(TT_AttributeSquare))) 164 return true; 165 return false; 166 }; 167 168 if (HasEnumOnLine() && 169 !LikelyDefinition(CurrentLine, /*ExcludeEnum=*/true)) { 170 // We have no scope opening/closing information for enum. 171 IsDefBlock = true; 172 OpeningLineIndex = I; 173 while (OpeningLineIndex > 0 && MayPrecedeDefinition()) 174 --OpeningLineIndex; 175 OpeningLine = Lines[OpeningLineIndex]; 176 TargetLine = OpeningLine; 177 TargetToken = TargetLine->First; 178 if (!FollowingOtherOpening()) 179 InsertReplacement(NewlineCount); 180 else if (IsNeverStyle) 181 InsertReplacement(OpeningLineIndex != 0); 182 TargetLine = CurrentLine; 183 TargetToken = TargetLine->First; 184 while (TargetToken && !TargetToken->is(tok::r_brace)) 185 TargetToken = TargetToken->Next; 186 if (!TargetToken) { 187 while (I < Lines.size() && !Lines[I]->First->is(tok::r_brace)) 188 ++I; 189 } 190 } else if (CurrentLine->First->closesScope()) { 191 if (OpeningLineIndex > Lines.size()) 192 continue; 193 // Handling the case that opening brace has its own line, with checking 194 // whether the last line already had an opening brace to guard against 195 // misrecognition. 196 if (OpeningLineIndex > 0 && 197 Lines[OpeningLineIndex]->First->is(tok::l_brace) && 198 Lines[OpeningLineIndex - 1]->Last->isNot(tok::l_brace)) 199 --OpeningLineIndex; 200 OpeningLine = Lines[OpeningLineIndex]; 201 // Closing a function definition. 202 if (LikelyDefinition(OpeningLine)) { 203 IsDefBlock = true; 204 while (OpeningLineIndex > 0 && MayPrecedeDefinition()) 205 --OpeningLineIndex; 206 OpeningLine = Lines[OpeningLineIndex]; 207 TargetLine = OpeningLine; 208 TargetToken = TargetLine->First; 209 if (!FollowingOtherOpening()) { 210 // Avoid duplicated replacement. 211 if (TargetToken->isNot(tok::l_brace)) 212 InsertReplacement(NewlineCount); 213 } else if (IsNeverStyle) 214 InsertReplacement(OpeningLineIndex != 0); 215 } 216 } 217 218 // Not the last token. 219 if (IsDefBlock && I + 1 < Lines.size()) { 220 OpeningLineIndex = I + 1; 221 TargetLine = Lines[OpeningLineIndex]; 222 TargetToken = TargetLine->First; 223 224 // No empty line for continuously closing scopes. The token will be 225 // handled in another case if the line following is opening a 226 // definition. 227 if (!TargetToken->closesScope() && !IsPPConditional(OpeningLineIndex)) { 228 // Check whether current line may precede a definition line. 229 while (OpeningLineIndex + 1 < Lines.size() && 230 MayPrecedeDefinition(/*Direction=*/0)) 231 ++OpeningLineIndex; 232 TargetLine = Lines[OpeningLineIndex]; 233 if (!LikelyDefinition(TargetLine)) { 234 OpeningLineIndex = I + 1; 235 TargetLine = Lines[I + 1]; 236 TargetToken = TargetLine->First; 237 InsertReplacement(NewlineCount); 238 } 239 } else if (IsNeverStyle) 240 InsertReplacement(/*NewlineToInsert=*/1); 241 } 242 } 243 for (const auto &R : Whitespaces.generateReplacements()) 244 // The add method returns an Error instance which simulates program exit 245 // code through overloading boolean operator, thus false here indicates 246 // success. 247 if (Result.add(R)) 248 return; 249 } 250 } // namespace format 251 } // namespace clang 252