17#define DEBUG_TYPE "definition-block-separator"
24 assert(
Style.SeparateDefinitionBlocks != FormatStyle::SDS_Leave);
27 separateBlocks(AnnotatedLines,
Result, Tokens);
31void DefinitionBlockSeparator::separateBlocks(
34 const bool IsNeverStyle =
35 Style.SeparateDefinitionBlocks == FormatStyle::SDS_Never;
38 if (
Tok->isOneOf(tok::l_brace, tok::l_paren, tok::l_square))
40 if (
Tok->isOneOf(tok::r_brace, tok::r_paren, tok::r_square))
44 auto LikelyDefinition = [&](
const AnnotatedLine *
Line,
45 bool ExcludeEnum =
false) {
46 if ((
Line->MightBeFunctionDecl &&
Line->mightBeFunctionDefinition()) ||
47 Line->startsWithNamespace()) {
52 CurrentToken = CurrentToken->Next) {
53 if (BracketLevel == 0) {
54 if (CurrentToken->isOneOf(tok::kw_class, tok::kw_struct,
56 (Style.isJavaScript() &&
60 if (!ExcludeEnum && CurrentToken->is(tok::kw_enum))
63 BracketLevel += GetBracketLevelChange(CurrentToken);
67 unsigned NewlineCount =
68 (
Style.SeparateDefinitionBlocks == FormatStyle::SDS_Always ? 1 : 0) + 1;
69 WhitespaceManager Whitespaces(
71 Style.LineEnding > FormatStyle::LE_CRLF
73 Env.getSourceManager().getBufferData(
Env.getFileID()),
74 Style.LineEnding == FormatStyle::LE_DeriveCRLF)
75 :
Style.LineEnding == FormatStyle::LE_CRLF);
76 for (
unsigned I = 0; I < Lines.size(); ++I) {
77 const auto &CurrentLine = Lines[I];
78 if (CurrentLine->InPPDirective)
81 AnnotatedLine *TargetLine;
82 auto OpeningLineIndex = CurrentLine->MatchingOpeningBlockLineIndex;
83 AnnotatedLine *OpeningLine =
nullptr;
84 const auto IsAccessSpecifierToken = [](
const FormatToken *Token) {
85 return Token->isAccessSpecifier() || Token->isObjCAccessSpecifier();
87 const auto InsertReplacement = [&](
const int NewlineToInsert) {
92 if (TargetToken->is(tok::comment) &&
97 if (TargetToken->is(tok::eof))
99 if (IsAccessSpecifierToken(TargetToken) ||
100 (OpeningLineIndex > 0 &&
101 IsAccessSpecifierToken(Lines[OpeningLineIndex - 1]->
First))) {
104 if (!TargetLine->Affected)
106 Whitespaces.replaceWhitespace(*TargetToken, NewlineToInsert,
107 TargetToken->OriginalColumn,
108 TargetToken->OriginalColumn);
110 const auto IsPPConditional = [&](
const size_t LineIndex) {
111 const auto &
Line = Lines[LineIndex];
112 return Line->First->is(tok::hash) &&
Line->First->Next &&
113 Line->First->Next->isOneOf(tok::pp_if, tok::pp_ifdef, tok::pp_else,
114 tok::pp_ifndef, tok::pp_elifndef,
115 tok::pp_elifdef, tok::pp_elif,
118 const auto FollowingOtherOpening = [&]() {
119 return OpeningLineIndex == 0 ||
120 Lines[OpeningLineIndex - 1]->Last->opensScope() ||
121 IsPPConditional(OpeningLineIndex - 1);
123 const auto HasEnumOnLine = [&]() {
124 bool FoundEnumKeyword =
false;
125 int BracketLevel = 0;
126 for (
const FormatToken *CurrentToken = CurrentLine->First; CurrentToken;
127 CurrentToken = CurrentToken->Next) {
128 if (BracketLevel == 0) {
129 if (CurrentToken->is(tok::kw_enum))
130 FoundEnumKeyword =
true;
131 else if (FoundEnumKeyword && CurrentToken->is(tok::l_brace))
134 BracketLevel += GetBracketLevelChange(CurrentToken);
136 return FoundEnumKeyword && I + 1 < Lines.size() &&
137 Lines[I + 1]->First->is(tok::l_brace);
140 bool IsDefBlock =
false;
141 const auto MayPrecedeDefinition = [&](
const int Direction = -1) {
142 assert(Direction >= -1);
143 assert(Direction <= 1);
145 if (Lines[OpeningLineIndex]->
First->is(TT_CSharpGenericTypeConstraint))
148 const size_t OperateIndex = OpeningLineIndex +
Direction;
149 assert(OperateIndex < Lines.size());
150 const auto &OperateLine = Lines[OperateIndex];
151 if (LikelyDefinition(OperateLine))
154 const auto *NextLine =
155 OperateIndex + 1 < Lines.size() ? Lines[OperateIndex + 1] :
nullptr;
157 if (
const auto *
Tok = OperateLine->First;
159 const bool IsEndComment =
Tok->NewlinesBefore == 1 && NextLine &&
160 NextLine->First->NewlinesBefore > 1;
166 if (OperateLine->First->is(tok::identifier) &&
167 OperateLine->First == OperateLine->Last && NextLine) {
173 if (NextLine->MightBeFunctionDecl &&
174 NextLine->mightBeFunctionDefinition() &&
175 NextLine->First->NewlinesBefore == 1 &&
176 OperateLine->First->is(TT_FunctionLikeOrFreestandingMacro)) {
181 if (
Style.isCSharp() && OperateLine->First->is(TT_AttributeLSquare))
186 if (HasEnumOnLine() &&
187 !LikelyDefinition(CurrentLine,
true)) {
190 OpeningLineIndex = I;
191 while (OpeningLineIndex > 0 && MayPrecedeDefinition())
193 OpeningLine = Lines[OpeningLineIndex];
194 TargetLine = OpeningLine;
195 TargetToken = TargetLine->First;
196 if (!FollowingOtherOpening())
197 InsertReplacement(NewlineCount);
198 else if (IsNeverStyle)
199 InsertReplacement(OpeningLineIndex != 0);
200 TargetLine = CurrentLine;
201 TargetToken = TargetLine->First;
202 while (TargetToken && TargetToken->isNot(tok::r_brace))
203 TargetToken = TargetToken->Next;
205 while (I < Lines.size() && Lines[I]->First->isNot(tok::r_brace))
207 }
else if (CurrentLine->First->closesScope()) {
208 if (OpeningLineIndex > Lines.size())
211 if (CurrentLine->First->startsSequence(tok::r_brace, tok::kw_catch))
216 if (OpeningLineIndex > 0 &&
217 Lines[OpeningLineIndex]->
First->is(tok::l_brace) &&
218 Lines[OpeningLineIndex - 1]->Last->isNot(tok::l_brace)) {
221 OpeningLine = Lines[OpeningLineIndex];
223 if (LikelyDefinition(OpeningLine)) {
225 while (OpeningLineIndex > 0 && MayPrecedeDefinition())
227 OpeningLine = Lines[OpeningLineIndex];
228 TargetLine = OpeningLine;
229 TargetToken = TargetLine->First;
230 if (!FollowingOtherOpening()) {
232 if (TargetToken->isNot(tok::l_brace))
233 InsertReplacement(NewlineCount);
234 }
else if (IsNeverStyle) {
235 InsertReplacement(OpeningLineIndex != 0);
241 if (IsDefBlock && I + 1 < Lines.size()) {
242 OpeningLineIndex = I + 1;
243 TargetLine = Lines[OpeningLineIndex];
244 TargetToken = TargetLine->First;
249 if (!TargetToken->closesScope() && !IsPPConditional(OpeningLineIndex)) {
251 while (OpeningLineIndex + 1 < Lines.size() &&
252 MayPrecedeDefinition(0)) {
255 TargetLine = Lines[OpeningLineIndex];
256 if (!LikelyDefinition(TargetLine)) {
257 OpeningLineIndex = I + 1;
258 TargetLine = Lines[I + 1];
259 TargetToken = TargetLine->First;
260 InsertReplacement(NewlineCount);
262 }
else if (IsNeverStyle) {
263 InsertReplacement(1);
267 for (
const auto &R : Whitespaces.generateReplacements()) {
This file declares DefinitionBlockSeparator, a TokenAnalyzer that inserts or removes empty lines sepa...
The JSON file list parser is used to communicate input to InstallAPI.
@ Result
The result type of a method or function.
Encapsulates keywords that are context sensitive or for languages not properly supported by Clang's l...
IdentifierInfo * kw_function