|  | //===--- AffectedRangeManager.cpp - Format C++ code -----------------------===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | /// | 
|  | /// \file | 
|  | /// This file implements AffectRangeManager class. | 
|  | /// | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "AffectedRangeManager.h" | 
|  |  | 
|  | #include "FormatToken.h" | 
|  | #include "TokenAnnotator.h" | 
|  |  | 
|  | namespace clang { | 
|  | namespace format { | 
|  |  | 
|  | bool AffectedRangeManager::computeAffectedLines( | 
|  | SmallVectorImpl<AnnotatedLine *> &Lines) { | 
|  | ArrayRef<AnnotatedLine *>::iterator I = Lines.begin(); | 
|  | ArrayRef<AnnotatedLine *>::iterator E = Lines.end(); | 
|  | bool SomeLineAffected = false; | 
|  | const AnnotatedLine *PreviousLine = nullptr; | 
|  | while (I != E) { | 
|  | AnnotatedLine *Line = *I; | 
|  | assert(Line->First); | 
|  | Line->LeadingEmptyLinesAffected = affectsLeadingEmptyLines(*Line->First); | 
|  |  | 
|  | // If a line is part of a preprocessor directive, it needs to be formatted | 
|  | // if any token within the directive is affected. | 
|  | if (Line->InPPDirective) { | 
|  | FormatToken *Last = Line->Last; | 
|  | const auto *PPEnd = I + 1; | 
|  | while (PPEnd != E && !(*PPEnd)->First->HasUnescapedNewline) { | 
|  | Last = (*PPEnd)->Last; | 
|  | ++PPEnd; | 
|  | } | 
|  |  | 
|  | if (affectsTokenRange(*Line->First, *Last, | 
|  | /*IncludeLeadingNewlines=*/false)) { | 
|  | SomeLineAffected = true; | 
|  | markAllAsAffected(I, PPEnd); | 
|  | } | 
|  | I = PPEnd; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (nonPPLineAffected(Line, PreviousLine, Lines)) | 
|  | SomeLineAffected = true; | 
|  |  | 
|  | PreviousLine = Line; | 
|  | ++I; | 
|  | } | 
|  | return SomeLineAffected; | 
|  | } | 
|  |  | 
|  | bool AffectedRangeManager::affectsCharSourceRange( | 
|  | const CharSourceRange &Range) { | 
|  | for (const CharSourceRange &R : Ranges) { | 
|  | if (!SourceMgr.isBeforeInTranslationUnit(Range.getEnd(), R.getBegin()) && | 
|  | !SourceMgr.isBeforeInTranslationUnit(R.getEnd(), Range.getBegin())) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool AffectedRangeManager::affectsTokenRange(const FormatToken &First, | 
|  | const FormatToken &Last, | 
|  | bool IncludeLeadingNewlines) { | 
|  | SourceLocation Start = First.WhitespaceRange.getBegin(); | 
|  | if (!IncludeLeadingNewlines) | 
|  | Start = Start.getLocWithOffset(First.LastNewlineOffset); | 
|  | SourceLocation End = Last.getStartOfNonWhitespace(); | 
|  | End = End.getLocWithOffset(Last.TokenText.size()); | 
|  | CharSourceRange Range = CharSourceRange::getCharRange(Start, End); | 
|  | return affectsCharSourceRange(Range); | 
|  | } | 
|  |  | 
|  | bool AffectedRangeManager::affectsLeadingEmptyLines(const FormatToken &Tok) { | 
|  | CharSourceRange EmptyLineRange = CharSourceRange::getCharRange( | 
|  | Tok.WhitespaceRange.getBegin(), | 
|  | Tok.WhitespaceRange.getBegin().getLocWithOffset(Tok.LastNewlineOffset)); | 
|  | return affectsCharSourceRange(EmptyLineRange); | 
|  | } | 
|  |  | 
|  | void AffectedRangeManager::markAllAsAffected( | 
|  | ArrayRef<AnnotatedLine *>::iterator I, | 
|  | ArrayRef<AnnotatedLine *>::iterator E) { | 
|  | while (I != E) { | 
|  | (*I)->Affected = true; | 
|  | markAllAsAffected((*I)->Children.begin(), (*I)->Children.end()); | 
|  | ++I; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool AffectedRangeManager::nonPPLineAffected( | 
|  | AnnotatedLine *Line, const AnnotatedLine *PreviousLine, | 
|  | SmallVectorImpl<AnnotatedLine *> &Lines) { | 
|  | bool SomeLineAffected = false; | 
|  | Line->ChildrenAffected = computeAffectedLines(Line->Children); | 
|  | if (Line->ChildrenAffected) | 
|  | SomeLineAffected = true; | 
|  |  | 
|  | // Stores whether one of the line's tokens is directly affected. | 
|  | bool SomeTokenAffected = false; | 
|  | // Stores whether we need to look at the leading newlines of the next token | 
|  | // in order to determine whether it was affected. | 
|  | bool IncludeLeadingNewlines = false; | 
|  |  | 
|  | // Stores whether the first child line of any of this line's tokens is | 
|  | // affected. | 
|  | bool SomeFirstChildAffected = false; | 
|  |  | 
|  | assert(Line->First); | 
|  | for (FormatToken *Tok = Line->First; Tok; Tok = Tok->Next) { | 
|  | // Determine whether 'Tok' was affected. | 
|  | if (affectsTokenRange(*Tok, *Tok, IncludeLeadingNewlines)) | 
|  | SomeTokenAffected = true; | 
|  |  | 
|  | // Determine whether the first child of 'Tok' was affected. | 
|  | if (!Tok->Children.empty() && Tok->Children.front()->Affected) | 
|  | SomeFirstChildAffected = true; | 
|  |  | 
|  | IncludeLeadingNewlines = Tok->Children.empty(); | 
|  | } | 
|  |  | 
|  | // Was this line moved, i.e. has it previously been on the same line as an | 
|  | // affected line? | 
|  | bool LineMoved = PreviousLine && PreviousLine->Affected && | 
|  | Line->First->NewlinesBefore == 0; | 
|  |  | 
|  | bool IsContinuedComment = | 
|  | Line->First->is(tok::comment) && !Line->First->Next && | 
|  | Line->First->NewlinesBefore < 2 && PreviousLine && | 
|  | PreviousLine->Affected && PreviousLine->Last->is(tok::comment); | 
|  |  | 
|  | bool IsAffectedClosingBrace = | 
|  | Line->First->is(tok::r_brace) && | 
|  | Line->MatchingOpeningBlockLineIndex != UnwrappedLine::kInvalidIndex && | 
|  | Lines[Line->MatchingOpeningBlockLineIndex]->Affected; | 
|  |  | 
|  | if (SomeTokenAffected || SomeFirstChildAffected || LineMoved || | 
|  | IsContinuedComment || IsAffectedClosingBrace) { | 
|  | Line->Affected = true; | 
|  | SomeLineAffected = true; | 
|  | } | 
|  | return SomeLineAffected; | 
|  | } | 
|  |  | 
|  | } // namespace format | 
|  | } // namespace clang |