brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.5 KiB · b12b370 Raw
216 lines · cpp
1//===--- ObjCPropertyAttributeOrderFixer.cpp -------------------*- 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/// This file implements ObjCPropertyAttributeOrderFixer, a TokenAnalyzer that11/// adjusts the order of attributes in an ObjC `@property(...)` declaration,12/// depending on the style.13///14//===----------------------------------------------------------------------===//15 16#include "ObjCPropertyAttributeOrderFixer.h"17 18namespace clang {19namespace format {20 21ObjCPropertyAttributeOrderFixer::ObjCPropertyAttributeOrderFixer(22    const Environment &Env, const FormatStyle &Style)23    : TokenAnalyzer(Env, Style) {24  // Create an "order priority" map to use to sort properties.25  unsigned Index = 0;26  for (const auto &Property : Style.ObjCPropertyAttributeOrder)27    SortOrderMap[Property] = Index++;28}29 30struct ObjCPropertyEntry {31  StringRef Attribute; // eg, `readwrite`32  StringRef Value;     // eg, the `foo` of the attribute `getter=foo`33};34 35void ObjCPropertyAttributeOrderFixer::sortPropertyAttributes(36    const SourceManager &SourceMgr, tooling::Replacements &Fixes,37    const FormatToken *BeginTok, const FormatToken *EndTok) {38  assert(BeginTok);39  assert(EndTok);40  assert(EndTok->Previous);41 42  // If there are zero or one tokens, nothing to do.43  if (BeginTok == EndTok || BeginTok->Next == EndTok)44    return;45 46  // Use a set to sort attributes and remove duplicates.47  std::set<unsigned> Ordinals;48 49  // Create a "remapping index" on how to reorder the attributes.50  SmallVector<int> Indices;51 52  // Collect the attributes.53  SmallVector<ObjCPropertyEntry> PropertyAttributes;54  bool HasDuplicates = false;55  int Index = 0;56  for (auto Tok = BeginTok; Tok != EndTok; Tok = Tok->Next) {57    assert(Tok);58    if (Tok->is(tok::comma)) {59      // Ignore the comma separators.60      continue;61    }62 63    // Most attributes look like identifiers, but `class` is a keyword.64    if (Tok->isNoneOf(tok::identifier, tok::kw_class)) {65      // If we hit any other kind of token, just bail.66      return;67    }68 69    const StringRef Attribute(Tok->TokenText);70    StringRef Value;71 72    // Also handle `getter=getFoo` attributes.73    // (Note: no check needed against `EndTok`, since its type is not74    // BinaryOperator or Identifier)75    assert(Tok->Next);76    if (Tok->Next->is(tok::equal)) {77      Tok = Tok->Next;78      assert(Tok->Next);79      if (Tok->Next->isNot(tok::identifier)) {80        // If we hit any other kind of token, just bail. It's unusual/illegal.81        return;82      }83      Tok = Tok->Next;84      Value = Tok->TokenText;85    }86 87    // Sort the indices based on the priority stored in `SortOrderMap`.88    const auto Ordinal =89        SortOrderMap.try_emplace(Attribute, SortOrderMap.size()).first->second;90    if (!Ordinals.insert(Ordinal).second) {91      HasDuplicates = true;92      continue;93    }94 95    if (Ordinal >= Indices.size())96      Indices.resize(Ordinal + 1);97    Indices[Ordinal] = Index++;98 99    // Memoize the attribute.100    PropertyAttributes.push_back({Attribute, Value});101  }102 103  if (!HasDuplicates) {104    // There's nothing to do unless there's more than one attribute.105    if (PropertyAttributes.size() < 2)106      return;107 108    int PrevIndex = -1;109    bool IsSorted = true;110    for (const auto Ordinal : Ordinals) {111      const auto Index = Indices[Ordinal];112      if (Index < PrevIndex) {113        IsSorted = false;114        break;115      }116      assert(Index > PrevIndex);117      PrevIndex = Index;118    }119 120    // If the property order is already correct, then no fix-up is needed.121    if (IsSorted)122      return;123  }124 125  // Generate the replacement text.126  std::string NewText;127  bool IsFirst = true;128  for (const auto Ordinal : Ordinals) {129    if (IsFirst)130      IsFirst = false;131    else132      NewText += ", ";133 134    const auto &PropertyEntry = PropertyAttributes[Indices[Ordinal]];135    NewText += PropertyEntry.Attribute;136 137    if (const auto Value = PropertyEntry.Value; !Value.empty()) {138      NewText += '=';139      NewText += Value;140    }141  }142 143  auto Range = CharSourceRange::getCharRange(144      BeginTok->getStartOfNonWhitespace(), EndTok->Previous->Tok.getEndLoc());145  auto Replacement = tooling::Replacement(SourceMgr, Range, NewText);146  auto Err = Fixes.add(Replacement);147  if (Err) {148    llvm::errs() << "Error while reodering ObjC property attributes : "149                 << llvm::toString(std::move(Err)) << "\n";150  }151}152 153void ObjCPropertyAttributeOrderFixer::analyzeObjCPropertyDecl(154    const SourceManager &SourceMgr, const AdditionalKeywords &Keywords,155    tooling::Replacements &Fixes, const FormatToken *Tok) {156  assert(Tok);157 158  // Expect `property` to be the very next token or else just bail early.159  const FormatToken *const PropertyTok = Tok->Next;160  if (!PropertyTok || PropertyTok->isNot(Keywords.kw_property))161    return;162 163  // Expect the opening paren to be the next token or else just bail early.164  const FormatToken *const LParenTok = PropertyTok->getNextNonComment();165  if (!LParenTok || LParenTok->isNot(tok::l_paren))166    return;167 168  // Get the matching right-paren, the bounds for property attributes.169  const FormatToken *const RParenTok = LParenTok->MatchingParen;170  if (!RParenTok)171    return;172 173  sortPropertyAttributes(SourceMgr, Fixes, LParenTok->Next, RParenTok);174}175 176std::pair<tooling::Replacements, unsigned>177ObjCPropertyAttributeOrderFixer::analyze(178    TokenAnnotator & /*Annotator*/,179    SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,180    FormatTokenLexer &Tokens) {181  tooling::Replacements Fixes;182  const AdditionalKeywords &Keywords = Tokens.getKeywords();183  const SourceManager &SourceMgr = Env.getSourceManager();184  AffectedRangeMgr.computeAffectedLines(AnnotatedLines);185 186  for (AnnotatedLine *Line : AnnotatedLines) {187    assert(Line);188    if (!Line->Affected || Line->Type != LT_ObjCProperty)189      continue;190    FormatToken *First = Line->First;191    assert(First);192    if (First->Finalized)193      continue;194 195    const auto *Last = Line->Last;196 197    for (const auto *Tok = First; Tok != Last; Tok = Tok->Next) {198      assert(Tok);199 200      // Skip until the `@` of a `@property` declaration.201      if (Tok->isNot(TT_ObjCProperty))202        continue;203 204      analyzeObjCPropertyDecl(SourceMgr, Keywords, Fixes, Tok);205 206      // There are never two `@property` in a line (they are split207      // by other passes), so this pass can break after just one.208      break;209    }210  }211  return {Fixes, 0};212}213 214} // namespace format215} // namespace clang216