[AST] Structural equivalence of methods

Summary:
Added structural equivalence check for C++ methods.
Improved structural equivalence tests.
Added related ASTImporter tests.

Reviewers: a.sidorin, szepet, xazax.hun, martong, a_sidorin

Reviewed By: martong, a_sidorin

Subscribers: a_sidorin, rnkovacs, cfe-commits

Differential Revision: https://reviews.llvm.org/D48628

llvm-svn: 336776
diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp
index b543991..367755f 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -250,6 +250,9 @@
   if (T1.isNull() || T2.isNull())
     return T1.isNull() && T2.isNull();
 
+  QualType OrigT1 = T1;
+  QualType OrigT2 = T2;
+
   if (!Context.StrictTypeSpelling) {
     // We aren't being strict about token-to-token equivalence of types,
     // so map down to the canonical type.
@@ -422,6 +425,7 @@
   case Type::FunctionProto: {
     const auto *Proto1 = cast<FunctionProtoType>(T1);
     const auto *Proto2 = cast<FunctionProtoType>(T2);
+
     if (Proto1->getNumParams() != Proto2->getNumParams())
       return false;
     for (unsigned I = 0, N = Proto1->getNumParams(); I != N; ++I) {
@@ -431,23 +435,33 @@
     }
     if (Proto1->isVariadic() != Proto2->isVariadic())
       return false;
-    if (Proto1->getExceptionSpecType() != Proto2->getExceptionSpecType())
-      return false;
-    if (Proto1->getExceptionSpecType() == EST_Dynamic) {
-      if (Proto1->getNumExceptions() != Proto2->getNumExceptions())
-        return false;
-      for (unsigned I = 0, N = Proto1->getNumExceptions(); I != N; ++I) {
-        if (!IsStructurallyEquivalent(Context, Proto1->getExceptionType(I),
-                                      Proto2->getExceptionType(I)))
-          return false;
-      }
-    } else if (isComputedNoexcept(Proto1->getExceptionSpecType())) {
-      if (!IsStructurallyEquivalent(Context, Proto1->getNoexceptExpr(),
-                                    Proto2->getNoexceptExpr()))
-        return false;
-    }
+
     if (Proto1->getTypeQuals() != Proto2->getTypeQuals())
       return false;
+    
+    // Check exceptions, this information is lost in canonical type.
+    const auto *OrigProto1 =
+        cast<FunctionProtoType>(OrigT1.getDesugaredType(Context.FromCtx));
+    const auto *OrigProto2 =
+        cast<FunctionProtoType>(OrigT2.getDesugaredType(Context.ToCtx));
+    auto Spec1 = OrigProto1->getExceptionSpecType();
+    auto Spec2 = OrigProto2->getExceptionSpecType();
+    
+    if (Spec1 != Spec2)
+      return false;
+    if (Spec1 == EST_Dynamic) {
+      if (OrigProto1->getNumExceptions() != OrigProto2->getNumExceptions())
+        return false;
+      for (unsigned I = 0, N = OrigProto1->getNumExceptions(); I != N; ++I) {
+        if (!IsStructurallyEquivalent(Context, OrigProto1->getExceptionType(I),
+                                      OrigProto2->getExceptionType(I)))
+          return false;
+      }
+    } else if (isComputedNoexcept(Spec1)) {
+      if (!IsStructurallyEquivalent(Context, OrigProto1->getNoexceptExpr(),
+                                    OrigProto2->getNoexceptExpr()))
+        return false;
+    }
 
     // Fall through to check the bits common with FunctionNoProtoType.
     LLVM_FALLTHROUGH;
@@ -830,6 +844,56 @@
   return true;
 }
 
+/// Determine structural equivalence of two methodss.
+static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
+                                     CXXMethodDecl *Method1,
+                                     CXXMethodDecl *Method2) {
+  bool PropertiesEqual =
+      Method1->getDeclKind() == Method2->getDeclKind() &&
+      Method1->getRefQualifier() == Method2->getRefQualifier() &&
+      Method1->getAccess() == Method2->getAccess() &&
+      Method1->getOverloadedOperator() == Method2->getOverloadedOperator() &&
+      Method1->isStatic() == Method2->isStatic() &&
+      Method1->isConst() == Method2->isConst() &&
+      Method1->isVolatile() == Method2->isVolatile() &&
+      Method1->isVirtual() == Method2->isVirtual() &&
+      Method1->isPure() == Method2->isPure() &&
+      Method1->isDefaulted() == Method2->isDefaulted() &&
+      Method1->isDeleted() == Method2->isDeleted();
+  if (!PropertiesEqual)
+    return false;
+  // FIXME: Check for 'final'.
+
+  if (auto *Constructor1 = dyn_cast<CXXConstructorDecl>(Method1)) {
+    auto *Constructor2 = cast<CXXConstructorDecl>(Method2);
+    if (Constructor1->isExplicit() != Constructor2->isExplicit())
+      return false;
+  }
+
+  if (auto *Conversion1 = dyn_cast<CXXConversionDecl>(Method1)) {
+    auto *Conversion2 = cast<CXXConversionDecl>(Method2);
+    if (Conversion1->isExplicit() != Conversion2->isExplicit())
+      return false;
+    if (!IsStructurallyEquivalent(Context, Conversion1->getConversionType(),
+                                  Conversion2->getConversionType()))
+      return false;
+  }
+
+  const IdentifierInfo *Name1 = Method1->getIdentifier();
+  const IdentifierInfo *Name2 = Method2->getIdentifier();
+  if (!::IsStructurallyEquivalent(Name1, Name2)) {
+    return false;
+    // TODO: Names do not match, add warning like at check for FieldDecl.
+  }
+
+  // Check the prototypes.
+  if (!::IsStructurallyEquivalent(Context,
+                                  Method1->getType(), Method2->getType()))
+    return false;
+
+  return true;
+}
+
 /// Determine structural equivalence of two records.
 static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
                                      RecordDecl *D1, RecordDecl *D2) {
@@ -1445,6 +1509,14 @@
         // Kind mismatch.
         Equivalent = false;
       }
+    } else if (auto *MD1 = dyn_cast<CXXMethodDecl>(D1)) {
+      if (auto *MD2 = dyn_cast<CXXMethodDecl>(D2)) {
+        if (!::IsStructurallyEquivalent(*this, MD1, MD2))
+          Equivalent = false;
+      } else {
+        // Kind mismatch.
+        Equivalent = false;
+      }
     } else if (FunctionDecl *FD1 = dyn_cast<FunctionDecl>(D1)) {
       if (FunctionDecl *FD2 = dyn_cast<FunctionDecl>(D2)) {
         if (!::IsStructurallyEquivalent(FD1->getIdentifier(),