Path: blob/main/contrib/llvm-project/clang/lib/Format/ObjCPropertyAttributeOrderFixer.cpp
35233 views
//===--- ObjCPropertyAttributeOrderFixer.cpp -------------------*- C++--*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7///8/// \file9/// This file implements ObjCPropertyAttributeOrderFixer, a TokenAnalyzer that10/// adjusts the order of attributes in an ObjC `@property(...)` declaration,11/// depending on the style.12///13//===----------------------------------------------------------------------===//1415#include "ObjCPropertyAttributeOrderFixer.h"1617#include <algorithm>1819namespace clang {20namespace format {2122ObjCPropertyAttributeOrderFixer::ObjCPropertyAttributeOrderFixer(23const Environment &Env, const FormatStyle &Style)24: TokenAnalyzer(Env, Style) {25// Create an "order priority" map to use to sort properties.26unsigned Index = 0;27for (const auto &Property : Style.ObjCPropertyAttributeOrder)28SortOrderMap[Property] = Index++;29}3031struct ObjCPropertyEntry {32StringRef Attribute; // eg, `readwrite`33StringRef Value; // eg, the `foo` of the attribute `getter=foo`34};3536void ObjCPropertyAttributeOrderFixer::sortPropertyAttributes(37const SourceManager &SourceMgr, tooling::Replacements &Fixes,38const FormatToken *BeginTok, const FormatToken *EndTok) {39assert(BeginTok);40assert(EndTok);41assert(EndTok->Previous);4243// If there are zero or one tokens, nothing to do.44if (BeginTok == EndTok || BeginTok->Next == EndTok)45return;4647// Use a set to sort attributes and remove duplicates.48std::set<unsigned> Ordinals;4950// Create a "remapping index" on how to reorder the attributes.51SmallVector<int> Indices;5253// Collect the attributes.54SmallVector<ObjCPropertyEntry> PropertyAttributes;55bool HasDuplicates = false;56int Index = 0;57for (auto Tok = BeginTok; Tok != EndTok; Tok = Tok->Next) {58assert(Tok);59if (Tok->is(tok::comma)) {60// Ignore the comma separators.61continue;62}6364// Most attributes look like identifiers, but `class` is a keyword.65if (!Tok->isOneOf(tok::identifier, tok::kw_class)) {66// If we hit any other kind of token, just bail.67return;68}6970const StringRef Attribute{Tok->TokenText};71StringRef Value;7273// Also handle `getter=getFoo` attributes.74// (Note: no check needed against `EndTok`, since its type is not75// BinaryOperator or Identifier)76assert(Tok->Next);77if (Tok->Next->is(tok::equal)) {78Tok = Tok->Next;79assert(Tok->Next);80if (Tok->Next->isNot(tok::identifier)) {81// If we hit any other kind of token, just bail. It's unusual/illegal.82return;83}84Tok = Tok->Next;85Value = Tok->TokenText;86}8788auto It = SortOrderMap.find(Attribute);89if (It == SortOrderMap.end())90It = SortOrderMap.insert({Attribute, SortOrderMap.size()}).first;9192// Sort the indices based on the priority stored in `SortOrderMap`.93const auto Ordinal = It->second;94if (!Ordinals.insert(Ordinal).second) {95HasDuplicates = true;96continue;97}9899if (Ordinal >= Indices.size())100Indices.resize(Ordinal + 1);101Indices[Ordinal] = Index++;102103// Memoize the attribute.104PropertyAttributes.push_back({Attribute, Value});105}106107if (!HasDuplicates) {108// There's nothing to do unless there's more than one attribute.109if (PropertyAttributes.size() < 2)110return;111112int PrevIndex = -1;113bool IsSorted = true;114for (const auto Ordinal : Ordinals) {115const auto Index = Indices[Ordinal];116if (Index < PrevIndex) {117IsSorted = false;118break;119}120assert(Index > PrevIndex);121PrevIndex = Index;122}123124// If the property order is already correct, then no fix-up is needed.125if (IsSorted)126return;127}128129// Generate the replacement text.130std::string NewText;131bool IsFirst = true;132for (const auto Ordinal : Ordinals) {133if (IsFirst)134IsFirst = false;135else136NewText += ", ";137138const auto &PropertyEntry = PropertyAttributes[Indices[Ordinal]];139NewText += PropertyEntry.Attribute;140141if (const auto Value = PropertyEntry.Value; !Value.empty()) {142NewText += '=';143NewText += Value;144}145}146147auto Range = CharSourceRange::getCharRange(148BeginTok->getStartOfNonWhitespace(), EndTok->Previous->Tok.getEndLoc());149auto Replacement = tooling::Replacement(SourceMgr, Range, NewText);150auto Err = Fixes.add(Replacement);151if (Err) {152llvm::errs() << "Error while reodering ObjC property attributes : "153<< llvm::toString(std::move(Err)) << "\n";154}155}156157void ObjCPropertyAttributeOrderFixer::analyzeObjCPropertyDecl(158const SourceManager &SourceMgr, const AdditionalKeywords &Keywords,159tooling::Replacements &Fixes, const FormatToken *Tok) {160assert(Tok);161162// Expect `property` to be the very next token or else just bail early.163const FormatToken *const PropertyTok = Tok->Next;164if (!PropertyTok || PropertyTok->isNot(Keywords.kw_property))165return;166167// Expect the opening paren to be the next token or else just bail early.168const FormatToken *const LParenTok = PropertyTok->getNextNonComment();169if (!LParenTok || LParenTok->isNot(tok::l_paren))170return;171172// Get the matching right-paren, the bounds for property attributes.173const FormatToken *const RParenTok = LParenTok->MatchingParen;174if (!RParenTok)175return;176177sortPropertyAttributes(SourceMgr, Fixes, LParenTok->Next, RParenTok);178}179180std::pair<tooling::Replacements, unsigned>181ObjCPropertyAttributeOrderFixer::analyze(182TokenAnnotator & /*Annotator*/,183SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,184FormatTokenLexer &Tokens) {185tooling::Replacements Fixes;186const AdditionalKeywords &Keywords = Tokens.getKeywords();187const SourceManager &SourceMgr = Env.getSourceManager();188AffectedRangeMgr.computeAffectedLines(AnnotatedLines);189190for (AnnotatedLine *Line : AnnotatedLines) {191assert(Line);192if (!Line->Affected || Line->Type != LT_ObjCProperty)193continue;194FormatToken *First = Line->First;195assert(First);196if (First->Finalized)197continue;198199const auto *Last = Line->Last;200201for (const auto *Tok = First; Tok != Last; Tok = Tok->Next) {202assert(Tok);203204// Skip until the `@` of a `@property` declaration.205if (Tok->isNot(TT_ObjCProperty))206continue;207208analyzeObjCPropertyDecl(SourceMgr, Keywords, Fixes, Tok);209210// There are never two `@property` in a line (they are split211// by other passes), so this pass can break after just one.212break;213}214}215return {Fixes, 0};216}217218} // namespace format219} // namespace clang220221222