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); 29 return {Result, 0}; 30 } 31 32 void DefinitionBlockSeparator::separateBlocks( 33 SmallVectorImpl<AnnotatedLine *> &Lines, tooling::Replacements &Result) { 34 auto LikelyDefinition = [this](const AnnotatedLine *Line) { 35 if (Line->MightBeFunctionDecl && Line->mightBeFunctionDefinition()) 36 return true; 37 FormatToken *CurrentToken = Line->First; 38 while (CurrentToken) { 39 if (CurrentToken->isOneOf(tok::kw_class, tok::kw_struct, 40 tok::kw_namespace, tok::kw_enum) || 41 (Style.Language == FormatStyle::LK_JavaScript && 42 CurrentToken->TokenText == "function")) 43 return true; 44 CurrentToken = CurrentToken->Next; 45 } 46 return false; 47 }; 48 unsigned NewlineCount = 49 (Style.SeparateDefinitionBlocks == FormatStyle::SDS_Always ? 1 : 0) + 1; 50 WhitespaceManager Whitespaces( 51 Env.getSourceManager(), Style, 52 Style.DeriveLineEnding 53 ? WhitespaceManager::inputUsesCRLF( 54 Env.getSourceManager().getBufferData(Env.getFileID()), 55 Style.UseCRLF) 56 : Style.UseCRLF); 57 for (unsigned I = 0; I < Lines.size(); I++) { 58 const auto &CurrentLine = Lines[I]; 59 FormatToken *TargetToken = nullptr; 60 AnnotatedLine *TargetLine; 61 auto OpeningLineIndex = CurrentLine->MatchingOpeningBlockLineIndex; 62 const auto InsertReplacement = [&](const int NewlineToInsert) { 63 assert(TargetLine); 64 assert(TargetToken); 65 66 // Do not handle EOF newlines. 67 if (TargetToken->is(tok::eof) && NewlineToInsert > 0) 68 return; 69 if (!TargetLine->Affected) 70 return; 71 Whitespaces.replaceWhitespace(*TargetToken, NewlineToInsert, 72 TargetToken->SpacesRequiredBefore - 1, 73 TargetToken->StartsColumn); 74 }; 75 const auto FollowingOtherOpening = [&]() { 76 return OpeningLineIndex == 0 || 77 Lines[OpeningLineIndex - 1]->Last->opensScope(); 78 }; 79 const auto HasEnumOnLine = [CurrentLine]() { 80 FormatToken *CurrentToken = CurrentLine->First; 81 while (CurrentToken) { 82 if (CurrentToken->is(tok::kw_enum)) 83 return true; 84 CurrentToken = CurrentToken->Next; 85 } 86 return false; 87 }; 88 89 bool IsDefBlock = 0; 90 91 if (HasEnumOnLine()) { 92 // We have no scope opening/closing information for enum. 93 IsDefBlock = 1; 94 OpeningLineIndex = I; 95 TargetLine = CurrentLine; 96 TargetToken = CurrentLine->First; 97 if (!FollowingOtherOpening()) 98 InsertReplacement(NewlineCount); 99 else 100 InsertReplacement(OpeningLineIndex != 0); 101 while (TargetToken && !TargetToken->is(tok::r_brace)) 102 TargetToken = TargetToken->Next; 103 if (!TargetToken) { 104 while (I < Lines.size() && !Lines[I]->First->is(tok::r_brace)) 105 I++; 106 } 107 } else if (CurrentLine->First->closesScope()) { 108 if (OpeningLineIndex > Lines.size()) 109 continue; 110 // Handling the case that opening bracket has its own line. 111 OpeningLineIndex -= Lines[OpeningLineIndex]->First->TokenText == "{"; 112 AnnotatedLine *OpeningLine = Lines[OpeningLineIndex]; 113 // Closing a function definition. 114 if (LikelyDefinition(OpeningLine)) { 115 IsDefBlock = 1; 116 if (OpeningLineIndex > 0) { 117 OpeningLineIndex -= 118 Style.Language == FormatStyle::LK_CSharp && 119 Lines[OpeningLineIndex - 1]->First->is(tok::l_square); 120 OpeningLine = Lines[OpeningLineIndex]; 121 } 122 TargetLine = OpeningLine; 123 TargetToken = TargetLine->First; 124 if (!FollowingOtherOpening()) { 125 // Avoid duplicated replacement. 126 if (!TargetToken->opensScope()) 127 InsertReplacement(NewlineCount); 128 } else 129 InsertReplacement(OpeningLineIndex != 0); 130 } 131 } 132 133 // Not the last token. 134 if (IsDefBlock && I + 1 < Lines.size()) { 135 TargetLine = Lines[I + 1]; 136 TargetToken = TargetLine->First; 137 138 // No empty line for continuously closing scopes. The token will be 139 // handled in another case if the line following is opening a 140 // definition. 141 if (!TargetToken->closesScope()) { 142 if (!LikelyDefinition(TargetLine)) 143 InsertReplacement(NewlineCount); 144 } else { 145 InsertReplacement(OpeningLineIndex != 0); 146 } 147 } 148 } 149 for (const auto &R : Whitespaces.generateReplacements()) 150 // The add method returns an Error instance which simulates program exit 151 // code through overloading boolean operator, thus false here indicates 152 // success. 153 if (Result.add(R)) 154 return; 155 } 156 } // namespace format 157 } // namespace clang 158