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