Do not crash if IsInHeader is called for builtin

In very particular circumstances (see test case), VisitFunctionDecl
would see a function definition that was an implicit constructor of a
builtin (va_list_tag).

Rather than crashing for implicit code, just say it's not in a header.

This feels a little questionable, because depending on how IsInHeader is
used, it might be misleading that it returns false for builtins/implicit
code. But I think it makes sense for now.

Fixes issue #1162.
This commit is contained in:
Kim Gräsman 2022-12-29 23:31:40 +01:00
parent 44fa8c7bd0
commit cced95dba5
3 changed files with 65 additions and 2 deletions

View File

@ -1777,7 +1777,7 @@ class IwyuBaseAstVisitor : public BaseAstVisitor<Derived> {
}
if (!IsInHeader(decl)) {
// No point in author-intent analysis of function definitions
// in source files.
// in source files or for builtins.
return true;
}
} else {

View File

@ -174,7 +174,11 @@ bool IsInScratchSpace(SourceLocation loc) {
bool IsInHeader(const clang::Decl* decl) {
const FileEntry* containing_file = GetFileEntry(decl);
CHECK_(containing_file);
if (!containing_file) {
// This is a builtin, or something is terribly wrong.
// At any rate, we're not in a header.
return false;
}
return !GlobalSourceManager()->isMainFile(*containing_file);
}

View File

@ -0,0 +1,59 @@
//===--- template_varargs.cc - test input file for iwyu -------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// This is a testcase heavily reduced from the most representative
// Boost.Serialization example available online:
// https://stackoverflow.com/a/33226687/96963.
//
// This input would previously crash IWYU because the implicit constructor of
// __builtin_va_list inside the virtual member function in a template does not
// have any location information.
template <class T>
class Varargs {
// Virtual member function is traversed as part of instantiation.
virtual void* unused(unsigned, ...) const {
__builtin_va_list x;
return nullptr;
}
};
template <class T>
struct Creator {
static T make() {
return T();
}
};
// Necessary complications to instantiate Varargs in a deeply nested context.
struct Base {
Base(const Varargs<int>&);
};
template <class T>
struct Derived : Base {
Derived() : Base(Creator<Varargs<int>>::make()) {
}
};
template <class T>
void InstantiateDerived() {
// Only instantiate the template, don't call the method.
(void)Creator<Derived<T>>::make;
}
void p() {
InstantiateDerived<int>();
}
/**** IWYU_SUMMARY
(tests/cxx/template_varargs.cc has correct #includes/fwd-decls)
***** IWYU_SUMMARY */