diff --git a/vndk/tools/header-checker/Android.bp b/vndk/tools/header-checker/Android.bp index 4fa4d5238..7c9fe0475 100644 --- a/vndk/tools/header-checker/Android.bp +++ b/vndk/tools/header-checker/Android.bp @@ -56,6 +56,7 @@ cc_binary_host { "header-abi-dumper/src/abi_wrappers.cpp", "header-abi-dumper/src/ast_processing.cpp", "header-abi-dumper/src/diagnostic_consumer.cpp", + "header-abi-dumper/src/fake_decl_source.cpp", "header-abi-dumper/src/fixed_argv.cpp", "header-abi-dumper/src/frontend_action.cpp", "header-abi-dumper/src/frontend_action_factory.cpp", diff --git a/vndk/tools/header-checker/header-abi-dumper/src/abi_wrappers.cpp b/vndk/tools/header-checker/header-abi-dumper/src/abi_wrappers.cpp index 8c3c045bf..e3b18fb63 100644 --- a/vndk/tools/header-checker/header-abi-dumper/src/abi_wrappers.cpp +++ b/vndk/tools/header-checker/header-abi-dumper/src/abi_wrappers.cpp @@ -364,7 +364,10 @@ TypeAndCreationStatus ABIWrapper::SetTypeKind( if (type_ptr->isRecordType()) { // If this record is anonymous, create it. const clang::RecordDecl *anon_record = GetAnonymousRecord(canonical_type); - if (anon_record && !CreateAnonymousRecord(anon_record)) { + // Avoid constructing RecordDeclWrapper with invalid record, which results + // in segmentation fault. + if (anon_record && !anon_record->isInvalidDecl() && + !CreateAnonymousRecord(anon_record)) { llvm::errs() << "Anonymous record could not be created\n"; ::exit(1); } diff --git a/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.cpp b/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.cpp index e4056a001..0aac0e332 100644 --- a/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.cpp +++ b/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.cpp @@ -107,7 +107,12 @@ bool HeaderASTVisitor::ShouldSkipFunctionDecl(const clang::FunctionDecl *decl) { } if (const clang::CXXMethodDecl *method_decl = llvm::dyn_cast(decl)) { - if (method_decl->getParent()->getTypeForDecl()->isDependentType()) { + const clang::CXXRecordDecl *record_decl = method_decl->getParent(); + // Avoid segmentation fault in getThunkInfo in getAllManglings. + if (method_decl->isVirtual() && record_decl->isInvalidDecl()) { + return true; + } + if (record_decl->getTypeForDecl()->isDependentType()) { return true; } } diff --git a/vndk/tools/header-checker/header-abi-dumper/src/fake_decl_source.cpp b/vndk/tools/header-checker/header-abi-dumper/src/fake_decl_source.cpp new file mode 100644 index 000000000..a8abbea4c --- /dev/null +++ b/vndk/tools/header-checker/header-abi-dumper/src/fake_decl_source.cpp @@ -0,0 +1,202 @@ +// Copyright (C) 2018 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "fake_decl_source.h" + +#include +#include + +FakeDeclSource::FakeDeclSource(const clang::CompilerInstance &ci) : ci_(ci) {} + +clang::CXXRecordDecl * +FakeDeclSource::CreateCXXRecordDecl(const clang::DeclarationName &name, + clang::DeclContext *decl_context) { + clang::CXXRecordDecl *cxx_record_decl = clang::CXXRecordDecl::Create( + ci_.getASTContext(), clang::TTK_Struct, decl_context, + clang::SourceLocation(), clang::SourceLocation(), + name.getAsIdentifierInfo(), /* PrevDecl */ nullptr); + cxx_record_decl->setInvalidDecl(true); + + return cxx_record_decl; +} + +clang::ClassTemplateDecl * +FakeDeclSource::CreateClassTemplateDecl(clang::CXXRecordDecl *cxx_record_decl, + clang::DeclContext *decl_context) { + clang::ASTContext &ast = ci_.getASTContext(); + + // Declare `template struct RecordName` in decl_context. + clang::TemplateTypeParmDecl *parm = clang::TemplateTypeParmDecl::Create( + ast, decl_context, clang::SourceLocation(), clang::SourceLocation(), + /* Depth */ 0, /* Position */ 0, /* Id */ nullptr, + /* Typename */ true, /* ParameterPack */ true); + parm->setInvalidDecl(true); + + clang::NamedDecl *parm_array[1] = {parm}; + clang::TemplateParameterList *parm_list = + clang::TemplateParameterList::Create( + ast, clang::SourceLocation(), clang::SourceLocation(), parm_array, + clang::SourceLocation(), /* RequiresClause */ nullptr); + + clang::ClassTemplateDecl *class_template_decl = + clang::ClassTemplateDecl::Create( + ast, decl_context, clang::SourceLocation(), + cxx_record_decl->getDeclName(), parm_list, cxx_record_decl, + /* AssociatedConstraints */ nullptr); + + cxx_record_decl->setDescribedClassTemplate(class_template_decl); + class_template_decl->setInvalidDecl(true); + + return class_template_decl; +} + +clang::NamespaceDecl * +FakeDeclSource::CreateNamespaceDecl(const clang::DeclarationName &name, + clang::DeclContext *decl_context) { + clang::NamespaceDecl *namespace_decl = clang::NamespaceDecl::Create( + ci_.getASTContext(), decl_context, /* Inline */ false, + clang::SourceLocation(), clang::SourceLocation(), + name.getAsIdentifierInfo(), /* PrevDecl */ nullptr); + namespace_decl->setInvalidDecl(true); + + return namespace_decl; +} + +clang::NamedDecl * +FakeDeclSource::CreateDecl(clang::Sema::LookupNameKind kind, + const clang::DeclarationNameInfo &name_info, + clang::DeclContext *decl_context) { + const clang::DeclarationName &name = name_info.getName(); + if (name.getNameKind() != clang::DeclarationName::Identifier) { + return nullptr; + } + + clang::NamedDecl *decl; + switch (kind) { + case clang::Sema::LookupOrdinaryName: + case clang::Sema::LookupTagName: { + clang::CXXRecordDecl *cxx_record_decl = + CreateCXXRecordDecl(name, decl_context); + // If `<` follows the type name, the type must be a template. + // Otherwise, the compiler takes it as a syntax error. + clang::Optional next_token = clang::Lexer::findNextToken( + name_info.getLoc(), ci_.getASTContext().getSourceManager(), + ci_.getLangOpts()); + if (next_token.hasValue() && next_token->is(clang::tok::less)) { + decl = CreateClassTemplateDecl(cxx_record_decl, decl_context); + } else { + decl = cxx_record_decl; + } + break; + } + case clang::Sema::LookupNestedNameSpecifierName: + decl = CreateNamespaceDecl(name, decl_context); + break; + default: + decl = nullptr; + } + + if (decl) { + decl_context->addDecl(decl); + } + return decl; +} + +clang::DeclContext * +FakeDeclSource::ResolveDeclContext(clang::DeclContext *member_context, + clang::Scope *scope, + clang::NestedNameSpecifier *nns) { + if (member_context) { + return member_context; + } + + if (nns) { + switch (nns->getKind()) { + case clang::NestedNameSpecifier::Namespace: + return nns->getAsNamespace(); + case clang::NestedNameSpecifier::NamespaceAlias: + return nns->getAsNamespaceAlias()->getNamespace(); + case clang::NestedNameSpecifier::TypeSpec: + case clang::NestedNameSpecifier::TypeSpecWithTemplate: + return nns->getAsRecordDecl(); + case clang::NestedNameSpecifier::Global: + return ci_.getASTContext().getTranslationUnitDecl(); + case clang::NestedNameSpecifier::Identifier: + case clang::NestedNameSpecifier::Super: + break; + } + } + + if (scope && scope->getEntity()) { + return scope->getEntity(); + } + + return ci_.getASTContext().getTranslationUnitDecl(); +} + +clang::TypoCorrection FakeDeclSource::CorrectTypo( + const clang::DeclarationNameInfo &typo, int lookup_kind, + clang::Scope *scope, clang::CXXScopeSpec *scope_spec, + clang::CorrectionCandidateCallback &ccc, clang::DeclContext *member_context, + bool entering_context, const clang::ObjCObjectPointerType *opt) { + // Skip function bodies. + if (scope && scope->getFnParent()) { + return clang::TypoCorrection(); + } + + clang::NestedNameSpecifier *nns = nullptr; + if (scope_spec && !scope_spec->isEmpty()) { + nns = scope_spec->getScopeRep(); + } + + clang::DeclContext *decl_context = + ResolveDeclContext(member_context, scope, nns); + + clang::NamedDecl *decl = + CreateDecl(clang::Sema::LookupNameKind(lookup_kind), typo, decl_context); + if (decl == nullptr) { + return clang::TypoCorrection(); + } + + return clang::TypoCorrection(decl, nns); +} + +bool FakeDeclSource::LookupUnqualified(clang::LookupResult &result, + clang::Scope *scope) { + // The compiler looks for redeclaration when it parses a known name. + if (result.isForRedeclaration()) { + return false; + } + // Skip function bodies. + if (scope && scope->getFnParent()) { + return false; + } + + clang::DeclContext *decl_context; + if (scope && scope->getEntity()) { + decl_context = scope->getEntity(); + } else { + decl_context = ci_.getASTContext().getTranslationUnitDecl(); + } + + clang::NamedDecl *decl = CreateDecl(result.getLookupKind(), + result.getLookupNameInfo(), decl_context); + if (decl == nullptr) { + return false; + } + + result.addDecl(decl); + result.resolveKind(); + return true; +} diff --git a/vndk/tools/header-checker/header-abi-dumper/src/fake_decl_source.h b/vndk/tools/header-checker/header-abi-dumper/src/fake_decl_source.h new file mode 100644 index 000000000..7d9cc7950 --- /dev/null +++ b/vndk/tools/header-checker/header-abi-dumper/src/fake_decl_source.h @@ -0,0 +1,59 @@ +// Copyright (C) 2018 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +// This class creates fake declarations when the compiler queries for unknown +// types. +class FakeDeclSource : public clang::ExternalSemaSource { + private: + const clang::CompilerInstance &ci_; + + clang::CXXRecordDecl *CreateCXXRecordDecl(const clang::DeclarationName &name, + clang::DeclContext *decl_context); + + clang::ClassTemplateDecl * + CreateClassTemplateDecl(clang::CXXRecordDecl *cxx_record_decl, + clang::DeclContext *decl_context); + + clang::NamespaceDecl *CreateNamespaceDecl(const clang::DeclarationName &name, + clang::DeclContext *decl_context); + + // This method creates a declaration in decl_context according to the lookup + // name kind and the declaration name kind. If this method doesn't support the + // kinds, it returns nullptr. + clang::NamedDecl *CreateDecl(clang::Sema::LookupNameKind kind, + const clang::DeclarationNameInfo &name, + clang::DeclContext *decl_context); + + // Return the DeclContext for CorrectTypo to create a declaration in. + clang::DeclContext *ResolveDeclContext(clang::DeclContext *member_context, + clang::Scope *scope, + clang::NestedNameSpecifier *nns); + + public: + FakeDeclSource(const clang::CompilerInstance &ci); + + clang::TypoCorrection + CorrectTypo(const clang::DeclarationNameInfo &typo, int lookup_kind, + clang::Scope *scope, clang::CXXScopeSpec *scope_spec, + clang::CorrectionCandidateCallback &ccc, + clang::DeclContext *member_context, bool entering_context, + const clang::ObjCObjectPointerType *opt) override; + + bool LookupUnqualified(clang::LookupResult &result, + clang::Scope *scope) override; +}; diff --git a/vndk/tools/header-checker/header-abi-dumper/src/frontend_action.cpp b/vndk/tools/header-checker/header-abi-dumper/src/frontend_action.cpp index 0bbe8832a..6662fce87 100644 --- a/vndk/tools/header-checker/header-abi-dumper/src/frontend_action.cpp +++ b/vndk/tools/header-checker/header-abi-dumper/src/frontend_action.cpp @@ -16,6 +16,7 @@ #include "ast_processing.h" #include "diagnostic_consumer.h" +#include "fake_decl_source.h" #include "header_abi_util.h" #include "ir_representation.h" @@ -38,6 +39,7 @@ HeaderCheckerFrontendAction::CreateASTConsumer(clang::CompilerInstance &ci, bool HeaderCheckerFrontendAction::BeginInvocation(clang::CompilerInstance &ci) { if (options_.suppress_errors_) { + ci.getFrontendOpts().SkipFunctionBodies = true; clang::DiagnosticsEngine &diagnostics = ci.getDiagnostics(); diagnostics.setClient( new HeaderCheckerDiagnosticConsumer(diagnostics.takeClient()), @@ -48,7 +50,9 @@ bool HeaderCheckerFrontendAction::BeginInvocation(clang::CompilerInstance &ci) { bool HeaderCheckerFrontendAction::BeginSourceFileAction( clang::CompilerInstance &ci) { - ci.getPreprocessor().SetSuppressIncludeNotFoundError( - options_.suppress_errors_); + if (options_.suppress_errors_) { + ci.setExternalSemaSource(new FakeDeclSource(ci)); + ci.getPreprocessor().SetSuppressIncludeNotFoundError(true); + } return true; }