Coverage Report

Created: 2022-01-18 06:27

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Format/DefinitionBlockSeparator.cpp
Line
Count
Source (jump to first uncovered line)
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
16
#include "DefinitionBlockSeparator.h"
17
#include "llvm/Support/Debug.h"
18
#define DEBUG_TYPE "definition-block-separator"
19
20
namespace clang {
21
namespace format {
22
std::pair<tooling::Replacements, unsigned> DefinitionBlockSeparator::analyze(
23
    TokenAnnotator &Annotator, SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
24
67
    FormatTokenLexer &Tokens) {
25
67
  assert(Style.SeparateDefinitionBlocks != FormatStyle::SDS_Leave);
26
0
  AffectedRangeMgr.computeAffectedLines(AnnotatedLines);
27
67
  tooling::Replacements Result;
28
67
  separateBlocks(AnnotatedLines, Result);
29
67
  return {Result, 0};
30
67
}
31
32
void DefinitionBlockSeparator::separateBlocks(
33
67
    SmallVectorImpl<AnnotatedLine *> &Lines, tooling::Replacements &Result) {
34
67
  const bool IsNeverStyle =
35
67
      Style.SeparateDefinitionBlocks == FormatStyle::SDS_Never;
36
458
  auto LikelyDefinition = [this](const AnnotatedLine *Line) {
37
458
    if ((Line->MightBeFunctionDecl && 
Line->mightBeFunctionDefinition()241
) ||
38
458
        
Line->startsWithNamespace()217
)
39
305
      return true;
40
153
    FormatToken *CurrentToken = Line->First;
41
365
    while (CurrentToken) {
42
285
      if (CurrentToken->isOneOf(tok::kw_class, tok::kw_struct, tok::kw_enum) ||
43
285
          
(230
Style.isJavaScript()230
&&
CurrentToken->TokenText == "function"78
))
44
73
        return true;
45
212
      CurrentToken = CurrentToken->Next;
46
212
    }
47
80
    return false;
48
153
  };
49
67
  unsigned NewlineCount =
50
67
      (Style.SeparateDefinitionBlocks == FormatStyle::SDS_Always ? 
138
:
029
) + 1;
51
67
  WhitespaceManager Whitespaces(
52
67
      Env.getSourceManager(), Style,
53
67
      Style.DeriveLineEnding
54
67
          ? WhitespaceManager::inputUsesCRLF(
55
67
                Env.getSourceManager().getBufferData(Env.getFileID()),
56
67
                Style.UseCRLF)
57
67
          : 
Style.UseCRLF0
);
58
1.18k
  for (unsigned I = 0; I < Lines.size(); 
++I1.12k
) {
59
1.12k
    const auto &CurrentLine = Lines[I];
60
1.12k
    if (CurrentLine->InPPDirective)
61
72
      continue;
62
1.04k
    FormatToken *TargetToken = nullptr;
63
1.04k
    AnnotatedLine *TargetLine;
64
1.04k
    auto OpeningLineIndex = CurrentLine->MatchingOpeningBlockLineIndex;
65
1.04k
    AnnotatedLine *OpeningLine = nullptr;
66
1.04k
    const auto InsertReplacement = [&](const int NewlineToInsert) {
67
340
      assert(TargetLine);
68
0
      assert(TargetToken);
69
70
      // Do not handle EOF newlines.
71
340
      if (TargetToken->is(tok::eof) && 
NewlineToInsert > 067
)
72
67
        return;
73
273
      if (!TargetLine->Affected)
74
0
        return;
75
273
      Whitespaces.replaceWhitespace(*TargetToken, NewlineToInsert,
76
273
                                    TargetToken->SpacesRequiredBefore - 1,
77
273
                                    TargetToken->StartsColumn);
78
273
    };
79
1.04k
    const auto IsPPConditional = [&](const size_t LineIndex) {
80
485
      const auto &Line = Lines[LineIndex];
81
485
      return Line->First->is(tok::hash) && 
Line->First->Next72
&&
82
485
             Line->First->Next->isOneOf(tok::pp_if, tok::pp_ifdef, tok::pp_else,
83
72
                                        tok::pp_ifndef, tok::pp_elifndef,
84
72
                                        tok::pp_elifdef, tok::pp_elif,
85
72
                                        tok::pp_endif);
86
485
    };
87
1.04k
    const auto FollowingOtherOpening = [&]() {
88
301
      return OpeningLineIndex == 0 ||
89
301
             
Lines[OpeningLineIndex - 1]->Last->opensScope()258
||
90
301
             
IsPPConditional(OpeningLineIndex - 1)221
;
91
301
    };
92
1.04k
    const auto HasEnumOnLine = [CurrentLine]() {
93
1.04k
      FormatToken *CurrentToken = CurrentLine->First;
94
4.76k
      while (CurrentToken) {
95
3.79k
        if (CurrentToken->is(tok::kw_enum))
96
83
          return true;
97
3.71k
        CurrentToken = CurrentToken->Next;
98
3.71k
      }
99
965
      return false;
100
1.04k
    };
101
102
1.04k
    bool IsDefBlock = false;
103
1.04k
    const auto MayPrecedeDefinition = [&](const int Direction = -1) {
104
585
      const size_t OperateIndex = OpeningLineIndex + Direction;
105
585
      assert(OperateIndex < Lines.size());
106
0
      const auto &OperateLine = Lines[OperateIndex];
107
585
      return (Style.isCSharp() && 
OperateLine->First->is(TT_AttributeSquare)57
) ||
108
585
             
OperateLine->First->is(tok::comment)579
;
109
585
    };
110
111
1.04k
    if (HasEnumOnLine()) {
112
      // We have no scope opening/closing information for enum.
113
83
      IsDefBlock = true;
114
83
      OpeningLineIndex = I;
115
127
      while (OpeningLineIndex > 0 && 
MayPrecedeDefinition()121
)
116
44
        --OpeningLineIndex;
117
83
      OpeningLine = Lines[OpeningLineIndex];
118
83
      TargetLine = OpeningLine;
119
83
      TargetToken = TargetLine->First;
120
83
      if (!FollowingOtherOpening())
121
19
        InsertReplacement(NewlineCount);
122
64
      else if (IsNeverStyle)
123
30
        InsertReplacement(OpeningLineIndex != 0);
124
83
      TargetLine = CurrentLine;
125
83
      TargetToken = TargetLine->First;
126
569
      while (TargetToken && 
!TargetToken->is(tok::r_brace)560
)
127
486
        TargetToken = TargetToken->Next;
128
83
      if (!TargetToken) {
129
42
        while (I < Lines.size() && !Lines[I]->First->is(tok::r_brace))
130
33
          ++I;
131
9
      }
132
965
    } else if (CurrentLine->First->closesScope()) {
133
218
      if (OpeningLineIndex > Lines.size())
134
0
        continue;
135
      // Handling the case that opening bracket has its own line.
136
218
      OpeningLineIndex -= Lines[OpeningLineIndex]->First->is(tok::l_brace);
137
218
      OpeningLine = Lines[OpeningLineIndex];
138
      // Closing a function definition.
139
218
      if (LikelyDefinition(OpeningLine)) {
140
218
        IsDefBlock = true;
141
268
        while (OpeningLineIndex > 0 && 
MayPrecedeDefinition()231
)
142
50
          --OpeningLineIndex;
143
218
        OpeningLine = Lines[OpeningLineIndex];
144
218
        TargetLine = OpeningLine;
145
218
        TargetToken = TargetLine->First;
146
218
        if (!FollowingOtherOpening()) {
147
          // Avoid duplicated replacement.
148
154
          if (TargetToken->isNot(tok::l_brace))
149
154
            InsertReplacement(NewlineCount);
150
154
        } else 
if (64
IsNeverStyle64
)
151
28
          InsertReplacement(OpeningLineIndex != 0);
152
218
      }
153
218
    }
154
155
    // Not the last token.
156
1.04k
    if (IsDefBlock && 
I + 1 < Lines.size()301
) {
157
301
      OpeningLineIndex = I + 1;
158
301
      TargetLine = Lines[OpeningLineIndex];
159
301
      TargetToken = TargetLine->First;
160
161
      // No empty line for continuously closing scopes. The token will be
162
      // handled in another case if the line following is opening a
163
      // definition.
164
301
      if (!TargetToken->closesScope() && 
!IsPPConditional(OpeningLineIndex)264
) {
165
        // Check whether current line may precede a definition line.
166
300
        while (OpeningLineIndex + 1 < Lines.size() &&
167
300
               
MayPrecedeDefinition(/*Direction=*/0)233
)
168
60
          ++OpeningLineIndex;
169
240
        TargetLine = Lines[OpeningLineIndex];
170
240
        if (!LikelyDefinition(TargetLine)) {
171
80
          TargetLine = Lines[I + 1];
172
80
          TargetToken = TargetLine->First;
173
80
          InsertReplacement(NewlineCount);
174
80
        }
175
240
      } else 
if (61
IsNeverStyle61
) {
176
29
        TargetLine = Lines[I + 1];
177
29
        TargetToken = TargetLine->First;
178
29
        InsertReplacement(OpeningLineIndex != 0);
179
29
      }
180
301
    }
181
1.04k
  }
182
67
  for (const auto &R : Whitespaces.generateReplacements())
183
    // The add method returns an Error instance which simulates program exit
184
    // code through overloading boolean operator, thus false here indicates
185
    // success.
186
143
    if (Result.add(R))
187
0
      return;
188
67
}
189
} // namespace format
190
} // namespace clang