From 3bb8bd6662baf02caa6618fc271b5daaebfbab63 Mon Sep 17 00:00:00 2001 From: Brad King Date: Thu, 20 Mar 2014 12:54:55 -0400 Subject: Output: Skip implicit members that cannot be compiled Implement the HandleTagDeclDefinition method in our ASTConsumer. Clang calls this method during parsing on completion of each class definition. This gives us a chance to modify class definitions. Teach HandleTagDeclDefinition to add the implicit member declarations. For each implicit member, ask Clang to try instantiating its definition to see if a call to the member will actually work. Suppress errors during this check using a clang::Sema::SFINAETrap and simply mark the declaration as invalid if there were errors. Add test cases for: * implicit members that cannot be used due to access, const data members, or reference data members. * a POD array data member since Clang uses special logic when creating implicit members that copy the data. * a mutable data member since Clang adds implicit member declarations itself (via clang::Sema::AddImplicitlyDeclaredMembersToClass), but we still need to try defining them. * a class that uses its implicit members so they are already defined. --- src/Output.cxx | 8 ++-- src/RunClang.cxx | 51 ++++++++++++++++++++++ test/CMakeLists.txt | 6 +++ ...ml.Class-implicit-member-access-mutable-xml.txt | 24 ++++++++++ .../gccxml.Class-implicit-member-access-xml.txt | 22 ++++++++++ .../gccxml.Class-implicit-member-array-xml.txt | 20 +++++++++ .../gccxml.Class-implicit-member-const-xml.txt | 16 +++++++ .../gccxml.Class-implicit-member-reference-xml.txt | 16 +++++++ test/expect/gccxml.Class-implicit-members-xml.txt | 20 +++++++++ .../input/Class-implicit-member-access-mutable.cxx | 10 +++++ test/input/Class-implicit-member-access.cxx | 9 ++++ test/input/Class-implicit-member-array.cxx | 3 ++ test/input/Class-implicit-member-const.cxx | 5 +++ test/input/Class-implicit-member-reference.cxx | 5 +++ test/input/Class-implicit-members.cxx | 7 +++ 15 files changed, 219 insertions(+), 3 deletions(-) create mode 100644 test/expect/gccxml.Class-implicit-member-access-mutable-xml.txt create mode 100644 test/expect/gccxml.Class-implicit-member-access-xml.txt create mode 100644 test/expect/gccxml.Class-implicit-member-array-xml.txt create mode 100644 test/expect/gccxml.Class-implicit-member-const-xml.txt create mode 100644 test/expect/gccxml.Class-implicit-member-reference-xml.txt create mode 100644 test/expect/gccxml.Class-implicit-members-xml.txt create mode 100644 test/input/Class-implicit-member-access-mutable.cxx create mode 100644 test/input/Class-implicit-member-access.cxx create mode 100644 test/input/Class-implicit-member-array.cxx create mode 100644 test/input/Class-implicit-member-const.cxx create mode 100644 test/input/Class-implicit-member-reference.cxx create mode 100644 test/input/Class-implicit-members.cxx diff --git a/src/Output.cxx b/src/Output.cxx index dab1230..08519cc 100644 --- a/src/Output.cxx +++ b/src/Output.cxx @@ -26,7 +26,6 @@ #include "clang/AST/DeclTemplate.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Lex/Preprocessor.h" -#include "clang/Sema/Sema.h" #include "llvm/Support/raw_ostream.h" #include @@ -405,6 +404,11 @@ unsigned int ASTVisitor::AddDumpNode(clang::Decl const* d, bool complete) { break; } + // Skip invalid declarations. + if(d->isInvalidDecl()) { + return 0; + } + return this->AddDumpNodeImpl(d, complete); } @@ -1150,8 +1154,6 @@ void ASTVisitor::OutputRecordDecl(clang::RecordDecl const* d, void ASTVisitor::OutputCXXRecordDecl(clang::CXXRecordDecl const* d, DumpNode const* dn) { - this->CI.getSema().ForceDeclarationOfImplicitMembers( - const_cast(d)); this->OutputRecordDecl(d, dn); } diff --git a/src/RunClang.cxx b/src/RunClang.cxx index e9f561d..55f8024 100644 --- a/src/RunClang.cxx +++ b/src/RunClang.cxx @@ -22,6 +22,8 @@ #include #include "clang/AST/ASTConsumer.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" #include "clang/Driver/Compilation.h" #include "clang/Driver/Driver.h" #include "clang/Driver/DriverDiagnostic.h" @@ -33,6 +35,7 @@ #include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Frontend/Utils.h" #include "clang/Lex/Preprocessor.h" +#include "clang/Sema/Sema.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/Option/ArgList.h" #include "llvm/Support/Host.h" @@ -51,6 +54,54 @@ public: std::vector const& startNames): CI(ci), OS(os), StartNames(startNames) {} + void AddImplicitMembers(clang::CXXRecordDecl* rd) { + clang::Sema& sema = this->CI.getSema(); + sema.ForceDeclarationOfImplicitMembers(rd); + +# define DEFINE_IMPLICIT(name, decl) do { \ + clang::Sema::SFINAETrap trap(sema, /*AccessChecking=*/ true); \ + sema.DefineImplicit##name(clang::SourceLocation(), (decl)); \ + if (trap.hasErrorOccurred()) { \ + (decl)->setInvalidDecl(); \ + } \ + } while(0) + + for(clang::DeclContext::decl_iterator i = rd->decls_begin(), + e = rd->decls_end(); i != e; ++i) { + clang::CXXMethodDecl* m = clang::dyn_cast(*i); + if(m && m->isImplicit() && !m->isDeleted() && + !m->doesThisDeclarationHaveABody()) { + if (clang::CXXConstructorDecl* c = + clang::dyn_cast(m)) { + if (c->isDefaultConstructor()) { + DEFINE_IMPLICIT(DefaultConstructor, c); + } else if (c->isCopyConstructor()) { + DEFINE_IMPLICIT(CopyConstructor, c); + } else if (c->isMoveConstructor()) { + DEFINE_IMPLICIT(MoveConstructor, c); + } + } else if (clang::CXXDestructorDecl* d = + clang::dyn_cast(m)) { + DEFINE_IMPLICIT(Destructor, d); + } else if (m->isCopyAssignmentOperator()) { + DEFINE_IMPLICIT(CopyAssignment, m); + } else if (m->isMoveAssignmentOperator()) { + DEFINE_IMPLICIT(MoveAssignment, m); + } + } + } + +# undef DEFINE_IMPLICIT + } + + void HandleTagDeclDefinition(clang::TagDecl* d) { + if(clang::CXXRecordDecl* rd = clang::dyn_cast(d)) { + if(!rd->isDependentContext()) { + this->AddImplicitMembers(rd); + } + } + } + void HandleTranslationUnit(clang::ASTContext& ctx) { outputXML(this->CI, ctx, this->OS, this->StartNames); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0441ce7..4749b58 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -86,6 +86,12 @@ castxml_test_gccxml(Class-base-typedef) castxml_test_gccxml(Class-bases) castxml_test_gccxml(Class-forward) castxml_test_gccxml(Class-friends) +castxml_test_gccxml(Class-implicit-member-access) +castxml_test_gccxml(Class-implicit-member-access-mutable) +castxml_test_gccxml(Class-implicit-member-array) +castxml_test_gccxml(Class-implicit-member-const) +castxml_test_gccxml(Class-implicit-member-reference) +castxml_test_gccxml(Class-implicit-members) castxml_test_gccxml(Class-incomplete) castxml_test_gccxml(Class-incomplete-twice) castxml_test_gccxml(Class-member-template) diff --git a/test/expect/gccxml.Class-implicit-member-access-mutable-xml.txt b/test/expect/gccxml.Class-implicit-member-access-mutable-xml.txt new file mode 100644 index 0000000..8e610e8 --- /dev/null +++ b/test/expect/gccxml.Class-implicit-member-access-mutable-xml.txt @@ -0,0 +1,24 @@ +^<\?xml version="1.0"\?> +]*> + + + + + + + + + + + + + + + + + + + + + +$ diff --git a/test/expect/gccxml.Class-implicit-member-access-xml.txt b/test/expect/gccxml.Class-implicit-member-access-xml.txt new file mode 100644 index 0000000..d3681b0 --- /dev/null +++ b/test/expect/gccxml.Class-implicit-member-access-xml.txt @@ -0,0 +1,22 @@ +^<\?xml version="1.0"\?> +]*> + + + + + + + + + + + + + + + + + + + +$ diff --git a/test/expect/gccxml.Class-implicit-member-array-xml.txt b/test/expect/gccxml.Class-implicit-member-array-xml.txt new file mode 100644 index 0000000..f6eac01 --- /dev/null +++ b/test/expect/gccxml.Class-implicit-member-array-xml.txt @@ -0,0 +1,20 @@ +^<\?xml version="1.0"\?> +]*> + + + + + + + + + + + + + + + + + +$ diff --git a/test/expect/gccxml.Class-implicit-member-const-xml.txt b/test/expect/gccxml.Class-implicit-member-const-xml.txt new file mode 100644 index 0000000..8accf1a --- /dev/null +++ b/test/expect/gccxml.Class-implicit-member-const-xml.txt @@ -0,0 +1,16 @@ +^<\?xml version="1.0"\?> +]*> + + + + + + + + + + + + + +$ diff --git a/test/expect/gccxml.Class-implicit-member-reference-xml.txt b/test/expect/gccxml.Class-implicit-member-reference-xml.txt new file mode 100644 index 0000000..72b0862 --- /dev/null +++ b/test/expect/gccxml.Class-implicit-member-reference-xml.txt @@ -0,0 +1,16 @@ +^<\?xml version="1.0"\?> +]*> + + + + + + + + + + + + + +$ diff --git a/test/expect/gccxml.Class-implicit-members-xml.txt b/test/expect/gccxml.Class-implicit-members-xml.txt new file mode 100644 index 0000000..58aed61 --- /dev/null +++ b/test/expect/gccxml.Class-implicit-members-xml.txt @@ -0,0 +1,20 @@ +^<\?xml version="1.0"\?> +]*> + + + + + + + + + + + + + + + + + +$ diff --git a/test/input/Class-implicit-member-access-mutable.cxx b/test/input/Class-implicit-member-access-mutable.cxx new file mode 100644 index 0000000..8d9aaec --- /dev/null +++ b/test/input/Class-implicit-member-access-mutable.cxx @@ -0,0 +1,10 @@ +class base { +protected: + base(); + ~base(); +private: + base(base const&); + base& operator=(base const&); + mutable int data; +}; +class start: public base {}; diff --git a/test/input/Class-implicit-member-access.cxx b/test/input/Class-implicit-member-access.cxx new file mode 100644 index 0000000..d9992bc --- /dev/null +++ b/test/input/Class-implicit-member-access.cxx @@ -0,0 +1,9 @@ +class base { +protected: + base(); + ~base(); +private: + base(base const&); + base& operator=(base const&); +}; +class start: public base {}; diff --git a/test/input/Class-implicit-member-array.cxx b/test/input/Class-implicit-member-array.cxx new file mode 100644 index 0000000..44002dc --- /dev/null +++ b/test/input/Class-implicit-member-array.cxx @@ -0,0 +1,3 @@ +class start { + int data[2]; +}; diff --git a/test/input/Class-implicit-member-const.cxx b/test/input/Class-implicit-member-const.cxx new file mode 100644 index 0000000..59221af --- /dev/null +++ b/test/input/Class-implicit-member-const.cxx @@ -0,0 +1,5 @@ +class start { + int const data; +public: + start(); +}; diff --git a/test/input/Class-implicit-member-reference.cxx b/test/input/Class-implicit-member-reference.cxx new file mode 100644 index 0000000..67da21d --- /dev/null +++ b/test/input/Class-implicit-member-reference.cxx @@ -0,0 +1,5 @@ +class start { + int& ref; +public: + start(); +}; diff --git a/test/input/Class-implicit-members.cxx b/test/input/Class-implicit-members.cxx new file mode 100644 index 0000000..2d79ee5 --- /dev/null +++ b/test/input/Class-implicit-members.cxx @@ -0,0 +1,7 @@ +class start { + start method(start const& x) { + start s; + s = x; + return s; + } +}; -- cgit v0.12