1 //===--- AffectedRangeManager.cpp - Format C++ code -----------------------===//
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
7 //===----------------------------------------------------------------------===//
10 /// This file implements AffectRangeManager class.
12 //===----------------------------------------------------------------------===//
14 #include "AffectedRangeManager.h"
16 #include "FormatToken.h"
17 #include "TokenAnnotator.h"
22 bool AffectedRangeManager::computeAffectedLines(
23 SmallVectorImpl
<AnnotatedLine
*> &Lines
) {
24 SmallVectorImpl
<AnnotatedLine
*>::iterator I
= Lines
.begin();
25 SmallVectorImpl
<AnnotatedLine
*>::iterator E
= Lines
.end();
26 bool SomeLineAffected
= false;
27 const AnnotatedLine
*PreviousLine
= nullptr;
29 AnnotatedLine
*Line
= *I
;
31 Line
->LeadingEmptyLinesAffected
= affectsLeadingEmptyLines(*Line
->First
);
33 // If a line is part of a preprocessor directive, it needs to be formatted
34 // if any token within the directive is affected.
35 if (Line
->InPPDirective
) {
36 FormatToken
*Last
= Line
->Last
;
37 SmallVectorImpl
<AnnotatedLine
*>::iterator PPEnd
= I
+ 1;
38 while (PPEnd
!= E
&& !(*PPEnd
)->First
->HasUnescapedNewline
) {
39 Last
= (*PPEnd
)->Last
;
43 if (affectsTokenRange(*Line
->First
, *Last
,
44 /*IncludeLeadingNewlines=*/false)) {
45 SomeLineAffected
= true;
46 markAllAsAffected(I
, PPEnd
);
52 if (nonPPLineAffected(Line
, PreviousLine
, Lines
))
53 SomeLineAffected
= true;
58 return SomeLineAffected
;
61 bool AffectedRangeManager::affectsCharSourceRange(
62 const CharSourceRange
&Range
) {
63 for (const CharSourceRange
&R
: Ranges
) {
64 if (!SourceMgr
.isBeforeInTranslationUnit(Range
.getEnd(), R
.getBegin()) &&
65 !SourceMgr
.isBeforeInTranslationUnit(R
.getEnd(), Range
.getBegin())) {
72 bool AffectedRangeManager::affectsTokenRange(const FormatToken
&First
,
73 const FormatToken
&Last
,
74 bool IncludeLeadingNewlines
) {
75 SourceLocation Start
= First
.WhitespaceRange
.getBegin();
76 if (!IncludeLeadingNewlines
)
77 Start
= Start
.getLocWithOffset(First
.LastNewlineOffset
);
78 SourceLocation End
= Last
.getStartOfNonWhitespace();
79 End
= End
.getLocWithOffset(Last
.TokenText
.size());
80 CharSourceRange Range
= CharSourceRange::getCharRange(Start
, End
);
81 return affectsCharSourceRange(Range
);
84 bool AffectedRangeManager::affectsLeadingEmptyLines(const FormatToken
&Tok
) {
85 CharSourceRange EmptyLineRange
= CharSourceRange::getCharRange(
86 Tok
.WhitespaceRange
.getBegin(),
87 Tok
.WhitespaceRange
.getBegin().getLocWithOffset(Tok
.LastNewlineOffset
));
88 return affectsCharSourceRange(EmptyLineRange
);
91 void AffectedRangeManager::markAllAsAffected(
92 SmallVectorImpl
<AnnotatedLine
*>::iterator I
,
93 SmallVectorImpl
<AnnotatedLine
*>::iterator E
) {
95 (*I
)->Affected
= true;
96 markAllAsAffected((*I
)->Children
.begin(), (*I
)->Children
.end());
101 bool AffectedRangeManager::nonPPLineAffected(
102 AnnotatedLine
*Line
, const AnnotatedLine
*PreviousLine
,
103 SmallVectorImpl
<AnnotatedLine
*> &Lines
) {
104 bool SomeLineAffected
= false;
105 Line
->ChildrenAffected
= computeAffectedLines(Line
->Children
);
106 if (Line
->ChildrenAffected
)
107 SomeLineAffected
= true;
109 // Stores whether one of the line's tokens is directly affected.
110 bool SomeTokenAffected
= false;
111 // Stores whether we need to look at the leading newlines of the next token
112 // in order to determine whether it was affected.
113 bool IncludeLeadingNewlines
= false;
115 // Stores whether the first child line of any of this line's tokens is
117 bool SomeFirstChildAffected
= false;
120 for (FormatToken
*Tok
= Line
->First
; Tok
; Tok
= Tok
->Next
) {
121 // Determine whether 'Tok' was affected.
122 if (affectsTokenRange(*Tok
, *Tok
, IncludeLeadingNewlines
))
123 SomeTokenAffected
= true;
125 // Determine whether the first child of 'Tok' was affected.
126 if (!Tok
->Children
.empty() && Tok
->Children
.front()->Affected
)
127 SomeFirstChildAffected
= true;
129 IncludeLeadingNewlines
= Tok
->Children
.empty();
132 // Was this line moved, i.e. has it previously been on the same line as an
134 bool LineMoved
= PreviousLine
&& PreviousLine
->Affected
&&
135 Line
->First
->NewlinesBefore
== 0;
137 bool IsContinuedComment
=
138 Line
->First
->is(tok::comment
) && !Line
->First
->Next
&&
139 Line
->First
->NewlinesBefore
< 2 && PreviousLine
&&
140 PreviousLine
->Affected
&& PreviousLine
->Last
->is(tok::comment
);
142 bool IsAffectedClosingBrace
=
143 Line
->First
->is(tok::r_brace
) &&
144 Line
->MatchingOpeningBlockLineIndex
!= UnwrappedLine::kInvalidIndex
&&
145 Lines
[Line
->MatchingOpeningBlockLineIndex
]->Affected
;
147 if (SomeTokenAffected
|| SomeFirstChildAffected
|| LineMoved
||
148 IsContinuedComment
|| IsAffectedClosingBrace
) {
149 Line
->Affected
= true;
150 SomeLineAffected
= true;
152 return SomeLineAffected
;
155 } // namespace format