157 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8///9/// \file10/// This file implements AffectRangeManager class.11///12//===----------------------------------------------------------------------===//13 14#include "AffectedRangeManager.h"15 16#include "FormatToken.h"17#include "TokenAnnotator.h"18 19namespace clang {20namespace format {21 22bool AffectedRangeManager::computeAffectedLines(23 SmallVectorImpl<AnnotatedLine *> &Lines) {24 ArrayRef<AnnotatedLine *>::iterator I = Lines.begin();25 ArrayRef<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 formatted34 // if any token within the directive is affected.35 if (Line->InPPDirective) {36 FormatToken *Last = Line->Last;37 const auto *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 61bool 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 72bool 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 84bool 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 91void AffectedRangeManager::markAllAsAffected(92 ArrayRef<AnnotatedLine *>::iterator I,93 ArrayRef<AnnotatedLine *>::iterator E) {94 while (I != E) {95 (*I)->Affected = true;96 markAllAsAffected((*I)->Children.begin(), (*I)->Children.end());97 ++I;98 }99}100 101bool 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 token112 // 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 is116 // 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 an133 // 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 format156} // namespace clang157