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.LineEnding > FormatStyle::LE_CRLF 73 ? WhitespaceManager::inputUsesCRLF( 74 Env.getSourceManager().getBufferData(Env.getFileID()), 75 Style.LineEnding == FormatStyle::LE_DeriveCRLF) 76 : Style.LineEnding == FormatStyle::LE_CRLF); 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 (const auto *Tok = OperateLine->First; 147 Tok->is(tok::comment) && !isClangFormatOn(Tok->TokenText)) { 148 return true; 149 } 150 151 // A single line identifier that is not in the last line. 152 if (OperateLine->First->is(tok::identifier) && 153 OperateLine->First == OperateLine->Last && 154 OperateIndex + 1 < Lines.size()) { 155 // UnwrappedLineParser's recognition of free-standing macro like 156 // Q_OBJECT may also recognize some uppercased type names that may be 157 // used as return type as that kind of macros, which is a bit hard to 158 // distinguish one from another purely from token patterns. Here, we 159 // try not to add new lines below those identifiers. 160 AnnotatedLine *NextLine = Lines[OperateIndex + 1]; 161 if (NextLine->MightBeFunctionDecl && 162 NextLine->mightBeFunctionDefinition() && 163 NextLine->First->NewlinesBefore == 1 && 164 OperateLine->First->is(TT_FunctionLikeOrFreestandingMacro)) { 165 return true; 166 } 167 } 168 169 if (Style.isCSharp() && OperateLine->First->is(TT_AttributeSquare)) 170 return true; 171 return false; 172 }; 173 174 if (HasEnumOnLine() && 175 !LikelyDefinition(CurrentLine, /*ExcludeEnum=*/true)) { 176 // We have no scope opening/closing information for enum. 177 IsDefBlock = true; 178 OpeningLineIndex = I; 179 while (OpeningLineIndex > 0 && MayPrecedeDefinition()) 180 --OpeningLineIndex; 181 OpeningLine = Lines[OpeningLineIndex]; 182 TargetLine = OpeningLine; 183 TargetToken = TargetLine->First; 184 if (!FollowingOtherOpening()) 185 InsertReplacement(NewlineCount); 186 else if (IsNeverStyle) 187 InsertReplacement(OpeningLineIndex != 0); 188 TargetLine = CurrentLine; 189 TargetToken = TargetLine->First; 190 while (TargetToken && TargetToken->isNot(tok::r_brace)) 191 TargetToken = TargetToken->Next; 192 if (!TargetToken) 193 while (I < Lines.size() && Lines[I]->First->isNot(tok::r_brace)) 194 ++I; 195 } else if (CurrentLine->First->closesScope()) { 196 if (OpeningLineIndex > Lines.size()) 197 continue; 198 // Handling the case that opening brace has its own line, with checking 199 // whether the last line already had an opening brace to guard against 200 // misrecognition. 201 if (OpeningLineIndex > 0 && 202 Lines[OpeningLineIndex]->First->is(tok::l_brace) && 203 Lines[OpeningLineIndex - 1]->Last->isNot(tok::l_brace)) { 204 --OpeningLineIndex; 205 } 206 OpeningLine = Lines[OpeningLineIndex]; 207 // Closing a function definition. 208 if (LikelyDefinition(OpeningLine)) { 209 IsDefBlock = true; 210 while (OpeningLineIndex > 0 && MayPrecedeDefinition()) 211 --OpeningLineIndex; 212 OpeningLine = Lines[OpeningLineIndex]; 213 TargetLine = OpeningLine; 214 TargetToken = TargetLine->First; 215 if (!FollowingOtherOpening()) { 216 // Avoid duplicated replacement. 217 if (TargetToken->isNot(tok::l_brace)) 218 InsertReplacement(NewlineCount); 219 } else if (IsNeverStyle) { 220 InsertReplacement(OpeningLineIndex != 0); 221 } 222 } 223 } 224 225 // Not the last token. 226 if (IsDefBlock && I + 1 < Lines.size()) { 227 OpeningLineIndex = I + 1; 228 TargetLine = Lines[OpeningLineIndex]; 229 TargetToken = TargetLine->First; 230 231 // No empty line for continuously closing scopes. The token will be 232 // handled in another case if the line following is opening a 233 // definition. 234 if (!TargetToken->closesScope() && !IsPPConditional(OpeningLineIndex)) { 235 // Check whether current line may precede a definition line. 236 while (OpeningLineIndex + 1 < Lines.size() && 237 MayPrecedeDefinition(/*Direction=*/0)) { 238 ++OpeningLineIndex; 239 } 240 TargetLine = Lines[OpeningLineIndex]; 241 if (!LikelyDefinition(TargetLine)) { 242 OpeningLineIndex = I + 1; 243 TargetLine = Lines[I + 1]; 244 TargetToken = TargetLine->First; 245 InsertReplacement(NewlineCount); 246 } 247 } else if (IsNeverStyle) { 248 InsertReplacement(/*NewlineToInsert=*/1); 249 } 250 } 251 } 252 for (const auto &R : Whitespaces.generateReplacements()) { 253 // The add method returns an Error instance which simulates program exit 254 // code through overloading boolean operator, thus false here indicates 255 // success. 256 if (Result.add(R)) 257 return; 258 } 259 } 260 } // namespace format 261 } // namespace clang 262