[clang][ExtractAPI] fix a couple crashes when used via libclang (#132297)

This PR fixes two crashes in ExtractAPI that occur when decls are
requested via libclang:

- A null-dereference would sometimes happen in
`DeclarationFragmentsBuilder::getFragmentsForClassTemplateSpecialization`
when the template being processed was loaded indirectly via a typedef,
with parameters filled in. The first commit loads the template parameter
locations ahead of time to perform a null check before dereferencing.
- An assertion (or another null-dereference) was happening in
`CXXRecordDecl::bases` when processing a forward-declaration (i.e. a
record without a definition). The second commit guards the use of
`bases` in `ExtractAPIVisitorBase::getBases` by first checking that the
decl in question has a complete definition.

The added test `extract-api-cursor-cpp` adds tests for these two
scenarios to protect against the crash in the future.

Fixes rdar://140592475, fixes rdar://123430367
This commit is contained in:
QuietMisdreavus 2025-03-26 17:46:21 -06:00 committed by GitHub
parent 78d7dd297f
commit 3386156b1e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 50 additions and 1 deletions

View File

@ -173,6 +173,10 @@ private:
protected:
SmallVector<SymbolReference> getBases(const CXXRecordDecl *Decl) {
if (!Decl->isCompleteDefinition()) {
return {};
}
// FIXME: store AccessSpecifier given by inheritance
SmallVector<SymbolReference> Bases;
for (const auto &BaseSpecifier : Decl->bases()) {

View File

@ -1225,6 +1225,10 @@ DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForClassTemplateSpecialization(
const ClassTemplateSpecializationDecl *Decl) {
DeclarationFragments Fragments;
std::optional<ArrayRef<TemplateArgumentLoc>> TemplateArgumentLocs = {};
if (auto *TemplateArgs = Decl->getTemplateArgsAsWritten()) {
TemplateArgumentLocs = TemplateArgs->arguments();
}
return Fragments
.append("template", DeclarationFragments::FragmentKind::Keyword)
.appendSpace()
@ -1237,7 +1241,7 @@ DeclarationFragmentsBuilder::getFragmentsForClassTemplateSpecialization(
.append("<", DeclarationFragments::FragmentKind::Text)
.append(getFragmentsForTemplateArguments(
Decl->getTemplateArgs().asArray(), Decl->getASTContext(),
Decl->getTemplateArgsAsWritten()->arguments()))
TemplateArgumentLocs))
.append(">", DeclarationFragments::FragmentKind::Text)
.appendSemicolon();
}

View File

@ -0,0 +1,41 @@
// Test is line- and column-sensitive. Run lines are below
template <typename T>
class basic_vector {
public:
T x;
T y;
};
using my_vec = basic_vector<int>;
class MyClass {
my_vec myVec;
};
struct OuterStruct {
struct InnerStruct;
int outer_field;
};
// RUN: c-index-test -single-symbol-sgf-at=%s:13:7 local %s | FileCheck --check-prefix=CHECK-VEC-TYPE %s
// CHECK-VEC-TYPE: "parentContexts":[{"kind":"c++.typealias","name":"my_vec","usr":"c:@my_vec"}]
// CHECK-VEC-TYPE: "declarationFragments":[{"kind":"keyword","spelling":"typedef"},{"kind":"text","spelling":" "},{"kind":"typeIdentifier","preciseIdentifier":"c:@ST>1#T@basic_vector","spelling":"basic_vector"},{"kind":"text","spelling":"<"},{"kind":"typeIdentifier","preciseIdentifier":"c:I","spelling":"int"},{"kind":"text","spelling":"> "},{"kind":"identifier","spelling":"my_vec"},{"kind":"text","spelling":";"}]
// CHECK-VEC-TYPE: "identifier":{"interfaceLanguage":"c++","precise":"c:@my_vec"}
// CHECK-VEC-TYPE: "kind":{"displayName":"Type Alias","identifier":"c++.typealias"}
// CHECK-VEC-TYPE: "title":"my_vec"
// CHECK-VEC-TYPE: "pathComponents":["my_vec"]
// RUN: c-index-test -single-symbol-sgf-at=%s:13:13 local %s | FileCheck --check-prefix=CHECK-MYVEC %s
// CHECK-MYVEC: "parentContexts":[{"kind":"c++.class","name":"MyClass","usr":"c:@S@MyClass"},{"kind":"c++.property","name":"myVec","usr":"c:@S@MyClass@FI@myVec"}]
// CHECK-MYVEC: "identifier":{"interfaceLanguage":"c++","precise":"c:@S@MyClass@FI@myVec"}
// CHECK-MYVEC: "kind":{"displayName":"Instance Property","identifier":"c++.property"}
// CHECK-MYVEC: "title":"myVec"
// CHECK-MYVEC: "pathComponents":["MyClass","myVec"]
// RUN: c-index-test -single-symbol-sgf-at=%s:17:17 local %s | FileCheck --check-prefix=CHECK-INNER %s
// CHECK-INNER: "parentContexts":[{"kind":"c++.struct","name":"OuterStruct","usr":"c:@S@OuterStruct"},{"kind":"c++.struct","name":"InnerStruct","usr":"c:@S@OuterStruct@S@InnerStruct"}]
// CHECK-INNER: "identifier":{"interfaceLanguage":"c++","precise":"c:@S@OuterStruct@S@InnerStruct"}
// CHECK-INNER: "kind":{"displayName":"Structure","identifier":"c++.struct"}
// CHECK-INNER: "title":"InnerStruct"
// CHECK-INNER: "pathComponents":["OuterStruct","InnerStruct"]