xref: /freebsd/contrib/llvm-project/clang/lib/Format/AffectedRangeManager.cpp (revision 06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e)
1  //===--- AffectedRangeManager.cpp - Format C++ code -----------------------===//
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 AffectRangeManager class.
11  ///
12  //===----------------------------------------------------------------------===//
13  
14  #include "AffectedRangeManager.h"
15  
16  #include "FormatToken.h"
17  #include "TokenAnnotator.h"
18  
19  namespace clang {
20  namespace format {
21  
computeAffectedLines(SmallVectorImpl<AnnotatedLine * > & Lines)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;
28    while (I != E) {
29      AnnotatedLine *Line = *I;
30      assert(Line->First);
31      Line->LeadingEmptyLinesAffected = affectsLeadingEmptyLines(*Line->First);
32  
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;
40          ++PPEnd;
41        }
42  
43        if (affectsTokenRange(*Line->First, *Last,
44                              /*IncludeLeadingNewlines=*/false)) {
45          SomeLineAffected = true;
46          markAllAsAffected(I, PPEnd);
47        }
48        I = PPEnd;
49        continue;
50      }
51  
52      if (nonPPLineAffected(Line, PreviousLine, Lines))
53        SomeLineAffected = true;
54  
55      PreviousLine = Line;
56      ++I;
57    }
58    return SomeLineAffected;
59  }
60  
affectsCharSourceRange(const CharSourceRange & Range)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())) {
66        return true;
67      }
68    }
69    return false;
70  }
71  
affectsTokenRange(const FormatToken & First,const FormatToken & Last,bool IncludeLeadingNewlines)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);
82  }
83  
affectsLeadingEmptyLines(const FormatToken & Tok)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);
89  }
90  
markAllAsAffected(SmallVectorImpl<AnnotatedLine * >::iterator I,SmallVectorImpl<AnnotatedLine * >::iterator E)91  void AffectedRangeManager::markAllAsAffected(
92      SmallVectorImpl<AnnotatedLine *>::iterator I,
93      SmallVectorImpl<AnnotatedLine *>::iterator E) {
94    while (I != E) {
95      (*I)->Affected = true;
96      markAllAsAffected((*I)->Children.begin(), (*I)->Children.end());
97      ++I;
98    }
99  }
100  
nonPPLineAffected(AnnotatedLine * Line,const AnnotatedLine * PreviousLine,SmallVectorImpl<AnnotatedLine * > & Lines)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;
108  
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;
114  
115    // Stores whether the first child line of any of this line's tokens is
116    // affected.
117    bool SomeFirstChildAffected = false;
118  
119    assert(Line->First);
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;
124  
125      // Determine whether the first child of 'Tok' was affected.
126      if (!Tok->Children.empty() && Tok->Children.front()->Affected)
127        SomeFirstChildAffected = true;
128  
129      IncludeLeadingNewlines = Tok->Children.empty();
130    }
131  
132    // Was this line moved, i.e. has it previously been on the same line as an
133    // affected line?
134    bool LineMoved = PreviousLine && PreviousLine->Affected &&
135                     Line->First->NewlinesBefore == 0;
136  
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);
141  
142    bool IsAffectedClosingBrace =
143        Line->First->is(tok::r_brace) &&
144        Line->MatchingOpeningBlockLineIndex != UnwrappedLine::kInvalidIndex &&
145        Lines[Line->MatchingOpeningBlockLineIndex]->Affected;
146  
147    if (SomeTokenAffected || SomeFirstChildAffected || LineMoved ||
148        IsContinuedComment || IsAffectedClosingBrace) {
149      Line->Affected = true;
150      SomeLineAffected = true;
151    }
152    return SomeLineAffected;
153  }
154  
155  } // namespace format
156  } // namespace clang
157