1
+ // ===--- IncludesSeparator.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 IncludesSeparator, a TokenAnalyzer that inserts
11
+ // / new lines or removes empty lines after an include area.
12
+ // / An includes area is a list of consecutive include statements.
13
+ // /
14
+ // ===----------------------------------------------------------------------===//
15
+
16
+ #include " IncludesSeparator.h"
17
+ #include " TokenAnnotator.h"
18
+ #define DEBUG_TYPE " includes-separator"
19
+
20
+ namespace {
21
+ bool isConditionalCompilationStart (const clang::format::AnnotatedLine &Line) {
22
+ if (!Line.First )
23
+ return false ;
24
+ const auto *NextToken = Line.First ->getNextNonComment ();
25
+ return Line.First ->is (clang::tok::hash) && NextToken &&
26
+ NextToken->isOneOf (clang::tok::pp_if, clang::tok::pp_ifdef,
27
+ clang::tok::pp_ifndef, clang::tok::pp_defined);
28
+ }
29
+
30
+ bool isConditionalCompilationEnd (const clang::format::AnnotatedLine &Line) {
31
+ if (!Line.First )
32
+ return false ;
33
+ const auto *NextToken = Line.First ->getNextNonComment ();
34
+ return Line.First ->is (clang::tok::hash) && NextToken &&
35
+ NextToken->is (clang::tok::pp_endif);
36
+ }
37
+
38
+ bool isConditionalCompilationStatement (
39
+ const clang::format::AnnotatedLine &Line) {
40
+ if (!Line.First )
41
+ return false ;
42
+ const auto *NextToken = Line.First ->getNextNonComment ();
43
+ return Line.First ->is (clang::tok::hash) && NextToken &&
44
+ NextToken->isOneOf (clang::tok::pp_if, clang::tok::pp_ifdef,
45
+ clang::tok::pp_ifndef, clang::tok::pp_elif,
46
+ clang::tok::pp_elifdef, clang::tok::pp_elifndef,
47
+ clang::tok::pp_else, clang::tok::pp_defined,
48
+ clang::tok::pp_endif);
49
+ }
50
+
51
+ bool isCCOnlyWithIncludes (
52
+ const llvm::SmallVectorImpl<clang::format::AnnotatedLine *> &Lines,
53
+ unsigned StartIdx) {
54
+ int CCLevel = 0 ;
55
+ for (unsigned I = StartIdx; I < Lines.size (); ++I) {
56
+ const auto &CurrentLine = *Lines[I];
57
+ if (isConditionalCompilationStart (CurrentLine))
58
+ CCLevel++;
59
+
60
+ if (isConditionalCompilationEnd (CurrentLine))
61
+ CCLevel--;
62
+
63
+ if (CCLevel == 0 )
64
+ break ;
65
+
66
+ if (!(CurrentLine.isInclude () ||
67
+ isConditionalCompilationStatement (CurrentLine))) {
68
+ return false ;
69
+ }
70
+ }
71
+ return true ;
72
+ }
73
+
74
+ unsigned getEndOfCCBlock (
75
+ const llvm::SmallVectorImpl<clang::format::AnnotatedLine *> &Lines,
76
+ unsigned StartIdx) {
77
+ int CCLevel = 0 ;
78
+ unsigned I = StartIdx;
79
+ for (; I < Lines.size (); ++I) {
80
+ const auto &CurrentLine = *Lines[I];
81
+ if (isConditionalCompilationStart (CurrentLine))
82
+ CCLevel++;
83
+
84
+ if (isConditionalCompilationEnd (CurrentLine))
85
+ CCLevel--;
86
+
87
+ if (CCLevel == 0 )
88
+ break ;
89
+ }
90
+ return I;
91
+ }
92
+ } // namespace
93
+
94
+ namespace clang {
95
+ namespace format {
96
+ std::pair<tooling::Replacements, unsigned >
97
+ IncludesSeparator::analyze (TokenAnnotator &Annotator,
98
+ SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
99
+ FormatTokenLexer &Tokens) {
100
+ assert (Style.EmptyLinesAfterIncludes .has_value ());
101
+ AffectedRangeMgr.computeAffectedLines (AnnotatedLines);
102
+ tooling::Replacements Result;
103
+ separateIncludes (AnnotatedLines, Result, Tokens);
104
+ return {Result, 0 };
105
+ }
106
+
107
+ void IncludesSeparator::separateIncludes (
108
+ SmallVectorImpl<AnnotatedLine *> &Lines, tooling::Replacements &Result,
109
+ FormatTokenLexer &Tokens) {
110
+ const unsigned NewlineCount =
111
+ std::min (Style.MaxEmptyLinesToKeep , *Style.EmptyLinesAfterIncludes ) + 1 ;
112
+ WhitespaceManager Whitespaces (
113
+ Env.getSourceManager (), Style,
114
+ Style.LineEnding > FormatStyle::LE_CRLF
115
+ ? WhitespaceManager::inputUsesCRLF (
116
+ Env.getSourceManager ().getBufferData (Env.getFileID ()),
117
+ Style.LineEnding == FormatStyle::LE_DeriveCRLF)
118
+ : Style.LineEnding == FormatStyle::LE_CRLF);
119
+
120
+ bool InIncludeArea = false ;
121
+ for (unsigned I = 0 ; I < Lines.size (); ++I) {
122
+ const auto &CurrentLine = *Lines[I];
123
+
124
+ if (InIncludeArea) {
125
+ if (CurrentLine.isInclude ())
126
+ continue ;
127
+
128
+ if (isConditionalCompilationStart (CurrentLine)) {
129
+ const bool CCWithOnlyIncludes = isCCOnlyWithIncludes (Lines, I);
130
+ I = getEndOfCCBlock (Lines, I);
131
+
132
+ // Conditional compilation blocks that only contain
133
+ // include statements are considered part of the include area.
134
+ if (CCWithOnlyIncludes)
135
+ continue ;
136
+ }
137
+
138
+ if (!CurrentLine.First ->is (tok::eof) && CurrentLine.Affected ) {
139
+ Whitespaces.replaceWhitespace (*CurrentLine.First , NewlineCount,
140
+ CurrentLine.First ->OriginalColumn ,
141
+ CurrentLine.First ->OriginalColumn );
142
+ }
143
+ InIncludeArea = false ;
144
+ } else {
145
+ if (CurrentLine.isInclude ())
146
+ InIncludeArea = true ;
147
+ }
148
+ }
149
+
150
+ for (const auto &R : Whitespaces.generateReplacements ()) {
151
+ // The add method returns an Error instance which simulates program exit
152
+ // code through overloading boolean operator, thus false here indicates
153
+ // success.
154
+ if (Result.add (R))
155
+ return ;
156
+ }
157
+ }
158
+ } // namespace format
159
+ } // namespace clang
160
+
0 commit comments