brintos

brintos / llvm-project-archived public Read only

0
0
Text · 14.4 KiB · 6d18db7 Raw
373 lines · c
1//===--- WhitespaceManager.h - Format C++ code ------------------*- C++ -*-===//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/// WhitespaceManager class manages whitespace around tokens and their11/// replacements.12///13//===----------------------------------------------------------------------===//14 15#ifndef LLVM_CLANG_LIB_FORMAT_WHITESPACEMANAGER_H16#define LLVM_CLANG_LIB_FORMAT_WHITESPACEMANAGER_H17 18#include "TokenAnnotator.h"19#include "clang/Basic/SourceManager.h"20 21namespace clang {22namespace format {23 24/// Manages the whitespaces around tokens and their replacements.25///26/// This includes special handling for certain constructs, e.g. the alignment of27/// trailing line comments.28///29/// To guarantee correctness of alignment operations, the \c WhitespaceManager30/// must be informed about every token in the source file; for each token, there31/// must be exactly one call to either \c replaceWhitespace or32/// \c addUntouchableToken.33///34/// There may be multiple calls to \c breakToken for a given token.35class WhitespaceManager {36public:37  WhitespaceManager(const SourceManager &SourceMgr, const FormatStyle &Style,38                    bool UseCRLF)39      : SourceMgr(SourceMgr), Style(Style), UseCRLF(UseCRLF) {}40 41  bool useCRLF() const { return UseCRLF; }42 43  /// Infers whether the input is using CRLF.44  static bool inputUsesCRLF(StringRef Text, bool DefaultToCRLF);45 46  /// Replaces the whitespace in front of \p Tok. Only call once for47  /// each \c AnnotatedToken.48  ///49  /// \p StartOfTokenColumn is the column at which the token will start after50  /// this replacement. It is needed for determining how \p Spaces is turned51  /// into tabs and spaces for some format styles.52  void replaceWhitespace(FormatToken &Tok, unsigned Newlines, unsigned Spaces,53                         unsigned StartOfTokenColumn, bool IsAligned = false,54                         bool InPPDirective = false);55 56  /// Adds information about an unchangeable token's whitespace.57  ///58  /// Needs to be called for every token for which \c replaceWhitespace59  /// was not called.60  void addUntouchableToken(const FormatToken &Tok, bool InPPDirective);61 62  llvm::Error addReplacement(const tooling::Replacement &Replacement);63 64  /// Inserts or replaces whitespace in the middle of a token.65  ///66  /// Inserts \p PreviousPostfix, \p Newlines, \p Spaces and \p CurrentPrefix67  /// (in this order) at \p Offset inside \p Tok, replacing \p ReplaceChars68  /// characters.69  ///70  /// Note: \p Spaces can be negative to retain information about initial71  /// relative column offset between a line of a block comment and the start of72  /// the comment. This negative offset may be compensated by trailing comment73  /// alignment here. In all other cases negative \p Spaces will be truncated to74  /// 0.75  ///76  /// When \p InPPDirective is true, escaped newlines are inserted. \p Spaces is77  /// used to align backslashes correctly.78  void replaceWhitespaceInToken(const FormatToken &Tok, unsigned Offset,79                                unsigned ReplaceChars,80                                StringRef PreviousPostfix,81                                StringRef CurrentPrefix, bool InPPDirective,82                                unsigned Newlines, int Spaces);83 84  /// Returns all the \c Replacements created during formatting.85  const tooling::Replacements &generateReplacements();86 87  /// Represents a change before a token, a break inside a token,88  /// or the layout of an unchanged token (or whitespace within).89  struct Change {90    /// Functor to sort changes in original source order.91    class IsBeforeInFile {92    public:93      IsBeforeInFile(const SourceManager &SourceMgr) : SourceMgr(SourceMgr) {}94      bool operator()(const Change &C1, const Change &C2) const;95 96    private:97      const SourceManager &SourceMgr;98    };99 100    /// Creates a \c Change.101    ///102    /// The generated \c Change will replace the characters at103    /// \p OriginalWhitespaceRange with a concatenation of104    /// \p PreviousLinePostfix, \p NewlinesBefore line breaks, \p Spaces spaces105    /// and \p CurrentLinePrefix.106    ///107    /// \p StartOfTokenColumn and \p InPPDirective will be used to lay out108    /// trailing comments and escaped newlines.109    Change(const FormatToken &Tok, bool CreateReplacement,110           SourceRange OriginalWhitespaceRange, int Spaces,111           unsigned StartOfTokenColumn, unsigned NewlinesBefore,112           StringRef PreviousLinePostfix, StringRef CurrentLinePrefix,113           bool IsAligned, bool ContinuesPPDirective, bool IsInsideToken);114 115    // The kind of the token whose whitespace this change replaces, or in which116    // this change inserts whitespace.117    // FIXME: Currently this is not set correctly for breaks inside comments, as118    // the \c BreakableToken is still doing its own alignment.119    const FormatToken *Tok;120 121    bool CreateReplacement;122    // Changes might be in the middle of a token, so we cannot just keep the123    // FormatToken around to query its information.124    SourceRange OriginalWhitespaceRange;125    unsigned StartOfTokenColumn;126    unsigned NewlinesBefore;127    std::string PreviousLinePostfix;128    std::string CurrentLinePrefix;129    bool IsAligned;130    bool ContinuesPPDirective;131 132    // The number of spaces in front of the token or broken part of the token.133    // This will be adapted when aligning tokens.134    // Can be negative to retain information about the initial relative offset135    // of the lines in a block comment. This is used when aligning trailing136    // comments. Uncompensated negative offset is truncated to 0.137    int Spaces;138 139    // If this change is inside of a token but not at the start of the token or140    // directly after a newline.141    bool IsInsideToken;142 143    // \c IsTrailingComment, \c TokenLength, \c PreviousEndOfTokenColumn and144    // \c EscapedNewlineColumn will be calculated in145    // \c calculateLineBreakInformation.146    bool IsTrailingComment;147    unsigned TokenLength;148    unsigned PreviousEndOfTokenColumn;149    unsigned EscapedNewlineColumn;150 151    // These fields are used to retain correct relative line indentation in a152    // block comment when aligning trailing comments.153    //154    // If this Change represents a continuation of a block comment,155    // \c StartOfBlockComment is pointer to the first Change in the block156    // comment. \c IndentationOffset is a relative column offset to this157    // change, so that the correct column can be reconstructed at the end of158    // the alignment process.159    const Change *StartOfBlockComment;160    int IndentationOffset;161 162    // Depth of conditionals. Computed from tracking fake parenthesis, except163    // it does not increase the indent for "chained" conditionals.164    int ConditionalsLevel;165 166    // A combination of indent, nesting and conditionals levels, which are used167    // in tandem to compute lexical scope, for the purposes of deciding168    // when to stop consecutive alignment runs.169    std::tuple<unsigned, unsigned, unsigned> indentAndNestingLevel() const {170      return std::make_tuple(Tok->IndentLevel, Tok->NestingLevel,171                             ConditionalsLevel);172    }173  };174 175private:176  struct CellDescription {177    unsigned Index = 0;178    unsigned Cell = 0;179    unsigned EndIndex = 0;180    bool HasSplit = false;181    CellDescription *NextColumnElement = nullptr;182 183    constexpr bool operator==(const CellDescription &Other) const {184      return Index == Other.Index && Cell == Other.Cell &&185             EndIndex == Other.EndIndex;186    }187    constexpr bool operator!=(const CellDescription &Other) const {188      return !(*this == Other);189    }190  };191 192  struct CellDescriptions {193    SmallVector<CellDescription> Cells;194    SmallVector<unsigned> CellCounts;195    unsigned InitialSpaces = 0;196 197    // Determine if every row in the array198    // has the same number of columns.199    bool isRectangular() const {200      if (CellCounts.size() < 2)201        return false;202 203      for (auto NumberOfColumns : CellCounts)204        if (NumberOfColumns != CellCounts[0])205          return false;206      return true;207    }208  };209 210  /// Calculate \c IsTrailingComment, \c TokenLength for the last tokens211  /// or token parts in a line and \c PreviousEndOfTokenColumn and212  /// \c EscapedNewlineColumn for the first tokens or token parts in a line.213  void calculateLineBreakInformation();214 215  /// Align consecutive C/C++ preprocessor macros over all \c Changes.216  void alignConsecutiveMacros();217 218  /// Align consecutive assignments over all \c Changes.219  void alignConsecutiveAssignments();220 221  /// Align consecutive bitfields over all \c Changes.222  void alignConsecutiveBitFields();223 224  /// Align consecutive colon. For bitfields, TableGen DAGArgs and defintions.225  void226  alignConsecutiveColons(const FormatStyle::AlignConsecutiveStyle &AlignStyle,227                         TokenType Type);228 229  /// Align consecutive declarations over all \c Changes.230  void alignConsecutiveDeclarations();231 232  /// Align consecutive declarations over all \c Changes.233  void alignChainedConditionals();234 235  /// Align consecutive short case statements over all \c Changes.236  void alignConsecutiveShortCaseStatements(bool IsExpr);237 238  /// Align consecutive TableGen DAGArg colon over all \c Changes.239  void alignConsecutiveTableGenBreakingDAGArgColons();240 241  /// Align consecutive TableGen cond operator colon over all \c Changes.242  void alignConsecutiveTableGenCondOperatorColons();243 244  /// Align consecutive TableGen definitions over all \c Changes.245  void alignConsecutiveTableGenDefinitions();246 247  /// Align trailing comments over all \c Changes.248  void alignTrailingComments();249 250  /// Align trailing comments from change \p Start to change \p End at251  /// the specified \p Column.252  void alignTrailingComments(unsigned Start, unsigned End, unsigned Column);253 254  /// Align escaped newlines over all \c Changes.255  void alignEscapedNewlines();256 257  /// Align escaped newlines from change \p Start to change \p End at258  /// the specified \p Column.259  void alignEscapedNewlines(unsigned Start, unsigned End, unsigned Column);260 261  /// Align Array Initializers over all \c Changes.262  void alignArrayInitializers();263 264  /// Align Array Initializers from change \p Start to change \p End at265  /// the specified \p Column.266  void alignArrayInitializers(unsigned Start, unsigned End);267 268  /// Align Array Initializers being careful to right justify the columns269  /// as described by \p CellDescs.270  void alignArrayInitializersRightJustified(CellDescriptions &&CellDescs);271 272  /// Align Array Initializers being careful to left justify the columns273  /// as described by \p CellDescs.274  void alignArrayInitializersLeftJustified(CellDescriptions &&CellDescs);275 276  /// Calculate the cell width between two indexes.277  unsigned calculateCellWidth(unsigned Start, unsigned End,278                              bool WithSpaces = false) const;279 280  /// Get a set of fully specified CellDescriptions between \p Start and281  /// \p End of the change list.282  CellDescriptions getCells(unsigned Start, unsigned End);283 284  /// Does this \p Cell contain a split element?285  static bool isSplitCell(const CellDescription &Cell);286 287  /// Get the width of the preceding cells from \p Start to \p End.288  template <typename I>289  auto getNetWidth(const I &Start, const I &End, unsigned InitialSpaces) const {290    auto NetWidth = InitialSpaces;291    for (auto PrevIter = Start; PrevIter != End; ++PrevIter) {292      // If we broke the line the initial spaces are already293      // accounted for.294      assert(PrevIter->Index < Changes.size());295      if (Changes[PrevIter->Index].NewlinesBefore > 0)296        NetWidth = 0;297      NetWidth +=298          calculateCellWidth(PrevIter->Index, PrevIter->EndIndex, true) + 1;299    }300    return NetWidth;301  }302 303  /// Get the maximum width of a cell in a sequence of columns.304  template <typename I>305  unsigned getMaximumCellWidth(I CellIter, unsigned NetWidth) const {306    unsigned CellWidth =307        calculateCellWidth(CellIter->Index, CellIter->EndIndex, true);308    if (Changes[CellIter->Index].NewlinesBefore == 0)309      CellWidth += NetWidth;310    for (const auto *Next = CellIter->NextColumnElement; Next;311         Next = Next->NextColumnElement) {312      auto ThisWidth = calculateCellWidth(Next->Index, Next->EndIndex, true);313      if (Changes[Next->Index].NewlinesBefore == 0)314        ThisWidth += NetWidth;315      CellWidth = std::max(CellWidth, ThisWidth);316    }317    return CellWidth;318  }319 320  /// Get The maximum width of all columns to a given cell.321  template <typename I>322  unsigned getMaximumNetWidth(const I &CellStart, const I &CellStop,323                              unsigned InitialSpaces, unsigned CellCount,324                              unsigned MaxRowCount) const {325    auto MaxNetWidth = getNetWidth(CellStart, CellStop, InitialSpaces);326    auto RowCount = 1U;327    auto Offset = std::distance(CellStart, CellStop);328    for (const auto *Next = CellStop->NextColumnElement; Next;329         Next = Next->NextColumnElement) {330      if (RowCount >= MaxRowCount)331        break;332      auto Start = (CellStart + RowCount * CellCount);333      auto End = Start + Offset;334      MaxNetWidth =335          std::max(MaxNetWidth, getNetWidth(Start, End, InitialSpaces));336      ++RowCount;337    }338    return MaxNetWidth;339  }340 341  /// Align a split cell with a newline to the first element in the cell.342  void alignToStartOfCell(unsigned Start, unsigned End);343 344  /// Link the Cell pointers in the list of Cells.345  static CellDescriptions linkCells(CellDescriptions &&CellDesc);346 347  /// Fill \c Replaces with the replacements for all effective changes.348  void generateChanges();349 350  /// Stores \p Text as the replacement for the whitespace in \p Range.351  void storeReplacement(SourceRange Range, StringRef Text);352  void appendNewlineText(std::string &Text, const Change &C);353  void appendEscapedNewlineText(std::string &Text, unsigned Newlines,354                                unsigned PreviousEndOfTokenColumn,355                                unsigned EscapedNewlineColumn);356  void appendIndentText(std::string &Text, unsigned IndentLevel,357                        unsigned Spaces, unsigned WhitespaceStartColumn,358                        bool IsAligned);359  unsigned appendTabIndent(std::string &Text, unsigned Spaces,360                           unsigned Indentation);361 362  SmallVector<Change, 16> Changes;363  const SourceManager &SourceMgr;364  tooling::Replacements Replaces;365  const FormatStyle &Style;366  bool UseCRLF;367};368 369} // namespace format370} // namespace clang371 372#endif373