| //===--- 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) { |
| SmallVectorImpl<AnnotatedLine *>::iterator I = Lines.begin(); |
| SmallVectorImpl<AnnotatedLine *>::iterator E = Lines.end(); |
| bool SomeLineAffected = false; |
| const AnnotatedLine *PreviousLine = nullptr; |
| while (I != E) { |
| AnnotatedLine *Line = *I; |
| 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; |
| SmallVectorImpl<AnnotatedLine *>::iterator 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 (SmallVectorImpl<CharSourceRange>::const_iterator I = Ranges.begin(), |
| E = Ranges.end(); |
| I != E; ++I) { |
| if (!SourceMgr.isBeforeInTranslationUnit(Range.getEnd(), I->getBegin()) && |
| !SourceMgr.isBeforeInTranslationUnit(I->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( |
| SmallVectorImpl<AnnotatedLine *>::iterator I, |
| SmallVectorImpl<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; |
| |
| 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 == nullptr && |
| 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 |