================ @@ -450,6 +453,116 @@ class StmtComparer { }; } // namespace +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + const Attr *Attr1, const Attr *Attr2) { + // Two attributes are structurally equivalent if they are the same kind + // of attribute, spelled with the same spelling kind, and have the same + // arguments. This means that [[noreturn]] and __attribute__((noreturn)) are + // not structurally equivalent, nor are [[nodiscard("foo")]] and + // [[nodiscard("bar")]]. + if (Attr1->getKind() != Attr2->getKind()) + return false; + + if (Attr1->getSyntax() != Attr2->getSyntax()) + return false; + + if (Attr1->getSpellingListIndex() != Attr2->getSpellingListIndex()) + return false; + + auto GetAttrName = [](const Attr *A) { + if (const IdentifierInfo *II = A->getAttrName()) + return II->getName(); + return StringRef{}; + }; + + if (GetAttrName(Attr1) != GetAttrName(Attr2)) + return false; + + // FIXME: check the attribute arguments. Attr does not track the arguments on + // the base class, which makes this awkward. We may want to tablegen a + // comparison function for attributes? In the meantime, we're doing this the + // cheap way by pretty printing the attributes and checking they produce + // equivalent string representations. + std::string AttrStr1, AttrStr2; + PrintingPolicy DefaultPolicy(Context.LangOpts); + llvm::raw_string_ostream SS1(AttrStr1), SS2(AttrStr2); + Attr1->printPretty(SS1, DefaultPolicy); + Attr2->printPretty(SS2, DefaultPolicy); + + return SS1.str() == SS2.str(); +} + +static bool +CheckStructurallyEquivalentAttributes(StructuralEquivalenceContext &Context, + const Decl *D1, const Decl *D2, + const Decl *PrimaryDecl = nullptr) { + // Gather the attributes and sort them by name so that they're in equivalent + // orders. This means that __attribute__((foo, bar)) is equivalent to + // __attribute__((bar, foo)). + llvm::SmallVector<const Attr *, 2> Attrs1, Attrs2; + auto CopyAttrs = [](auto &&Range, llvm::SmallVectorImpl<const Attr *> &Cont) { + for (const Attr *A : Range) + Cont.push_back(A); + }; + CopyAttrs(D1->attrs(), Attrs1); + CopyAttrs(D2->attrs(), Attrs2); + + auto Sorter = [](const Attr *LHS, const Attr *RHS) { + const IdentifierInfo *II1 = LHS->getAttrName(), *II2 = RHS->getAttrName(); + if (!II1 || !II2) + return II1 == II2; + return *II1 < *II2; + }; + llvm::sort(Attrs1, Sorter); + llvm::sort(Attrs2, Sorter); + + auto A2 = Attrs2.begin(), A2End = Attrs2.end(); + const auto *DiagnoseDecl = cast<TypeDecl>(PrimaryDecl ? PrimaryDecl : D2); + for (auto A1 = Attrs1.begin(), A1End = Attrs1.end(); A1 != A1End; + ++A1, ++A2) { + if (A2 == A2End) { ---------------- Sirraide wrote:
It might be easier to bail out on `Attrs1.size() != Atrrs2.size()` first and then use `llvm::zip` here? I haven’t thought too hard about this thought. https://github.com/llvm/llvm-project/pull/132939 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits