Index: unittests/AST/ASTImporterTest.cpp =================================================================== --- unittests/AST/ASTImporterTest.cpp +++ unittests/AST/ASTImporterTest.cpp @@ -19,6 +19,7 @@ #include "clang/Tooling/Tooling.h" #include "DeclMatcher.h" +#include "Language.h" #include "gtest/gtest.h" #include "llvm/ADT/StringMap.h" @@ -29,50 +30,6 @@ using internal::BindableMatcher; using llvm::StringMap; -typedef std::vector ArgVector; -typedef std::vector RunOptions; - -static bool isCXX(Language Lang) { - return Lang == Lang_CXX || Lang == Lang_CXX11; -} - -static ArgVector getBasicRunOptionsForLanguage(Language Lang) { - ArgVector BasicArgs; - // Test with basic arguments. - switch (Lang) { - case Lang_C: - BasicArgs = {"-x", "c", "-std=c99"}; - break; - case Lang_C89: - BasicArgs = {"-x", "c", "-std=c89"}; - break; - case Lang_CXX: - BasicArgs = {"-std=c++98", "-frtti"}; - break; - case Lang_CXX11: - BasicArgs = {"-std=c++11", "-frtti"}; - break; - case Lang_OpenCL: - case Lang_OBJCXX: - llvm_unreachable("Not implemented yet!"); - } - return BasicArgs; -} - -static RunOptions getRunOptionsForLanguage(Language Lang) { - ArgVector BasicArgs = getBasicRunOptionsForLanguage(Lang); - - // For C++, test with "-fdelayed-template-parsing" enabled to handle MSVC - // default behaviour. - if (isCXX(Lang)) { - ArgVector ArgsForDelayedTemplateParse = BasicArgs; - ArgsForDelayedTemplateParse.emplace_back("-fdelayed-template-parsing"); - return {BasicArgs, ArgsForDelayedTemplateParse}; - } - - return {BasicArgs}; -} - // Creates a virtual file and assigns that to the context of given AST. If the // file already exists then the file will not be created again as a duplicate. static void Index: unittests/AST/CMakeLists.txt =================================================================== --- unittests/AST/CMakeLists.txt +++ unittests/AST/CMakeLists.txt @@ -15,9 +15,11 @@ DeclTest.cpp EvaluateAsRValueTest.cpp ExternalASTSourceTest.cpp + Language.cpp NamedDeclPrinterTest.cpp SourceLocationTest.cpp StmtPrinterTest.cpp + StructuralEquivalenceTest.cpp ) target_link_libraries(ASTTests Index: unittests/AST/Language.h =================================================================== --- unittests/AST/Language.h +++ unittests/AST/Language.h @@ -0,0 +1,47 @@ +//===------ unittest/AST/Language.h - AST unit test support ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines language options for AST unittests. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_UNITTESTS_AST_LANGUAGE_H +#define LLVM_CLANG_UNITTESTS_AST_LANGUAGE_H + +#include "llvm/Support/ErrorHandling.h" +#include +#include + +namespace clang { +namespace ast_matchers { + +typedef std::vector ArgVector; +typedef std::vector RunOptions; + +enum Language { + Lang_C, + Lang_C89, + Lang_CXX, + Lang_CXX11, + Lang_CXX14, + Lang_OpenCL, + Lang_OBJCXX +}; + +inline bool isCXX(Language Lang) { + return Lang == Lang_CXX || Lang == Lang_CXX11 || Lang == Lang_CXX14; +} + +ArgVector getBasicRunOptionsForLanguage(Language Lang); +RunOptions getRunOptionsForLanguage(Language Lang); + +} // end namespace ast_matchers +} // end namespace clang + +#endif Index: unittests/AST/Language.cpp =================================================================== --- unittests/AST/Language.cpp +++ unittests/AST/Language.cpp @@ -0,0 +1,60 @@ +//===------ unittest/AST/Language.cpp - AST unit test support -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines language options for AST unittests. +// +//===----------------------------------------------------------------------===// + +#include "Language.h" + +namespace clang { +namespace ast_matchers { + +ArgVector getBasicRunOptionsForLanguage(Language Lang) { + ArgVector BasicArgs; + // Test with basic arguments. + switch (Lang) { + case Lang_C: + BasicArgs = {"-x", "c", "-std=c99"}; + break; + case Lang_C89: + BasicArgs = {"-x", "c", "-std=c89"}; + break; + case Lang_CXX: + BasicArgs = {"-std=c++98", "-frtti"}; + break; + case Lang_CXX11: + BasicArgs = {"-std=c++11", "-frtti"}; + break; + case Lang_CXX14: + BasicArgs = {"-std=c++14", "-frtti"}; + break; + case Lang_OpenCL: + case Lang_OBJCXX: + llvm_unreachable("Not implemented yet!"); + } + return BasicArgs; +} + +RunOptions getRunOptionsForLanguage(Language Lang) { + ArgVector BasicArgs = getBasicRunOptionsForLanguage(Lang); + + // For C++, test with "-fdelayed-template-parsing" enabled to handle MSVC + // default behaviour. + if (isCXX(Lang)) { + ArgVector ArgsForDelayedTemplateParse = BasicArgs; + ArgsForDelayedTemplateParse.emplace_back("-fdelayed-template-parsing"); + return {BasicArgs, ArgsForDelayedTemplateParse}; + } + + return {BasicArgs}; +} + +} // end namespace ast_matchers +} // end namespace clang Index: unittests/AST/MatchVerifier.h =================================================================== --- unittests/AST/MatchVerifier.h +++ unittests/AST/MatchVerifier.h @@ -23,20 +23,12 @@ #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/Tooling/Tooling.h" +#include "Language.h" #include "gtest/gtest.h" namespace clang { namespace ast_matchers { -enum Language { - Lang_C, - Lang_C89, - Lang_CXX, - Lang_CXX11, - Lang_OpenCL, - Lang_OBJCXX -}; - /// \brief Base class for verifying some property of nodes found by a matcher. template class MatchVerifier : public MatchFinder::MatchCallback { @@ -113,6 +105,10 @@ Args.push_back("-std=c++11"); FileName = "input.cc"; break; + case Lang_CXX14: + Args.push_back("-std=c++14"); + FileName = "input.cc"; + break; case Lang_OpenCL: FileName = "input.cl"; break; Index: unittests/AST/StructuralEquivalenceTest.cpp =================================================================== --- unittests/AST/StructuralEquivalenceTest.cpp +++ unittests/AST/StructuralEquivalenceTest.cpp @@ -0,0 +1,207 @@ +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/AST/ASTStructuralEquivalence.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Tooling/Tooling.h" + +#include "Language.h" +#include "DeclMatcher.h" + +#include "gtest/gtest.h" + +namespace clang { +namespace ast_matchers { + +using std::get; + +struct StructuralEquivalenceTest : ::testing::Test { + std::unique_ptr AST0, AST1; + std::string Code0, Code1; // Buffers for SourceManager + + // Get a pair of Decl pointers to the synthetised declarations from the given + // code snipets. By default we search for the unique Decl with name 'foo' in + // both snippets. + std::tuple + makeNamedDecls(const std::string &SrcCode0, const std::string &SrcCode1, + Language Lang, const char *const Identifier = "foo") { + + this->Code0 = SrcCode0; + this->Code1 = SrcCode1; + ArgVector Args = getBasicRunOptionsForLanguage(Lang); + + const char *const InputFileName = "input.cc"; + + AST0 = tooling::buildASTFromCodeWithArgs(Code0, Args, InputFileName); + AST1 = tooling::buildASTFromCodeWithArgs(Code1, Args, InputFileName); + + ASTContext &Ctx0 = AST0->getASTContext(), &Ctx1 = AST1->getASTContext(); + + auto getDecl = [](ASTContext &Ctx, const std::string &Name) -> NamedDecl * { + IdentifierInfo *SearchedII = &Ctx.Idents.get(Name); + assert(SearchedII && "Declaration with the identifier " + "should be specified in test!"); + DeclarationName SearchDeclName(SearchedII); + SmallVector FoundDecls; + Ctx.getTranslationUnitDecl()->localUncachedLookup(SearchDeclName, + FoundDecls); + + // We should find one Decl but one only. + assert(FoundDecls.size() == 1); + + return FoundDecls[0]; + }; + + NamedDecl *D0 = getDecl(Ctx0, Identifier); + NamedDecl *D1 = getDecl(Ctx1, Identifier); + assert(D0); + assert(D1); + return std::make_tuple(D0, D1); + } + + bool testStructuralMatch(NamedDecl *D0, NamedDecl *D1) { + llvm::DenseSet> NonEquivalentDecls; + StructuralEquivalenceContext Ctx(D0->getASTContext(), D1->getASTContext(), + NonEquivalentDecls, false, false); + return Ctx.IsStructurallyEquivalent(D0, D1); + } + + bool testStructuralMatch(std::tuple t) { + return testStructuralMatch(get<0>(t), get<1>(t)); + } +}; + +TEST_F(StructuralEquivalenceTest, Int) { + auto Decls = makeNamedDecls("int foo;", "int foo;", Lang_CXX); + EXPECT_TRUE(testStructuralMatch(Decls)); +} + +TEST_F(StructuralEquivalenceTest, IntVsSignedInt) { + auto Decls = makeNamedDecls("int foo;", "signed int foo;", Lang_CXX); + EXPECT_TRUE(testStructuralMatch(Decls)); +} + +TEST_F(StructuralEquivalenceTest, Char) { + auto Decls = makeNamedDecls("char foo;", "char foo;", Lang_CXX); + EXPECT_TRUE(testStructuralMatch(Decls)); +} + +// This test is disabled for now. +// FIXME Whether this is equivalent is dependendant on the target. +TEST_F(StructuralEquivalenceTest, DISABLED_CharVsSignedChar) { + auto Decls = makeNamedDecls("char foo;", "signed char foo;", Lang_CXX); + EXPECT_FALSE(testStructuralMatch(Decls)); +} + +TEST_F(StructuralEquivalenceTest, ForwardRecordDecl) { + auto Decls = makeNamedDecls("struct foo;", "struct foo;", Lang_CXX); + EXPECT_TRUE(testStructuralMatch(Decls)); +} + +TEST_F(StructuralEquivalenceTest, IntVsSignedIntInStruct) { + auto Decls = makeNamedDecls("struct foo { int x; };", + "struct foo { signed int x; };", Lang_CXX); + EXPECT_TRUE(testStructuralMatch(Decls)); +} + +TEST_F(StructuralEquivalenceTest, CharVsSignedCharInStruct) { + auto Decls = makeNamedDecls("struct foo { char x; };", + "struct foo { signed char x; };", Lang_CXX); + EXPECT_FALSE(testStructuralMatch(Decls)); +} + +TEST_F(StructuralEquivalenceTest, IntVsSignedIntTemplateSpec) { + auto Decls = makeNamedDecls( + "template struct foo; template<> struct foo{};", + "template struct foo; template<> struct foo{};", + Lang_CXX); + ClassTemplateSpecializationDecl *Spec0 = + *cast(get<0>(Decls))->spec_begin(); + ClassTemplateSpecializationDecl *Spec1 = + *cast(get<1>(Decls))->spec_begin(); + ASSERT_TRUE(Spec0 != nullptr); + ASSERT_TRUE(Spec1 != nullptr); + EXPECT_TRUE(testStructuralMatch(Spec0, Spec1)); +} + +TEST_F(StructuralEquivalenceTest, CharVsSignedCharTemplateSpec) { + auto Decls = makeNamedDecls( + "template struct foo; template<> struct foo{};", + "template struct foo; template<> struct foo{};", + Lang_CXX); + ClassTemplateSpecializationDecl *Spec0 = + *cast(get<0>(Decls))->spec_begin(); + ClassTemplateSpecializationDecl *Spec1 = + *cast(get<1>(Decls))->spec_begin(); + ASSERT_TRUE(Spec0 != nullptr); + ASSERT_TRUE(Spec1 != nullptr); + EXPECT_FALSE(testStructuralMatch(Spec0, Spec1)); +} + +TEST_F(StructuralEquivalenceTest, CharVsSignedCharTemplateSpecWithInheritance) { + auto Decls = makeNamedDecls( + R"( + struct true_type{}; + template struct foo; + template<> struct foo : true_type {}; + )", + R"( + struct true_type{}; + template struct foo; + template<> struct foo : true_type {}; + )", + Lang_CXX); + ClassTemplateSpecializationDecl *Spec0 = + *cast(get<0>(Decls))->spec_begin(); + ClassTemplateSpecializationDecl *Spec1 = + *cast(get<1>(Decls))->spec_begin(); + ASSERT_TRUE(Spec0 != nullptr); + ASSERT_TRUE(Spec1 != nullptr); + EXPECT_FALSE(testStructuralMatch(Spec0, Spec1)); +} + +// This test is disabled for now. +// FIXME Enable it, once the check is implemented. +TEST_F(StructuralEquivalenceTest, DISABLED_WrongOrderInNamespace) { + auto Code = + R"( + namespace NS { + template class Base { + int a; + }; + class Derived : Base { + }; + } + void foo(NS::Derived &); + )"; + auto Decls = makeNamedDecls(Code, Code, Lang_CXX); + + NamespaceDecl *NS = + LastDeclMatcher().match(get<1>(Decls), namespaceDecl()); + ClassTemplateDecl *TD = LastDeclMatcher().match( + get<1>(Decls), classTemplateDecl(hasName("Base"))); + + // Reorder the decls, move the TD to the last place in the DC. + NS->removeDecl(TD); + NS->addDeclInternal(TD); + + EXPECT_FALSE(testStructuralMatch(Decls)); +} + +TEST_F(StructuralEquivalenceTest, WrongOrderOfFieldsInClass) { + auto Code = "class X { int a; int b; };"; + auto Decls = makeNamedDecls(Code, Code, Lang_CXX, "X"); + + CXXRecordDecl *RD = FirstDeclMatcher().match( + get<1>(Decls), cxxRecordDecl(hasName("X"))); + FieldDecl *FD = + FirstDeclMatcher().match(get<1>(Decls), fieldDecl(hasName("a"))); + + // Reorder the FieldDecls + RD->removeDecl(FD); + RD->addDeclInternal(FD); + + EXPECT_FALSE(testStructuralMatch(Decls)); +} + +} // end namespace ast_matchers +} // end namespace clang