xref: /openbsd-src/gnu/llvm/clang/lib/Format/DefinitionBlockSeparator.cpp (revision 12c855180aad702bbcca06e0398d774beeafb155)
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