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