1515
1616#include " ObjCPropertyAttributeOrderFixer.h"
1717
18- #include " llvm/ADT/Sequence.h"
19-
2018#include < algorithm>
2119
2220namespace clang {
@@ -25,26 +23,20 @@ namespace format {
2523ObjCPropertyAttributeOrderFixer::ObjCPropertyAttributeOrderFixer (
2624 const Environment &Env, const FormatStyle &Style)
2725 : TokenAnalyzer(Env, Style) {
28-
2926 // Create an "order priority" map to use to sort properties.
30- unsigned index = 0 ;
27+ unsigned Index = 0 ;
3128 for (const auto &Property : Style.ObjCPropertyAttributeOrder )
32- SortOrderMap[Property] = index ++;
29+ SortOrderMap[Property] = Index ++;
3330}
3431
3532struct ObjCPropertyEntry {
36- StringRef Attribute; // eg, " readwrite"
37- StringRef Value; // eg, the " foo" of the attribute " getter=foo"
33+ StringRef Attribute; // eg, ` readwrite`
34+ StringRef Value; // eg, the ` foo` of the attribute ` getter=foo`
3835};
3936
40- static bool isObjCPropertyAttribute (const FormatToken *Tok) {
41- // Most attributes look like identifiers, but `class` is a keyword.
42- return Tok->isOneOf (tok::identifier, tok::kw_class);
43- }
44-
4537void ObjCPropertyAttributeOrderFixer::sortPropertyAttributes (
4638 const SourceManager &SourceMgr, tooling::Replacements &Fixes,
47- const FormatToken *BeginTok, const FormatToken *EndTok) const {
39+ const FormatToken *BeginTok, const FormatToken *EndTok) {
4840 assert (BeginTok);
4941 assert (EndTok);
5042 assert (EndTok->Previous );
@@ -53,22 +45,31 @@ void ObjCPropertyAttributeOrderFixer::sortPropertyAttributes(
5345 if (BeginTok == EndTok || BeginTok->Next == EndTok)
5446 return ;
5547
48+ // Use a set to sort attributes and remove duplicates.
49+ std::set<unsigned > Ordinals;
50+
51+ // Create a "remapping index" on how to reorder the attributes.
52+ SmallVector<int > Indices;
53+
5654 // Collect the attributes.
57- SmallVector<ObjCPropertyEntry, 8 > PropertyAttributes;
55+ SmallVector<ObjCPropertyEntry> PropertyAttributes;
56+ bool HasDuplicates = false ;
57+ int Index = 0 ;
5858 for (auto Tok = BeginTok; Tok != EndTok; Tok = Tok->Next ) {
5959 assert (Tok);
6060 if (Tok->is (tok::comma)) {
6161 // Ignore the comma separators.
6262 continue ;
6363 }
6464
65- if (!isObjCPropertyAttribute (Tok)) {
65+ // Most attributes look like identifiers, but `class` is a keyword.
66+ if (!Tok->isOneOf (tok::identifier, tok::kw_class)) {
6667 // If we hit any other kind of token, just bail.
6768 return ;
6869 }
6970
70- // Memoize the attribute. (Note that 'class' is a legal attribute!)
71- PropertyAttributes. push_back ({Tok-> TokenText , StringRef{}}) ;
71+ const StringRef Attribute{Tok-> TokenText };
72+ StringRef Value ;
7273
7374 // Also handle `getter=getFoo` attributes.
7475 // (Note: no check needed against `EndTok`, since its type is not
@@ -82,49 +83,66 @@ void ObjCPropertyAttributeOrderFixer::sortPropertyAttributes(
8283 return ;
8384 }
8485 Tok = Tok->Next ;
85- PropertyAttributes.back ().Value = Tok->TokenText ;
86+ Value = Tok->TokenText ;
87+ }
88+
89+ auto It = SortOrderMap.find (Attribute);
90+ if (It == SortOrderMap.end ())
91+ It = SortOrderMap.insert ({Attribute, SortOrderMap.size ()}).first ;
92+
93+ // Sort the indices based on the priority stored in `SortOrderMap`.
94+ const auto Ordinal = It->second ;
95+ if (!Ordinals.insert (Ordinal).second ) {
96+ HasDuplicates = true ;
97+ continue ;
8698 }
99+
100+ if (Ordinal >= Indices.size ())
101+ Indices.resize (Ordinal + 1 );
102+ Indices[Ordinal] = Index++;
103+
104+ // Memoize the attribute.
105+ PropertyAttributes.push_back ({Attribute, Value});
87106 }
88107
89- // There's nothing to do unless there's more than one attribute.
90- if (PropertyAttributes.size () < 2 )
91- return ;
108+ if (!HasDuplicates) {
109+ // There's nothing to do unless there's more than one attribute.
110+ if (PropertyAttributes.size () < 2 )
111+ return ;
92112
93- // Create a "remapping index" on how to reorder the attributes.
94- SmallVector<unsigned , 8 > Indices =
95- llvm::to_vector<8 >(llvm::seq<unsigned >(0 , PropertyAttributes.size ()));
96-
97- // Sort the indices based on the priority stored in 'SortOrderMap'; use Max
98- // for missing values.
99- const auto SortOrderMax = Style.ObjCPropertyAttributeOrder .size ();
100- auto SortIndex = [&](const StringRef &Needle) -> unsigned {
101- auto I = SortOrderMap.find (Needle);
102- return (I == SortOrderMap.end ()) ? SortOrderMax : I->getValue ();
103- };
104- llvm::stable_sort (Indices, [&](unsigned LHSI, unsigned RHSI) {
105- return SortIndex (PropertyAttributes[LHSI].Attribute ) <
106- SortIndex (PropertyAttributes[RHSI].Attribute );
107- });
108-
109- // If the property order is already correct, then no fix-up is needed.
110- if (llvm::is_sorted (Indices))
111- return ;
113+ int PrevIndex = -1 ;
114+ bool IsSorted = true ;
115+ for (const auto Ordinal : Ordinals) {
116+ const auto Index = Indices[Ordinal];
117+ if (Index < PrevIndex) {
118+ IsSorted = false ;
119+ break ;
120+ }
121+ assert (Index > PrevIndex);
122+ PrevIndex = Index;
123+ }
124+
125+ // If the property order is already correct, then no fix-up is needed.
126+ if (IsSorted)
127+ return ;
128+ }
112129
113130 // Generate the replacement text.
114131 std::string NewText;
115- const auto AppendAttribute = [&](const ObjCPropertyEntry &PropertyEntry) {
132+ bool IsFirst = true ;
133+ for (const auto Ordinal : Ordinals) {
134+ if (IsFirst)
135+ IsFirst = false ;
136+ else
137+ NewText += " , " ;
138+
139+ const auto &PropertyEntry = PropertyAttributes[Indices[Ordinal]];
116140 NewText += PropertyEntry.Attribute ;
117141
118- if (! PropertyEntry.Value .empty ()) {
119- NewText += " = " ;
120- NewText += PropertyEntry. Value ;
142+ if (const auto Value = PropertyEntry.Value ; ! Value.empty ()) {
143+ NewText += ' = ' ;
144+ NewText += Value;
121145 }
122- };
123-
124- AppendAttribute (PropertyAttributes[Indices[0 ]]);
125- for (unsigned Index : llvm::drop_begin (Indices)) {
126- NewText += " , " ;
127- AppendAttribute (PropertyAttributes[Index]);
128146 }
129147
130148 auto Range = CharSourceRange::getCharRange (
@@ -139,7 +157,7 @@ void ObjCPropertyAttributeOrderFixer::sortPropertyAttributes(
139157
140158void ObjCPropertyAttributeOrderFixer::analyzeObjCPropertyDecl (
141159 const SourceManager &SourceMgr, const AdditionalKeywords &Keywords,
142- tooling::Replacements &Fixes, const FormatToken *Tok) const {
160+ tooling::Replacements &Fixes, const FormatToken *Tok) {
143161 assert (Tok);
144162
145163 // Expect `property` to be the very next token or else just bail early.
0 commit comments