<div dir="ltr">Hello Balazs,<br><br>This commit broke at least one of our builders:<br><a href="http://lab.llvm.org:8011/builders/llvm-clang-x86_64-expensive-checks-win/builds/10897">http://lab.llvm.org:8011/builders/llvm-clang-x86_64-expensive-checks-win/builds/10897</a><br><br>. . . <br>Failing Tests (1):<br>    Clang-Unit :: AST/./ASTTests.exe/StructuralEquivalenceRecordTest.Name<br><br>Please have a look?<br>It is not good idea to keep the bot red for too long. This hides new problem which later hard to track down.<br><br>Thanks<br><br><div>Galina</div><div><br></div><div class="gmail_extra"><br><div class="gmail_quote">On Wed, Jul 11, 2018 at 2:37 AM, Balazs Keri via cfe-commits <span dir="ltr"><<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Author: balazske<br>
Date: Wed Jul 11 02:37:24 2018<br>
New Revision: 336776<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=336776&view=rev" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project?rev=336776&view=rev</a><br>
Log:<br>
[AST] Structural equivalence of methods<br>
<br>
Summary:<br>
Added structural equivalence check for C++ methods.<br>
Improved structural equivalence tests.<br>
Added related ASTImporter tests.<br>
<br>
Reviewers: a.sidorin, szepet, xazax.hun, martong, a_sidorin<br>
<br>
Reviewed By: martong, a_sidorin<br>
<br>
Subscribers: a_sidorin, rnkovacs, cfe-commits<br>
<br>
Differential Revision: <a href="https://reviews.llvm.org/D48628" rel="noreferrer" target="_blank">https://reviews.llvm.org/<wbr>D48628</a><br>
<br>
Modified:<br>
    cfe/trunk/lib/AST/ASTImporter.<wbr>cpp<br>
    cfe/trunk/lib/AST/<wbr>ASTStructuralEquivalence.cpp<br>
    cfe/trunk/unittests/AST/<wbr>ASTImporterTest.cpp<br>
    cfe/trunk/unittests/AST/<wbr>StructuralEquivalenceTest.cpp<br>
<br>
Modified: cfe/trunk/lib/AST/ASTImporter.<wbr>cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTImporter.cpp?rev=336776&r1=336775&r2=336776&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/lib/AST/<wbr>ASTImporter.cpp?rev=336776&r1=<wbr>336775&r2=336776&view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/lib/AST/ASTImporter.<wbr>cpp (original)<br>
+++ cfe/trunk/lib/AST/ASTImporter.<wbr>cpp Wed Jul 11 02:37:24 2018<br>
@@ -230,6 +230,7 @@ namespace clang {<br>
     bool IsStructuralMatch(<wbr>EnumConstantDecl *FromEC, EnumConstantDecl *ToEC);<br>
     bool IsStructuralMatch(<wbr>FunctionTemplateDecl *From,<br>
                            FunctionTemplateDecl *To);<br>
+    bool IsStructuralMatch(FunctionDecl *From, FunctionDecl *To);<br>
     bool IsStructuralMatch(<wbr>ClassTemplateDecl *From, ClassTemplateDecl *To);<br>
     bool IsStructuralMatch(<wbr>VarTemplateDecl *From, VarTemplateDecl *To);<br>
     Decl *VisitDecl(Decl *D);<br>
@@ -1525,6 +1526,13 @@ bool ASTNodeImporter::<wbr>IsStructuralMatch(<br>
   return Ctx.IsStructurallyEquivalent(<wbr>From, To);<br>
 }<br>
<br>
+bool ASTNodeImporter::<wbr>IsStructuralMatch(FunctionDecl *From, FunctionDecl *To) {<br>
+  StructuralEquivalenceContext Ctx(<br>
+      Importer.getFromContext(), Importer.getToContext(),<br>
+      Importer.<wbr>getNonEquivalentDecls(), false, false);<br>
+  return Ctx.IsStructurallyEquivalent(<wbr>From, To);<br>
+}<br>
+<br>
 bool ASTNodeImporter::<wbr>IsStructuralMatch(<wbr>EnumConstantDecl *FromEC,<br>
                                         EnumConstantDecl *ToEC) {<br>
   const llvm::APSInt &FromVal = FromEC->getInitVal();<br>
@@ -2433,13 +2441,15 @@ Decl *ASTNodeImporter::<wbr>VisitFunctionDecl<br>
       if (auto *FoundFunction = dyn_cast<FunctionDecl>(<wbr>FoundDecl)) {<br>
         if (FoundFunction-><wbr>hasExternalFormalLinkage() &&<br>
             D->hasExternalFormalLinkage()) {<br>
-          if (Importer.<wbr>IsStructurallyEquivalent(D-><wbr>getType(), <br>
-                                                FoundFunction->getType())) {<br>
-              if (D-><wbr>doesThisDeclarationHaveABody() &&<br>
-                  FoundFunction->hasBody())<br>
-                return Importer.Imported(D, FoundFunction);<br>
-              FoundByLookup = FoundFunction;<br>
-              break;<br>
+          if (IsStructuralMatch(D, FoundFunction)) {<br>
+            const FunctionDecl *Definition = nullptr;<br>
+            if (D-><wbr>doesThisDeclarationHaveABody() &&<br>
+                FoundFunction->hasBody(<wbr>Definition)) {<br>
+              return Importer.Imported(<br>
+                  D, const_cast<FunctionDecl *>(Definition));<br>
+            }<br>
+            FoundByLookup = FoundFunction;<br>
+            break;<br>
           }<br>
<br>
           // FIXME: Check for overloading more carefully, e.g., by boosting<br>
<br>
Modified: cfe/trunk/lib/AST/<wbr>ASTStructuralEquivalence.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTStructuralEquivalence.cpp?rev=336776&r1=336775&r2=336776&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/lib/AST/<wbr>ASTStructuralEquivalence.cpp?<wbr>rev=336776&r1=336775&r2=<wbr>336776&view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/lib/AST/<wbr>ASTStructuralEquivalence.cpp (original)<br>
+++ cfe/trunk/lib/AST/<wbr>ASTStructuralEquivalence.cpp Wed Jul 11 02:37:24 2018<br>
@@ -250,6 +250,9 @@ static bool IsStructurallyEquivalent(Str<br>
   if (T1.isNull() || T2.isNull())<br>
     return T1.isNull() && T2.isNull();<br>
<br>
+  QualType OrigT1 = T1;<br>
+  QualType OrigT2 = T2;<br>
+<br>
   if (!Context.StrictTypeSpelling) {<br>
     // We aren't being strict about token-to-token equivalence of types,<br>
     // so map down to the canonical type.<br>
@@ -422,6 +425,7 @@ static bool IsStructurallyEquivalent(Str<br>
   case Type::FunctionProto: {<br>
     const auto *Proto1 = cast<FunctionProtoType>(T1);<br>
     const auto *Proto2 = cast<FunctionProtoType>(T2);<br>
+<br>
     if (Proto1->getNumParams() != Proto2->getNumParams())<br>
       return false;<br>
     for (unsigned I = 0, N = Proto1->getNumParams(); I != N; ++I) {<br>
@@ -431,23 +435,33 @@ static bool IsStructurallyEquivalent(Str<br>
     }<br>
     if (Proto1->isVariadic() != Proto2->isVariadic())<br>
       return false;<br>
-    if (Proto1->getExceptionSpecType(<wbr>) != Proto2->getExceptionSpecType()<wbr>)<br>
+<br>
+    if (Proto1->getTypeQuals() != Proto2->getTypeQuals())<br>
+      return false;<br>
+    <br>
+    // Check exceptions, this information is lost in canonical type.<br>
+    const auto *OrigProto1 =<br>
+        cast<FunctionProtoType>(<wbr>OrigT1.getDesugaredType(<wbr>Context.FromCtx));<br>
+    const auto *OrigProto2 =<br>
+        cast<FunctionProtoType>(<wbr>OrigT2.getDesugaredType(<wbr>Context.ToCtx));<br>
+    auto Spec1 = OrigProto1-><wbr>getExceptionSpecType();<br>
+    auto Spec2 = OrigProto2-><wbr>getExceptionSpecType();<br>
+    <br>
+    if (Spec1 != Spec2)<br>
       return false;<br>
-    if (Proto1->getExceptionSpecType(<wbr>) == EST_Dynamic) {<br>
-      if (Proto1->getNumExceptions() != Proto2->getNumExceptions())<br>
+    if (Spec1 == EST_Dynamic) {<br>
+      if (OrigProto1->getNumExceptions(<wbr>) != OrigProto2->getNumExceptions()<wbr>)<br>
         return false;<br>
-      for (unsigned I = 0, N = Proto1->getNumExceptions(); I != N; ++I) {<br>
-        if (!IsStructurallyEquivalent(<wbr>Context, Proto1->getExceptionType(I),<br>
-                                      Proto2->getExceptionType(I)))<br>
+      for (unsigned I = 0, N = OrigProto1->getNumExceptions()<wbr>; I != N; ++I) {<br>
+        if (!IsStructurallyEquivalent(<wbr>Context, OrigProto1->getExceptionType(<wbr>I),<br>
+                                      OrigProto2->getExceptionType(<wbr>I)))<br>
           return false;<br>
       }<br>
-    } else if (isComputedNoexcept(Proto1-><wbr>getExceptionSpecType())) {<br>
-      if (!IsStructurallyEquivalent(<wbr>Context, Proto1->getNoexceptExpr(),<br>
-                                    Proto2->getNoexceptExpr()))<br>
+    } else if (isComputedNoexcept(Spec1)) {<br>
+      if (!IsStructurallyEquivalent(<wbr>Context, OrigProto1->getNoexceptExpr(),<br>
+                                    OrigProto2->getNoexceptExpr())<wbr>)<br>
         return false;<br>
     }<br>
-    if (Proto1->getTypeQuals() != Proto2->getTypeQuals())<br>
-      return false;<br>
<br>
     // Fall through to check the bits common with FunctionNoProtoType.<br>
     LLVM_FALLTHROUGH;<br>
@@ -830,6 +844,56 @@ static bool IsStructurallyEquivalent(Str<br>
   return true;<br>
 }<br>
<br>
+/// Determine structural equivalence of two methodss.<br>
+static bool IsStructurallyEquivalent(<wbr>StructuralEquivalenceContext &Context,<br>
+                                     CXXMethodDecl *Method1,<br>
+                                     CXXMethodDecl *Method2) {<br>
+  bool PropertiesEqual =<br>
+      Method1->getDeclKind() == Method2->getDeclKind() &&<br>
+      Method1->getRefQualifier() == Method2->getRefQualifier() &&<br>
+      Method1->getAccess() == Method2->getAccess() &&<br>
+      Method1-><wbr>getOverloadedOperator() == Method2-><wbr>getOverloadedOperator() &&<br>
+      Method1->isStatic() == Method2->isStatic() &&<br>
+      Method1->isConst() == Method2->isConst() &&<br>
+      Method1->isVolatile() == Method2->isVolatile() &&<br>
+      Method1->isVirtual() == Method2->isVirtual() &&<br>
+      Method1->isPure() == Method2->isPure() &&<br>
+      Method1->isDefaulted() == Method2->isDefaulted() &&<br>
+      Method1->isDeleted() == Method2->isDeleted();<br>
+  if (!PropertiesEqual)<br>
+    return false;<br>
+  // FIXME: Check for 'final'.<br>
+<br>
+  if (auto *Constructor1 = dyn_cast<CXXConstructorDecl>(<wbr>Method1)) {<br>
+    auto *Constructor2 = cast<CXXConstructorDecl>(<wbr>Method2);<br>
+    if (Constructor1->isExplicit() != Constructor2->isExplicit())<br>
+      return false;<br>
+  }<br>
+<br>
+  if (auto *Conversion1 = dyn_cast<CXXConversionDecl>(<wbr>Method1)) {<br>
+    auto *Conversion2 = cast<CXXConversionDecl>(<wbr>Method2);<br>
+    if (Conversion1->isExplicit() != Conversion2->isExplicit())<br>
+      return false;<br>
+    if (!IsStructurallyEquivalent(<wbr>Context, Conversion1-><wbr>getConversionType(),<br>
+                                  Conversion2-><wbr>getConversionType()))<br>
+      return false;<br>
+  }<br>
+<br>
+  const IdentifierInfo *Name1 = Method1->getIdentifier();<br>
+  const IdentifierInfo *Name2 = Method2->getIdentifier();<br>
+  if (!::IsStructurallyEquivalent(<wbr>Name1, Name2)) {<br>
+    return false;<br>
+    // TODO: Names do not match, add warning like at check for FieldDecl.<br>
+  }<br>
+<br>
+  // Check the prototypes.<br>
+  if (!::IsStructurallyEquivalent(<wbr>Context,<br>
+                                  Method1->getType(), Method2->getType()))<br>
+    return false;<br>
+<br>
+  return true;<br>
+}<br>
+<br>
 /// Determine structural equivalence of two records.<br>
 static bool IsStructurallyEquivalent(<wbr>StructuralEquivalenceContext &Context,<br>
                                      RecordDecl *D1, RecordDecl *D2) {<br>
@@ -1443,6 +1507,14 @@ bool StructuralEquivalenceContext::<wbr>Finis<br>
           Equivalent = false;<br>
       } else {<br>
         // Kind mismatch.<br>
+        Equivalent = false;<br>
+      }<br>
+    } else if (auto *MD1 = dyn_cast<CXXMethodDecl>(D1)) {<br>
+      if (auto *MD2 = dyn_cast<CXXMethodDecl>(D2)) {<br>
+        if (!::IsStructurallyEquivalent(*<wbr>this, MD1, MD2))<br>
+          Equivalent = false;<br>
+      } else {<br>
+        // Kind mismatch.<br>
         Equivalent = false;<br>
       }<br>
     } else if (FunctionDecl *FD1 = dyn_cast<FunctionDecl>(D1)) {<br>
<br>
Modified: cfe/trunk/unittests/AST/<wbr>ASTImporterTest.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/AST/ASTImporterTest.cpp?rev=336776&r1=336775&r2=336776&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/unittests/<wbr>AST/ASTImporterTest.cpp?rev=<wbr>336776&r1=336775&r2=336776&<wbr>view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/unittests/AST/<wbr>ASTImporterTest.cpp (original)<br>
+++ cfe/trunk/unittests/AST/<wbr>ASTImporterTest.cpp Wed Jul 11 02:37:24 2018<br>
@@ -2243,6 +2243,132 @@ TEST_P(ImportExpr, UnresolvedMemberExpr)<br>
                  compoundStmt(has(callExpr(has(<wbr>unresolvedMemberExpr()))))))))<wbr>);<br>
 }<br>
<br>
+TEST_P(ASTImporterTestBase, ImportOfEquivalentRecord) {<br>
+  Decl *ToR1;<br>
+  {<br>
+    Decl *FromTU = getTuDecl(<br>
+        "struct A { };", Lang_CXX, "input0.cc");<br>
+    auto *FromR = FirstDeclMatcher<<wbr>CXXRecordDecl>().match(<br>
+        FromTU, cxxRecordDecl(hasName("A")));<br>
+<br>
+    ToR1 = Import(FromR, Lang_CXX);<br>
+  }<br>
+<br>
+  Decl *ToR2;<br>
+  {<br>
+    Decl *FromTU = getTuDecl(<br>
+        "struct A { };", Lang_CXX, "input1.cc");<br>
+    auto *FromR = FirstDeclMatcher<<wbr>CXXRecordDecl>().match(<br>
+        FromTU, cxxRecordDecl(hasName("A")));<br>
+<br>
+    ToR2 = Import(FromR, Lang_CXX);<br>
+  }<br>
+  <br>
+  EXPECT_EQ(ToR1, ToR2);<br>
+}<br>
+<br>
+TEST_P(ASTImporterTestBase, ImportOfNonEquivalentRecord) {<br>
+  Decl *ToR1;<br>
+  {<br>
+    Decl *FromTU = getTuDecl(<br>
+        "struct A { int x; };", Lang_CXX, "input0.cc");<br>
+    auto *FromR = FirstDeclMatcher<<wbr>CXXRecordDecl>().match(<br>
+        FromTU, cxxRecordDecl(hasName("A")));<br>
+    ToR1 = Import(FromR, Lang_CXX);<br>
+  }<br>
+  Decl *ToR2;<br>
+  {<br>
+    Decl *FromTU = getTuDecl(<br>
+        "struct A { unsigned x; };", Lang_CXX, "input1.cc");<br>
+    auto *FromR = FirstDeclMatcher<<wbr>CXXRecordDecl>().match(<br>
+        FromTU, cxxRecordDecl(hasName("A")));<br>
+    ToR2 = Import(FromR, Lang_CXX);<br>
+  }<br>
+  EXPECT_NE(ToR1, ToR2);<br>
+}<br>
+<br>
+TEST_P(ASTImporterTestBase, ImportOfEquivalentField) {<br>
+  Decl *ToF1;<br>
+  {<br>
+    Decl *FromTU = getTuDecl(<br>
+        "struct A { int x; };", Lang_CXX, "input0.cc");<br>
+    auto *FromF = FirstDeclMatcher<FieldDecl>().<wbr>match(<br>
+        FromTU, fieldDecl(hasName("x")));<br>
+    ToF1 = Import(FromF, Lang_CXX);<br>
+  }<br>
+  Decl *ToF2;<br>
+  {<br>
+    Decl *FromTU = getTuDecl(<br>
+        "struct A { int x; };", Lang_CXX, "input1.cc");<br>
+    auto *FromF = FirstDeclMatcher<FieldDecl>().<wbr>match(<br>
+        FromTU, fieldDecl(hasName("x")));<br>
+    ToF2 = Import(FromF, Lang_CXX);<br>
+  }<br>
+  EXPECT_EQ(ToF1, ToF2);<br>
+}<br>
+<br>
+TEST_P(ASTImporterTestBase, ImportOfNonEquivalentField) {<br>
+  Decl *ToF1;<br>
+  {<br>
+    Decl *FromTU = getTuDecl(<br>
+        "struct A { int x; };", Lang_CXX, "input0.cc");<br>
+    auto *FromF = FirstDeclMatcher<FieldDecl>().<wbr>match(<br>
+        FromTU, fieldDecl(hasName("x")));<br>
+    ToF1 = Import(FromF, Lang_CXX);<br>
+  }<br>
+  Decl *ToF2;<br>
+  {<br>
+    Decl *FromTU = getTuDecl(<br>
+        "struct A { unsigned x; };", Lang_CXX, "input1.cc");<br>
+    auto *FromF = FirstDeclMatcher<FieldDecl>().<wbr>match(<br>
+        FromTU, fieldDecl(hasName("x")));<br>
+    ToF2 = Import(FromF, Lang_CXX);<br>
+  }<br>
+  EXPECT_NE(ToF1, ToF2);<br>
+}<br>
+<br>
+TEST_P(ASTImporterTestBase, ImportOfEquivalentMethod) {<br>
+  Decl *ToM1;<br>
+  {<br>
+    Decl *FromTU = getTuDecl(<br>
+        "struct A { void x(); }; void A::x() { }", Lang_CXX, "input0.cc");<br>
+    auto *FromM = FirstDeclMatcher<FunctionDecl><wbr>().match(<br>
+        FromTU, functionDecl(hasName("x"), isDefinition()));<br>
+    ToM1 = Import(FromM, Lang_CXX);<br>
+  }<br>
+  Decl *ToM2;<br>
+  {<br>
+    Decl *FromTU = getTuDecl(<br>
+        "struct A { void x(); }; void A::x() { }", Lang_CXX, "input1.cc");<br>
+    auto *FromM = FirstDeclMatcher<FunctionDecl><wbr>().match(<br>
+        FromTU, functionDecl(hasName("x"), isDefinition()));<br>
+    ToM2 = Import(FromM, Lang_CXX);<br>
+  }<br>
+  EXPECT_EQ(ToM1, ToM2);<br>
+}<br>
+<br>
+TEST_P(ASTImporterTestBase, ImportOfNonEquivalentMethod) {<br>
+  Decl *ToM1;<br>
+  {<br>
+    Decl *FromTU = getTuDecl(<br>
+        "struct A { void x(); }; void A::x() { }",<br>
+        Lang_CXX, "input0.cc");<br>
+    auto *FromM = FirstDeclMatcher<FunctionDecl><wbr>().match(<br>
+        FromTU, functionDecl(hasName("x"), isDefinition()));<br>
+    ToM1 = Import(FromM, Lang_CXX);<br>
+  }<br>
+  Decl *ToM2;<br>
+  {<br>
+    Decl *FromTU = getTuDecl(<br>
+        "struct A { void x() const; }; void A::x() const { }",<br>
+        Lang_CXX, "input1.cc");<br>
+    auto *FromM = FirstDeclMatcher<FunctionDecl><wbr>().match(<br>
+        FromTU, functionDecl(hasName("x"), isDefinition()));<br>
+    ToM2 = Import(FromM, Lang_CXX);<br>
+  }<br>
+  EXPECT_NE(ToM1, ToM2);<br>
+}<br>
+<br>
 struct DeclContextTest : ASTImporterTestBase {};<br>
<br>
 TEST_P(DeclContextTest, removeDeclOfClassTemplateSpeci<wbr>alization) {<br>
<br>
Modified: cfe/trunk/unittests/AST/<wbr>StructuralEquivalenceTest.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/AST/StructuralEquivalenceTest.cpp?rev=336776&r1=336775&r2=336776&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-<wbr>project/cfe/trunk/unittests/<wbr>AST/StructuralEquivalenceTest.<wbr>cpp?rev=336776&r1=336775&r2=<wbr>336776&view=diff</a><br>
==============================<wbr>==============================<wbr>==================<br>
--- cfe/trunk/unittests/AST/<wbr>StructuralEquivalenceTest.cpp (original)<br>
+++ cfe/trunk/unittests/AST/<wbr>StructuralEquivalenceTest.cpp Wed Jul 11 02:37:24 2018<br>
@@ -18,13 +18,13 @@ struct StructuralEquivalenceTest : ::tes<br>
   std::unique_ptr<ASTUnit> AST0, AST1;<br>
   std::string Code0, Code1; // Buffers for SourceManager<br>
<br>
-  // Get a pair of Decl pointers to the synthetised declarations from the given<br>
-  // code snipets. By default we search for the unique Decl with name 'foo' in<br>
-  // both snippets.<br>
-  std::tuple<NamedDecl *, NamedDecl *><br>
-  makeNamedDecls(const std::string &SrcCode0, const std::string &SrcCode1,<br>
-                 Language Lang, const char *const Identifier = "foo") {<br>
-<br>
+  // Get a pair of node pointers into the synthesized AST from the given code<br>
+  // snippets. To determine the returned node, a separate matcher is specified<br>
+  // for both snippets. The first matching node is returned.<br>
+  template <typename NodeType, typename MatcherType><br>
+  std::tuple<NodeType *, NodeType *> makeDecls(<br>
+      const std::string &SrcCode0, const std::string &SrcCode1, Language Lang,<br>
+      const MatcherType &Matcher0, const MatcherType &Matcher1) {<br>
     this->Code0 = SrcCode0;<br>
     this->Code1 = SrcCode1;<br>
     ArgVector Args = getBasicRunOptionsForLanguage(<wbr>Lang);<br>
@@ -34,30 +34,34 @@ struct StructuralEquivalenceTest : ::tes<br>
     AST0 = tooling::<wbr>buildASTFromCodeWithArgs(<wbr>Code0, Args, InputFileName);<br>
     AST1 = tooling::<wbr>buildASTFromCodeWithArgs(<wbr>Code1, Args, InputFileName);<br>
<br>
-    ASTContext &Ctx0 = AST0->getASTContext(), &Ctx1 = AST1->getASTContext();<br>
+    NodeType *D0 = FirstDeclMatcher<NodeType>().<wbr>match(<br>
+        AST0->getASTContext().<wbr>getTranslationUnitDecl(), Matcher0);<br>
+    NodeType *D1 = FirstDeclMatcher<NodeType>().<wbr>match(<br>
+        AST1->getASTContext().<wbr>getTranslationUnitDecl(), Matcher1);<br>
<br>
-    auto getDecl = [](ASTContext &Ctx, const std::string &Name) -> NamedDecl * {<br>
-      IdentifierInfo *SearchedII = &Ctx.Idents.get(Name);<br>
-      assert(SearchedII && "Declaration with the identifier "<br>
-                           "should be specified in test!");<br>
-      DeclarationName SearchDeclName(SearchedII);<br>
-      SmallVector<NamedDecl *, 4> FoundDecls;<br>
-      Ctx.getTranslationUnitDecl()-><wbr>localUncachedLookup(<wbr>SearchDeclName,<br>
-                                                        FoundDecls);<br>
-<br>
-      // We should find one Decl but one only.<br>
-      assert(FoundDecls.size() == 1);<br>
-<br>
-      return FoundDecls[0];<br>
-    };<br>
-<br>
-    NamedDecl *D0 = getDecl(Ctx0, Identifier);<br>
-    NamedDecl *D1 = getDecl(Ctx1, Identifier);<br>
-    assert(D0);<br>
-    assert(D1);<br>
     return std::make_tuple(D0, D1);<br>
   }<br>
<br>
+  // Get a pair of node pointers into the synthesized AST from the given code<br>
+  // snippets. The same matcher is used for both snippets.<br>
+  template <typename NodeType, typename MatcherType><br>
+  std::tuple<NodeType *, NodeType *> makeDecls(<br>
+      const std::string &SrcCode0, const std::string &SrcCode1, Language Lang,<br>
+      const MatcherType &AMatcher) {<br>
+    return makeDecls<NodeType, MatcherType>(<br>
+          SrcCode0, SrcCode1, Lang, AMatcher, AMatcher);<br>
+  }<br>
+<br>
+  // Get a pair of Decl pointers to the synthesized declarations from the given<br>
+  // code snippets. We search for the first NamedDecl with given name in both<br>
+  // snippets.<br>
+  std::tuple<NamedDecl *, NamedDecl *> makeNamedDecls(<br>
+      const std::string &SrcCode0, const std::string &SrcCode1,<br>
+      Language Lang, const char *const Identifier = "foo") {<br>
+    auto Matcher = namedDecl(hasName(Identifier))<wbr>;<br>
+    return makeDecls<NamedDecl>(SrcCode0, SrcCode1, Lang, Matcher);<br>
+  }<br>
+<br>
   bool testStructuralMatch(NamedDecl *D0, NamedDecl *D1) {<br>
     llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls;<br>
     StructuralEquivalenceContext Ctx(D0->getASTContext(), D1->getASTContext(),<br>
@@ -110,35 +114,29 @@ TEST_F(<wbr>StructuralEquivalenceTest, CharVs<br>
 }<br>
<br>
 TEST_F(<wbr>StructuralEquivalenceTest, IntVsSignedIntTemplateSpec) {<br>
-  auto Decls = makeNamedDecls(<br>
-      "template <class T> struct foo; template<> struct foo<int>{};",<br>
-      "template <class T> struct foo; template<> struct foo<signed int>{};",<br>
-      Lang_CXX);<br>
-  ClassTemplateSpecializationDec<wbr>l *Spec0 =<br>
-      *cast<ClassTemplateDecl>(get<<wbr>0>(Decls))->spec_begin();<br>
-  ClassTemplateSpecializationDec<wbr>l *Spec1 =<br>
-      *cast<ClassTemplateDecl>(get<<wbr>1>(Decls))->spec_begin();<br>
-  ASSERT_TRUE(Spec0 != nullptr);<br>
-  ASSERT_TRUE(Spec1 != nullptr);<br>
+  auto Decls = makeDecls<<wbr>ClassTemplateSpecializationDec<wbr>l>(<br>
+      R"(template <class T> struct foo; template<> struct foo<int>{};)",<br>
+      R"(template <class T> struct foo; template<> struct foo<signed int>{};)",<br>
+      Lang_CXX,<br>
+      classTemplateSpecializationDec<wbr>l());<br>
+  auto Spec0 = get<0>(Decls);<br>
+  auto Spec1 = get<1>(Decls);<br>
   EXPECT_TRUE(<wbr>testStructuralMatch(Spec0, Spec1));<br>
 }<br>
<br>
 TEST_F(<wbr>StructuralEquivalenceTest, CharVsSignedCharTemplateSpec) {<br>
-  auto Decls = makeNamedDecls(<br>
-      "template <class T> struct foo; template<> struct foo<char>{};",<br>
-      "template <class T> struct foo; template<> struct foo<signed char>{};",<br>
-      Lang_CXX);<br>
-  ClassTemplateSpecializationDec<wbr>l *Spec0 =<br>
-      *cast<ClassTemplateDecl>(get<<wbr>0>(Decls))->spec_begin();<br>
-  ClassTemplateSpecializationDec<wbr>l *Spec1 =<br>
-      *cast<ClassTemplateDecl>(get<<wbr>1>(Decls))->spec_begin();<br>
-  ASSERT_TRUE(Spec0 != nullptr);<br>
-  ASSERT_TRUE(Spec1 != nullptr);<br>
+  auto Decls = makeDecls<<wbr>ClassTemplateSpecializationDec<wbr>l>(<br>
+      R"(template <class T> struct foo; template<> struct foo<char>{};)",<br>
+      R"(template <class T> struct foo; template<> struct foo<signed char>{};)",<br>
+      Lang_CXX,<br>
+      classTemplateSpecializationDec<wbr>l());<br>
+  auto Spec0 = get<0>(Decls);<br>
+  auto Spec1 = get<1>(Decls);<br>
   EXPECT_FALSE(<wbr>testStructuralMatch(Spec0, Spec1));<br>
 }<br>
<br>
 TEST_F(<wbr>StructuralEquivalenceTest, CharVsSignedCharTemplateSpecWi<wbr>thInheritance) {<br>
-  auto Decls = makeNamedDecls(<br>
+  auto Decls = makeDecls<<wbr>ClassTemplateSpecializationDec<wbr>l>(<br>
       R"(<br>
       struct true_type{};<br>
       template <class T> struct foo;<br>
@@ -149,14 +147,9 @@ TEST_F(<wbr>StructuralEquivalenceTest, CharVs<br>
       template <class T> struct foo;<br>
       template<> struct foo<signed char> : true_type {};<br>
       )",<br>
-      Lang_CXX);<br>
-  ClassTemplateSpecializationDec<wbr>l *Spec0 =<br>
-      *cast<ClassTemplateDecl>(get<<wbr>0>(Decls))->spec_begin();<br>
-  ClassTemplateSpecializationDec<wbr>l *Spec1 =<br>
-      *cast<ClassTemplateDecl>(get<<wbr>1>(Decls))->spec_begin();<br>
-  ASSERT_TRUE(Spec0 != nullptr);<br>
-  ASSERT_TRUE(Spec1 != nullptr);<br>
-  EXPECT_FALSE(<wbr>testStructuralMatch(Spec0, Spec1));<br>
+      Lang_CXX,<br>
+      classTemplateSpecializationDec<wbr>l());<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(Decls));<br>
 }<br>
<br>
 // This test is disabled for now.<br>
@@ -203,5 +196,350 @@ TEST_F(<wbr>StructuralEquivalenceTest, WrongO<br>
   EXPECT_FALSE(<wbr>testStructuralMatch(Decls));<br>
 }<br>
<br>
+struct StructuralEquivalenceFunctionT<wbr>est : StructuralEquivalenceTest {<br>
+};<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceFunctionT<wbr>est, ParamConstWithRef) {<br>
+  auto t = makeNamedDecls("void foo(int&);",<br>
+                          "void foo(const int&);", Lang_CXX);<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceFunctionT<wbr>est, ParamConstSimple) {<br>
+  auto t = makeNamedDecls("void foo(int);",<br>
+                          "void foo(const int);", Lang_CXX);<br>
+  EXPECT_TRUE(<wbr>testStructuralMatch(t));<br>
+  // consider this OK<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceFunctionT<wbr>est, Throw) {<br>
+  auto t = makeNamedDecls("void foo();",<br>
+                          "void foo() throw();", Lang_CXX);<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceFunctionT<wbr>est, Noexcept) {<br>
+  auto t = makeNamedDecls("void foo();",<br>
+                          "void foo() noexcept;", Lang_CXX11);<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceFunctionT<wbr>est, ThrowVsNoexcept) {<br>
+  auto t = makeNamedDecls("void foo() throw();",<br>
+                          "void foo() noexcept;", Lang_CXX11);<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceFunctionT<wbr>est, ThrowVsNoexceptFalse) {<br>
+  auto t = makeNamedDecls("void foo() throw();",<br>
+                          "void foo() noexcept(false);", Lang_CXX11);<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceFunctionT<wbr>est, ThrowVsNoexceptTrue) {<br>
+  auto t = makeNamedDecls("void foo() throw();",<br>
+                          "void foo() noexcept(true);", Lang_CXX11);<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceFunctionT<wbr>est, DISABLED_NoexceptNonMatch) {<br>
+  // The expression is not checked yet.<br>
+  auto t = makeNamedDecls("void foo() noexcept(false);",<br>
+                          "void foo() noexcept(true);", Lang_CXX11);<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceFunctionT<wbr>est, NoexceptMatch) {<br>
+  auto t = makeNamedDecls("void foo() noexcept(false);",<br>
+                          "void foo() noexcept(false);", Lang_CXX11);<br>
+  EXPECT_TRUE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceFunctionT<wbr>est, NoexceptVsNoexceptFalse) {<br>
+  auto t = makeNamedDecls("void foo() noexcept;",<br>
+                          "void foo() noexcept(false);", Lang_CXX11);<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceFunctionT<wbr>est, NoexceptVsNoexceptTrue) {<br>
+  auto t = makeNamedDecls("void foo() noexcept;",<br>
+                          "void foo() noexcept(true);", Lang_CXX11);<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceFunctionT<wbr>est, ReturnType) {<br>
+  auto t = makeNamedDecls("char foo();",<br>
+                          "int foo();", Lang_CXX);<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceFunctionT<wbr>est, ReturnConst) {<br>
+  auto t = makeNamedDecls("char foo();",<br>
+                          "const char foo();", Lang_CXX);<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceFunctionT<wbr>est, ReturnRef) {<br>
+  auto t = makeNamedDecls("char &foo();",<br>
+                          "char &&foo();", Lang_CXX11);<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceFunctionT<wbr>est, ParamCount) {<br>
+  auto t = makeNamedDecls("void foo(int);",<br>
+                          "void foo(int, int);", Lang_CXX);<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceFunctionT<wbr>est, ParamType) {<br>
+  auto t = makeNamedDecls("void foo(int);",<br>
+                          "void foo(char);", Lang_CXX);<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceFunctionT<wbr>est, ParamName) {<br>
+  auto t = makeNamedDecls("void foo(int a);",<br>
+                          "void foo(int b);", Lang_CXX);<br>
+  EXPECT_TRUE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceFunctionT<wbr>est, Variadic) {<br>
+  auto t = makeNamedDecls("void foo(int x...);",<br>
+                          "void foo(int x);", Lang_CXX);<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceFunctionT<wbr>est, ParamPtr) {<br>
+  auto t = makeNamedDecls("void foo(int *);",<br>
+                          "void foo(int);", Lang_CXX);<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceFunctionT<wbr>est, NameInParen) {<br>
+  auto t = makeNamedDecls(<br>
+      "void ((foo))();",<br>
+      "void foo();",<br>
+      Lang_CXX);<br>
+  EXPECT_TRUE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceFunctionT<wbr>est, NameInParenWithExceptionSpec) {<br>
+  auto t = makeNamedDecls(<br>
+      "void (foo)() throw(int);",<br>
+      "void (foo)() noexcept;",<br>
+      Lang_CXX11);<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceFunctionT<wbr>est, NameInParenWithConst) {<br>
+  auto t = makeNamedDecls(<br>
+      "struct A { void (foo)() const; };",<br>
+      "struct A { void (foo)(); };",<br>
+      Lang_CXX11);<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+struct StructuralEquivalenceCXXMethod<wbr>Test : StructuralEquivalenceTest {<br>
+};<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceCXXMethod<wbr>Test, Virtual) {<br>
+  auto t = makeDecls<CXXMethodDecl>(<br>
+      "struct X { void foo(); };",<br>
+      "struct X { virtual void foo(); };", Lang_CXX,<br>
+      cxxMethodDecl(hasName("foo")))<wbr>;<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceCXXMethod<wbr>Test, Pure) {<br>
+  auto t = makeNamedDecls("struct X { virtual void foo(); };",<br>
+                          "struct X { virtual void foo() = 0; };", Lang_CXX);<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceCXXMethod<wbr>Test, DISABLED_Final) {<br>
+  // The final-ness is not checked yet.<br>
+  auto t = makeNamedDecls("struct X { virtual void foo(); };",<br>
+                          "struct X { virtual void foo() final; };", Lang_CXX);<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceCXXMethod<wbr>Test, Const) {<br>
+  auto t = makeNamedDecls("struct X { void foo(); };",<br>
+                          "struct X { void foo() const; };", Lang_CXX);<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceCXXMethod<wbr>Test, Static) {<br>
+  auto t = makeNamedDecls("struct X { void foo(); };",<br>
+                          "struct X { static void foo(); };", Lang_CXX);<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceCXXMethod<wbr>Test, Ref1) {<br>
+  auto t = makeNamedDecls("struct X { void foo(); };",<br>
+                          "struct X { void foo() &&; };", Lang_CXX11);<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceCXXMethod<wbr>Test, Ref2) {<br>
+  auto t = makeNamedDecls("struct X { void foo() &; };",<br>
+                          "struct X { void foo() &&; };", Lang_CXX11);<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceCXXMethod<wbr>Test, AccessSpecifier) {<br>
+  auto t = makeDecls<CXXMethodDecl>(<br>
+      "struct X { public: void foo(); };",<br>
+      "struct X { private: void foo(); };", Lang_CXX,<br>
+      cxxMethodDecl(hasName("foo")))<wbr>;<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceCXXMethod<wbr>Test, Delete) {<br>
+  auto t = makeNamedDecls("struct X { void foo(); };",<br>
+                          "struct X { void foo() = delete; };", Lang_CXX11);<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceCXXMethod<wbr>Test, Constructor) {<br>
+  auto t = makeDecls<FunctionDecl>(<br>
+      "void foo();", "struct foo { foo(); };", Lang_CXX,<br>
+      functionDecl(), cxxConstructorDecl());<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceCXXMethod<wbr>Test, ConstructorParam) {<br>
+  auto t = makeDecls<CXXConstructorDecl>(<wbr>"struct X { X(); };",<br>
+                                         "struct X { X(int); };", Lang_CXX,<br>
+                                         cxxConstructorDecl());<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceCXXMethod<wbr>Test, ConstructorExplicit) {<br>
+  auto t = makeDecls<CXXConstructorDecl>(<wbr>"struct X { X(int); };",<br>
+                                         "struct X { explicit X(int); };",<br>
+                                         Lang_CXX11,<br>
+                                         cxxConstructorDecl());<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceCXXMethod<wbr>Test, ConstructorDefault) {<br>
+  auto t = makeDecls<CXXConstructorDecl>(<wbr>"struct X { X(); };",<br>
+                                         "struct X { X() = default; };",<br>
+                                         Lang_CXX11,<br>
+                                         cxxConstructorDecl());<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceCXXMethod<wbr>Test, Conversion) {<br>
+  auto t = makeDecls<CXXConversionDecl>("<wbr>struct X { operator bool(); };",<br>
+                                        "struct X { operator char(); };",<br>
+                                         Lang_CXX11,<br>
+                                         cxxConversionDecl());<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceCXXMethod<wbr>Test, Operator) {<br>
+  auto t = makeDecls<FunctionDecl>(<br>
+      "struct X { int operator +(int); };",<br>
+      "struct X { int operator -(int); };", Lang_CXX,<br>
+      functionDecl(<wbr>hasOverloadedOperatorName("+")<wbr>),<br>
+      functionDecl(<wbr>hasOverloadedOperatorName("-")<wbr>));<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceCXXMethod<wbr>Test, OutOfClass1) {<br>
+  auto t = makeDecls<FunctionDecl>(<br>
+      "struct X { virtual void f(); }; void X::f() { }",<br>
+      "struct X { virtual void f() { }; };",<br>
+      Lang_CXX,<br>
+      functionDecl(allOf(hasName("f"<wbr>), isDefinition())));<br>
+  EXPECT_TRUE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceCXXMethod<wbr>Test, OutOfClass2) {<br>
+  auto t = makeDecls<FunctionDecl>(<br>
+      "struct X { virtual void f(); }; void X::f() { }",<br>
+      "struct X { void f(); }; void X::f() { }",<br>
+      Lang_CXX,<br>
+      functionDecl(allOf(hasName("f"<wbr>), isDefinition())));<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+struct StructuralEquivalenceRecordTes<wbr>t : StructuralEquivalenceTest {<br>
+};<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceRecordTes<wbr>t, Name) {<br>
+  auto t = makeDecls<CXXRecordDecl>(<br>
+      "struct A{ };",<br>
+      "struct B{ };",<br>
+      Lang_CXX,<br>
+      cxxRecordDecl());<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceRecordTes<wbr>t, Fields) {<br>
+  auto t = makeNamedDecls(<br>
+      "struct foo{ int x; };",<br>
+      "struct foo{ char x; };",<br>
+      Lang_CXX);<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceRecordTes<wbr>t, DISABLED_Methods) {<br>
+  // Currently, methods of a class are not checked at class equivalence.<br>
+  auto t = makeNamedDecls(<br>
+      "struct foo{ int x(); };",<br>
+      "struct foo{ char x(); };",<br>
+      Lang_CXX);<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceRecordTes<wbr>t, Bases) {<br>
+  auto t = makeNamedDecls(<br>
+      "struct A{ }; struct foo: A { };",<br>
+      "struct B{ }; struct foo: B { };",<br>
+      Lang_CXX);<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceRecordTes<wbr>t, InheritanceVirtual) {<br>
+  auto t = makeNamedDecls(<br>
+      "struct A{ }; struct foo: A { };",<br>
+      "struct A{ }; struct foo: virtual A { };",<br>
+      Lang_CXX);<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceRecordTes<wbr>t, DISABLED_InheritanceType) {<br>
+  // Access specifier in inheritance is not checked yet.<br>
+  auto t = makeNamedDecls(<br>
+      "struct A{ }; struct foo: public A { };",<br>
+      "struct A{ }; struct foo: private A { };",<br>
+      Lang_CXX);<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceRecordTes<wbr>t, Match) {<br>
+  auto Code = R"(<br>
+      struct A{ };<br>
+      struct B{ };<br>
+      struct foo: A, virtual B {<br>
+        void x();<br>
+        int a;<br>
+      };<br>
+      )";<br>
+  auto t = makeNamedDecls(Code, Code, Lang_CXX);<br>
+  EXPECT_TRUE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
+TEST_F(<wbr>StructuralEquivalenceTest, CompareSameDeclWithMultiple) {<br>
+  auto t = makeNamedDecls(<br>
+      "struct A{ }; struct B{ }; void foo(A a, A b);",<br>
+      "struct A{ }; struct B{ }; void foo(A a, B b);",<br>
+      Lang_CXX);<br>
+  EXPECT_FALSE(<wbr>testStructuralMatch(t));<br>
+}<br>
+<br>
 } // end namespace ast_matchers<br>
 } // end namespace clang<br>
<br>
<br>
______________________________<wbr>_________________<br>
cfe-commits mailing list<br>
<a href="mailto:cfe-commits@lists.llvm.org">cfe-commits@lists.llvm.org</a><br>
<a href="http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits" rel="noreferrer" target="_blank">http://lists.llvm.org/cgi-bin/<wbr>mailman/listinfo/cfe-commits</a><br>
</blockquote></div><br></div></div>