diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -277,6 +277,17 @@ bool IsStmtEquivalent(const Stmt *S1, const Stmt *S2) { return true; } + bool IsStmtEquivalent(const GotoStmt *S1, const GotoStmt *S2) { + auto *L1 = S1->getLabel(); + auto *L2 = S2->getLabel(); + if (!L1 || !L2) { + return L1 == L2; + } + IdentifierInfo *Name1 = L1->getIdentifier(); + IdentifierInfo *Name2 = L2->getIdentifier(); + return ::IsStructurallyEquivalent(Name1, Name2); + } + bool IsStmtEquivalent(const SourceLocExpr *E1, const SourceLocExpr *E2) { return E1->getIdentKind() == E2->getIdentKind(); } @@ -1295,6 +1306,24 @@ return true; } +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + VarDecl *D1, VarDecl *D2) { + if (D1->getStorageClass() != D2->getStorageClass()) { + return false; + } + IdentifierInfo *Name1 = D1->getIdentifier(); + IdentifierInfo *Name2 = D2->getIdentifier(); + if (!::IsStructurallyEquivalent(Name1, Name2)) { + return false; + } + + if (!IsStructurallyEquivalent(Context, D1->getType(), D2->getType())) { + return false; + } + + return IsStructurallyEquivalent(Context, D1->getInit(), D2->getInit()); +} + static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, FieldDecl *Field1, FieldDecl *Field2, QualType Owner2Type) { diff --git a/clang/unittests/AST/StructuralEquivalenceTest.cpp b/clang/unittests/AST/StructuralEquivalenceTest.cpp --- a/clang/unittests/AST/StructuralEquivalenceTest.cpp +++ b/clang/unittests/AST/StructuralEquivalenceTest.cpp @@ -1,5 +1,6 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/ASTStructuralEquivalence.h" +#include "clang/AST/Decl.h" #include "clang/AST/DeclTemplate.h" #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/Frontend/ASTUnit.h" @@ -1801,10 +1802,10 @@ TEST_F(StructuralEquivalenceCacheTest, ReturnStmtNonEq) { auto TU = makeTuDecls( R"( - bool x(){ return true; } + bool x() { return true; } )", R"( - bool x(){ return false; } + bool x() { return false; } )", Lang_CXX03); @@ -1817,6 +1818,60 @@ } +TEST_F(StructuralEquivalenceCacheTest, VarDeclNoEq) { + auto TU = makeTuDecls( + R"( + int p; + )", + R"( + int q; + )", + Lang_CXX03); + + StructuralEquivalenceContext Ctx( + get<0>(TU)->getASTContext(), get<1>(TU)->getASTContext(), + NonEquivalentDecls, StructuralEquivalenceKind::Default, false, false); + + auto X = findDeclPair(TU, varDecl()); + EXPECT_FALSE(Ctx.IsEquivalent(X.first, X.second)); +} + +TEST_F(StructuralEquivalenceCacheTest, VarDeclWithDifferentStorageClassNoEq) { + auto TU = makeTuDecls( + R"( + int p; + )", + R"( + static int p; + )", + Lang_CXX03); + + StructuralEquivalenceContext Ctx( + get<0>(TU)->getASTContext(), get<1>(TU)->getASTContext(), + NonEquivalentDecls, StructuralEquivalenceKind::Default, false, false); + + auto X = findDeclPair(TU, varDecl()); + EXPECT_FALSE(Ctx.IsEquivalent(X.first, X.second)); +} + +TEST_F(StructuralEquivalenceCacheTest, VarDeclWithInitNoEq) { + auto TU = makeTuDecls( + R"( + int p = 1; + )", + R"( + int p = 2; + )", + Lang_CXX03); + + StructuralEquivalenceContext Ctx( + get<0>(TU)->getASTContext(), get<1>(TU)->getASTContext(), + NonEquivalentDecls, StructuralEquivalenceKind::Default, false, false); + + auto X = findDeclPair(TU, varDecl()); + EXPECT_FALSE(Ctx.IsEquivalent(X.first, X.second)); +} + TEST_F(StructuralEquivalenceCacheTest, SpecialNonEq) { auto TU = makeTuDecls( R"( @@ -2320,5 +2375,23 @@ EXPECT_TRUE(testStructuralMatch(t)); } +TEST_F(StructuralEquivalenceCacheTest, GotoStmtNoEq) { + auto t = makeStmts( + R"( + void foo() { + goto L1; + L1: foo(); + } + )", + R"( + void foo() { + goto L2; + L2: foo(); + } + )", + Lang_CXX03, gotoStmt()); + EXPECT_FALSE(testStructuralMatch(t)); +} + } // end namespace ast_matchers } // end namespace clang