Skip to content

Commit 630285e

Browse files
[clang][ExtractAPI] fix a couple crashes when used via libclang (llvm#132297) (#10362)
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
1 parent 965c511 commit 630285e

File tree

3 files changed

+50
-1
lines changed

3 files changed

+50
-1
lines changed

clang/include/clang/ExtractAPI/ExtractAPIVisitor.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,10 @@ class ExtractAPIVisitorBase : public RecursiveASTVisitor<Derived> {
173173

174174
protected:
175175
SmallVector<SymbolReference> getBases(const CXXRecordDecl *Decl) {
176+
if (!Decl->isCompleteDefinition()) {
177+
return {};
178+
}
179+
176180
// FIXME: store AccessSpecifier given by inheritance
177181
SmallVector<SymbolReference> Bases;
178182
for (const auto &BaseSpecifier : Decl->bases()) {

clang/lib/ExtractAPI/DeclarationFragments.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1226,6 +1226,10 @@ DeclarationFragments
12261226
DeclarationFragmentsBuilder::getFragmentsForClassTemplateSpecialization(
12271227
const ClassTemplateSpecializationDecl *Decl) {
12281228
DeclarationFragments Fragments;
1229+
std::optional<ArrayRef<TemplateArgumentLoc>> TemplateArgumentLocs = {};
1230+
if (auto *TemplateArgs = Decl->getTemplateArgsAsWritten()) {
1231+
TemplateArgumentLocs = TemplateArgs->arguments();
1232+
}
12291233
return Fragments
12301234
.append("template", DeclarationFragments::FragmentKind::Keyword)
12311235
.appendSpace()
@@ -1238,7 +1242,7 @@ DeclarationFragmentsBuilder::getFragmentsForClassTemplateSpecialization(
12381242
.append("<", DeclarationFragments::FragmentKind::Text)
12391243
.append(getFragmentsForTemplateArguments(
12401244
Decl->getTemplateArgs().asArray(), Decl->getASTContext(),
1241-
Decl->getTemplateArgsAsWritten()->arguments()))
1245+
TemplateArgumentLocs))
12421246
.append(">", DeclarationFragments::FragmentKind::Text)
12431247
.appendSemicolon();
12441248
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Test is line- and column-sensitive. Run lines are below
2+
3+
template <typename T>
4+
class basic_vector {
5+
public:
6+
T x;
7+
T y;
8+
};
9+
10+
using my_vec = basic_vector<int>;
11+
12+
class MyClass {
13+
my_vec myVec;
14+
};
15+
16+
struct OuterStruct {
17+
struct InnerStruct;
18+
int outer_field;
19+
};
20+
21+
// RUN: c-index-test -single-symbol-sgf-at=%s:13:7 local %s | FileCheck --check-prefix=CHECK-VEC-TYPE %s
22+
// CHECK-VEC-TYPE: "parentContexts":[{"kind":"c++.typealias","name":"my_vec","usr":"c:@my_vec"}]
23+
// 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":";"}]
24+
// CHECK-VEC-TYPE: "identifier":{"interfaceLanguage":"c++","precise":"c:@my_vec"}
25+
// CHECK-VEC-TYPE: "kind":{"displayName":"Type Alias","identifier":"c++.typealias"}
26+
// CHECK-VEC-TYPE: "title":"my_vec"
27+
// CHECK-VEC-TYPE: "pathComponents":["my_vec"]
28+
29+
// RUN: c-index-test -single-symbol-sgf-at=%s:13:13 local %s | FileCheck --check-prefix=CHECK-MYVEC %s
30+
// CHECK-MYVEC: "parentContexts":[{"kind":"c++.class","name":"MyClass","usr":"c:@S@MyClass"},{"kind":"c++.property","name":"myVec","usr":"c:@S@MyClass@FI@myVec"}]
31+
// CHECK-MYVEC: "identifier":{"interfaceLanguage":"c++","precise":"c:@S@MyClass@FI@myVec"}
32+
// CHECK-MYVEC: "kind":{"displayName":"Instance Property","identifier":"c++.property"}
33+
// CHECK-MYVEC: "title":"myVec"
34+
// CHECK-MYVEC: "pathComponents":["MyClass","myVec"]
35+
36+
// RUN: c-index-test -single-symbol-sgf-at=%s:17:17 local %s | FileCheck --check-prefix=CHECK-INNER %s
37+
// CHECK-INNER: "parentContexts":[{"kind":"c++.struct","name":"OuterStruct","usr":"c:@S@OuterStruct"},{"kind":"c++.struct","name":"InnerStruct","usr":"c:@S@OuterStruct@S@InnerStruct"}]
38+
// CHECK-INNER: "identifier":{"interfaceLanguage":"c++","precise":"c:@S@OuterStruct@S@InnerStruct"}
39+
// CHECK-INNER: "kind":{"displayName":"Structure","identifier":"c++.struct"}
40+
// CHECK-INNER: "title":"InnerStruct"
41+
// CHECK-INNER: "pathComponents":["OuterStruct","InnerStruct"]

0 commit comments

Comments
 (0)