DefinitionBlockSeparator.cpp 10.0 KB

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