================
@@ -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);
----------------
AaronBallman wrote:

Correct, order does not matter per the standard. Spelling matters per practical 
considerations (`__attribute__((noreturn))` is not the same thing as 
`[[noreturn]]` in terms of semantics).

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

Reply via email to