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