#include "ObjCPropertyAttributeOrderFixer.h"
#include <algorithm>
namespace clang {
namespace format {
ObjCPropertyAttributeOrderFixer::ObjCPropertyAttributeOrderFixer(
const Environment &Env, const FormatStyle &Style)
: TokenAnalyzer(Env, Style) {
unsigned Index = 0;
for (const auto &Property : Style.ObjCPropertyAttributeOrder)
SortOrderMap[Property] = Index++;
}
struct ObjCPropertyEntry {
StringRef Attribute;
StringRef Value;
};
void ObjCPropertyAttributeOrderFixer::sortPropertyAttributes(
const SourceManager &SourceMgr, tooling::Replacements &Fixes,
const FormatToken *BeginTok, const FormatToken *EndTok) {
assert(BeginTok);
assert(EndTok);
assert(EndTok->Previous);
if (BeginTok == EndTok || BeginTok->Next == EndTok)
return;
std::set<unsigned> Ordinals;
SmallVector<int> Indices;
SmallVector<ObjCPropertyEntry> PropertyAttributes;
bool HasDuplicates = false;
int Index = 0;
for (auto Tok = BeginTok; Tok != EndTok; Tok = Tok->Next) {
assert(Tok);
if (Tok->is(tok::comma)) {
continue;
}
if (!Tok->isOneOf(tok::identifier, tok::kw_class)) {
return;
}
const StringRef Attribute{Tok->TokenText};
StringRef Value;
assert(Tok->Next);
if (Tok->Next->is(tok::equal)) {
Tok = Tok->Next;
assert(Tok->Next);
if (Tok->Next->isNot(tok::identifier)) {
return;
}
Tok = Tok->Next;
Value = Tok->TokenText;
}
auto It = SortOrderMap.find(Attribute);
if (It == SortOrderMap.end())
It = SortOrderMap.insert({Attribute, SortOrderMap.size()}).first;
const auto Ordinal = It->second;
if (!Ordinals.insert(Ordinal).second) {
HasDuplicates = true;
continue;
}
if (Ordinal >= Indices.size())
Indices.resize(Ordinal + 1);
Indices[Ordinal] = Index++;
PropertyAttributes.push_back({Attribute, Value});
}
if (!HasDuplicates) {
if (PropertyAttributes.size() < 2)
return;
int PrevIndex = -1;
bool IsSorted = true;
for (const auto Ordinal : Ordinals) {
const auto Index = Indices[Ordinal];
if (Index < PrevIndex) {
IsSorted = false;
break;
}
assert(Index > PrevIndex);
PrevIndex = Index;
}
if (IsSorted)
return;
}
std::string NewText;
bool IsFirst = true;
for (const auto Ordinal : Ordinals) {
if (IsFirst)
IsFirst = false;
else
NewText += ", ";
const auto &PropertyEntry = PropertyAttributes[Indices[Ordinal]];
NewText += PropertyEntry.Attribute;
if (const auto Value = PropertyEntry.Value; !Value.empty()) {
NewText += '=';
NewText += Value;
}
}
auto Range = CharSourceRange::getCharRange(
BeginTok->getStartOfNonWhitespace(), EndTok->Previous->Tok.getEndLoc());
auto Replacement = tooling::Replacement(SourceMgr, Range, NewText);
auto Err = Fixes.add(Replacement);
if (Err) {
llvm::errs() << "Error while reodering ObjC property attributes : "
<< llvm::toString(std::move(Err)) << "\n";
}
}
void ObjCPropertyAttributeOrderFixer::analyzeObjCPropertyDecl(
const SourceManager &SourceMgr, const AdditionalKeywords &Keywords,
tooling::Replacements &Fixes, const FormatToken *Tok) {
assert(Tok);
const FormatToken *const PropertyTok = Tok->Next;
if (!PropertyTok || PropertyTok->isNot(Keywords.kw_property))
return;
const FormatToken *const LParenTok = PropertyTok->getNextNonComment();
if (!LParenTok || LParenTok->isNot(tok::l_paren))
return;
const FormatToken *const RParenTok = LParenTok->MatchingParen;
if (!RParenTok)
return;
sortPropertyAttributes(SourceMgr, Fixes, LParenTok->Next, RParenTok);
}
std::pair<tooling::Replacements, unsigned>
ObjCPropertyAttributeOrderFixer::analyze(
TokenAnnotator & ,
SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
FormatTokenLexer &Tokens) {
tooling::Replacements Fixes;
const AdditionalKeywords &Keywords = Tokens.getKeywords();
const SourceManager &SourceMgr = Env.getSourceManager();
AffectedRangeMgr.computeAffectedLines(AnnotatedLines);
for (AnnotatedLine *Line : AnnotatedLines) {
assert(Line);
if (!Line->Affected || Line->Type != LT_ObjCProperty)
continue;
FormatToken *First = Line->First;
assert(First);
if (First->Finalized)
continue;
const auto *Last = Line->Last;
for (const auto *Tok = First; Tok != Last; Tok = Tok->Next) {
assert(Tok);
if (Tok->isNot(TT_ObjCProperty))
continue;
analyzeObjCPropertyDecl(SourceMgr, Keywords, Fixes, Tok);
break;
}
}
return {Fixes, 0};
}
}
}