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