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 } 51 int BracketLevel = 0; 52 for (const FormatToken *CurrentToken = Line->First; CurrentToken; 53 CurrentToken = CurrentToken->Next) { 54 if (BracketLevel == 0) { 55 if ((CurrentToken->isOneOf(tok::kw_class, tok::kw_struct, 56 tok::kw_union) || 57 (Style.isJavaScript() && 58 CurrentToken->is(ExtraKeywords.kw_function)))) { 59 return true; 60 } 61 if (!ExcludeEnum && CurrentToken->is(tok::kw_enum)) 62 return true; 63 } 64 BracketLevel += GetBracketLevelChange(CurrentToken); 65 } 66 return false; 67 }; 68 unsigned NewlineCount = 69 (Style.SeparateDefinitionBlocks == FormatStyle::SDS_Always ? 1 : 0) + 1; 70 WhitespaceManager Whitespaces( 71 Env.getSourceManager(), Style, 72 Style.DeriveLineEnding 73 ? WhitespaceManager::inputUsesCRLF( 74 Env.getSourceManager().getBufferData(Env.getFileID()), 75 Style.UseCRLF) 76 : Style.UseCRLF); 77 for (unsigned I = 0; I < Lines.size(); ++I) { 78 const auto &CurrentLine = Lines[I]; 79 if (CurrentLine->InPPDirective) 80 continue; 81 FormatToken *TargetToken = nullptr; 82 AnnotatedLine *TargetLine; 83 auto OpeningLineIndex = CurrentLine->MatchingOpeningBlockLineIndex; 84 AnnotatedLine *OpeningLine = nullptr; 85 const auto IsAccessSpecifierToken = [](const FormatToken *Token) { 86 return Token->isAccessSpecifier() || Token->isObjCAccessSpecifier(); 87 }; 88 const auto InsertReplacement = [&](const int NewlineToInsert) { 89 assert(TargetLine); 90 assert(TargetToken); 91 92 // Do not handle EOF newlines. 93 if (TargetToken->is(tok::eof)) 94 return; 95 if (IsAccessSpecifierToken(TargetToken) || 96 (OpeningLineIndex > 0 && 97 IsAccessSpecifierToken(Lines[OpeningLineIndex - 1]->First))) { 98 return; 99 } 100 if (!TargetLine->Affected) 101 return; 102 Whitespaces.replaceWhitespace(*TargetToken, NewlineToInsert, 103 TargetToken->OriginalColumn, 104 TargetToken->OriginalColumn); 105 }; 106 const auto IsPPConditional = [&](const size_t LineIndex) { 107 const auto &Line = Lines[LineIndex]; 108 return Line->First->is(tok::hash) && Line->First->Next && 109 Line->First->Next->isOneOf(tok::pp_if, tok::pp_ifdef, tok::pp_else, 110 tok::pp_ifndef, tok::pp_elifndef, 111 tok::pp_elifdef, tok::pp_elif, 112 tok::pp_endif); 113 }; 114 const auto FollowingOtherOpening = [&]() { 115 return OpeningLineIndex == 0 || 116 Lines[OpeningLineIndex - 1]->Last->opensScope() || 117 IsPPConditional(OpeningLineIndex - 1); 118 }; 119 const auto HasEnumOnLine = [&]() { 120 bool FoundEnumKeyword = false; 121 int BracketLevel = 0; 122 for (const FormatToken *CurrentToken = CurrentLine->First; CurrentToken; 123 CurrentToken = CurrentToken->Next) { 124 if (BracketLevel == 0) { 125 if (CurrentToken->is(tok::kw_enum)) 126 FoundEnumKeyword = true; 127 else if (FoundEnumKeyword && CurrentToken->is(tok::l_brace)) 128 return true; 129 } 130 BracketLevel += GetBracketLevelChange(CurrentToken); 131 } 132 return FoundEnumKeyword && I + 1 < Lines.size() && 133 Lines[I + 1]->First->is(tok::l_brace); 134 }; 135 136 bool IsDefBlock = false; 137 const auto MayPrecedeDefinition = [&](const int Direction = -1) { 138 assert(Direction >= -1); 139 assert(Direction <= 1); 140 const size_t OperateIndex = OpeningLineIndex + Direction; 141 assert(OperateIndex < Lines.size()); 142 const auto &OperateLine = Lines[OperateIndex]; 143 if (LikelyDefinition(OperateLine)) 144 return false; 145 146 if (OperateLine->First->is(tok::comment)) 147 return true; 148 149 // A single line identifier that is not in the last line. 150 if (OperateLine->First->is(tok::identifier) && 151 OperateLine->First == OperateLine->Last && 152 OperateIndex + 1 < Lines.size()) { 153 // UnwrappedLineParser's recognition of free-standing macro like 154 // Q_OBJECT may also recognize some uppercased type names that may be 155 // used as return type as that kind of macros, which is a bit hard to 156 // distinguish one from another purely from token patterns. Here, we 157 // try not to add new lines below those identifiers. 158 AnnotatedLine *NextLine = Lines[OperateIndex + 1]; 159 if (NextLine->MightBeFunctionDecl && 160 NextLine->mightBeFunctionDefinition() && 161 NextLine->First->NewlinesBefore == 1 && 162 OperateLine->First->is(TT_FunctionLikeOrFreestandingMacro)) { 163 return true; 164 } 165 } 166 167 if ((Style.isCSharp() && OperateLine->First->is(TT_AttributeSquare))) 168 return true; 169 return false; 170 }; 171 172 if (HasEnumOnLine() && 173 !LikelyDefinition(CurrentLine, /*ExcludeEnum=*/true)) { 174 // We have no scope opening/closing information for enum. 175 IsDefBlock = true; 176 OpeningLineIndex = I; 177 while (OpeningLineIndex > 0 && MayPrecedeDefinition()) 178 --OpeningLineIndex; 179 OpeningLine = Lines[OpeningLineIndex]; 180 TargetLine = OpeningLine; 181 TargetToken = TargetLine->First; 182 if (!FollowingOtherOpening()) 183 InsertReplacement(NewlineCount); 184 else if (IsNeverStyle) 185 InsertReplacement(OpeningLineIndex != 0); 186 TargetLine = CurrentLine; 187 TargetToken = TargetLine->First; 188 while (TargetToken && !TargetToken->is(tok::r_brace)) 189 TargetToken = TargetToken->Next; 190 if (!TargetToken) 191 while (I < Lines.size() && !Lines[I]->First->is(tok::r_brace)) 192 ++I; 193 } else if (CurrentLine->First->closesScope()) { 194 if (OpeningLineIndex > Lines.size()) 195 continue; 196 // Handling the case that opening brace has its own line, with checking 197 // whether the last line already had an opening brace to guard against 198 // misrecognition. 199 if (OpeningLineIndex > 0 && 200 Lines[OpeningLineIndex]->First->is(tok::l_brace) && 201 Lines[OpeningLineIndex - 1]->Last->isNot(tok::l_brace)) { 202 --OpeningLineIndex; 203 } 204 OpeningLine = Lines[OpeningLineIndex]; 205 // Closing a function definition. 206 if (LikelyDefinition(OpeningLine)) { 207 IsDefBlock = true; 208 while (OpeningLineIndex > 0 && MayPrecedeDefinition()) 209 --OpeningLineIndex; 210 OpeningLine = Lines[OpeningLineIndex]; 211 TargetLine = OpeningLine; 212 TargetToken = TargetLine->First; 213 if (!FollowingOtherOpening()) { 214 // Avoid duplicated replacement. 215 if (TargetToken->isNot(tok::l_brace)) 216 InsertReplacement(NewlineCount); 217 } else if (IsNeverStyle) { 218 InsertReplacement(OpeningLineIndex != 0); 219 } 220 } 221 } 222 223 // Not the last token. 224 if (IsDefBlock && I + 1 < Lines.size()) { 225 OpeningLineIndex = I + 1; 226 TargetLine = Lines[OpeningLineIndex]; 227 TargetToken = TargetLine->First; 228 229 // No empty line for continuously closing scopes. The token will be 230 // handled in another case if the line following is opening a 231 // definition. 232 if (!TargetToken->closesScope() && !IsPPConditional(OpeningLineIndex)) { 233 // Check whether current line may precede a definition line. 234 while (OpeningLineIndex + 1 < Lines.size() && 235 MayPrecedeDefinition(/*Direction=*/0)) { 236 ++OpeningLineIndex; 237 } 238 TargetLine = Lines[OpeningLineIndex]; 239 if (!LikelyDefinition(TargetLine)) { 240 OpeningLineIndex = I + 1; 241 TargetLine = Lines[I + 1]; 242 TargetToken = TargetLine->First; 243 InsertReplacement(NewlineCount); 244 } 245 } else if (IsNeverStyle) { 246 InsertReplacement(/*NewlineToInsert=*/1); 247 } 248 } 249 } 250 for (const auto &R : Whitespaces.generateReplacements()) { 251 // The add method returns an Error instance which simulates program exit 252 // code through overloading boolean operator, thus false here indicates 253 // success. 254 if (Result.add(R)) 255 return; 256 } 257 } 258 } // namespace format 259 } // namespace clang 260