[clang] [clang][ExtractAPI] Fix for nested types are not enabled. (PR #159698)
NagaChaitanya Vellanki via cfe-commits
cfe-commits at lists.llvm.org
Fri Sep 19 15:55:25 PDT 2025
https://github.com/chaitanyav updated https://github.com/llvm/llvm-project/pull/159698
>From 6b638a98afd02759b4de7031585a1f0d0ffc8740 Mon Sep 17 00:00:00 2001
From: NagaChaitanya Vellanki <pnagato at protonmail.com>
Date: Thu, 18 Sep 2025 20:36:53 -0700
Subject: [PATCH] [clang][ExtractAPI] Fix for nested types are not enabled.
Recursively extract nested types. Keep anonymous structs/unions embedded in var declarators
Fixes #61477
---
.../clang/ExtractAPI/ExtractAPIVisitor.h | 68 +--
.../ExtractAPI/anonymous_record_no_typedef.c | 24 +-
clang/test/ExtractAPI/nested_types.cpp | 290 ++++++++++
clang/test/ExtractAPI/nested_types_deep.cpp | 532 ++++++++++++++++++
4 files changed, 850 insertions(+), 64 deletions(-)
create mode 100644 clang/test/ExtractAPI/nested_types.cpp
create mode 100644 clang/test/ExtractAPI/nested_types_deep.cpp
diff --git a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
index 9ea664f57f828..5f0b8dfcede62 100644
--- a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
+++ b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
@@ -250,27 +250,6 @@ class ExtractAPIVisitorBase : public RecursiveASTVisitor<Derived> {
return D.getName().empty() && getTypedefName(&D).empty() &&
D.isEmbeddedInDeclarator() && !D.isFreeStanding();
}
-
- void maybeMergeWithAnonymousTag(const DeclaratorDecl &D,
- RecordContext *NewRecordContext) {
- if (!NewRecordContext)
- return;
- auto *Tag = D.getType()->getAsTagDecl();
- if (!Tag) {
- if (const auto *AT = D.getASTContext().getAsArrayType(D.getType())) {
- Tag = AT->getElementType()->getAsTagDecl();
- }
- }
- SmallString<128> TagUSR;
- clang::index::generateUSRForDecl(Tag, TagUSR);
- if (auto *Record = llvm::dyn_cast_if_present<TagRecord>(
- API.findRecordForUSR(TagUSR))) {
- if (Record->IsEmbeddedInVarDeclarator) {
- NewRecordContext->stealRecordChain(*Record);
- API.removeRecord(Record);
- }
- }
- }
};
template <typename Derived>
@@ -322,14 +301,10 @@ bool ExtractAPIVisitorBase<Derived>::VisitVarDecl(const VarDecl *Decl) {
SubHeading, Access, isInSystemHeader(Decl));
} else {
// Add the global variable record to the API set.
- auto *NewRecord = API.createRecord<GlobalVariableRecord>(
+ API.createRecord<GlobalVariableRecord>(
USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
AvailabilityInfo::createFromDecl(Decl), Linkage, Comment, Declaration,
SubHeading, isInSystemHeader(Decl));
-
- // If this global variable has a non typedef'd anonymous tag type let's
- // pretend the type's child records are under us in the hierarchy.
- maybeMergeWithAnonymousTag(*Decl, NewRecord);
}
return true;
@@ -565,15 +540,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitNamespaceDecl(
template <typename Derived>
bool ExtractAPIVisitorBase<Derived>::TraverseRecordDecl(RecordDecl *Decl) {
- bool Ret = Base::TraverseRecordDecl(Decl);
-
- if (!isEmbeddedInVarDeclarator(*Decl) && Decl->isAnonymousStructOrUnion()) {
- SmallString<128> USR;
- index::generateUSRForDecl(Decl, USR);
- API.removeRecord(USR);
- }
-
- return Ret;
+ return Base::TraverseRecordDecl(Decl);
}
template <typename Derived>
@@ -620,13 +587,7 @@ template <typename Derived>
bool ExtractAPIVisitorBase<Derived>::TraverseCXXRecordDecl(
CXXRecordDecl *Decl) {
bool Ret = Base::TraverseCXXRecordDecl(Decl);
-
- if (!isEmbeddedInVarDeclarator(*Decl) && Decl->isAnonymousStructOrUnion()) {
- SmallString<128> USR;
- index::generateUSRForDecl(Decl, USR);
- API.removeRecord(USR);
- }
-
+ // Keep anonymous structs in the hierarchy to preserve path components
return Ret;
}
@@ -643,6 +604,11 @@ bool ExtractAPIVisitorBase<Derived>::VisitCXXRecordDecl(
SmallString<128> USR;
index::generateUSRForDecl(Decl, USR);
+
+ if (API.findRecordForUSR(USR)) {
+ return true;
+ }
+
PresumedLoc Loc =
Context.getSourceManager().getPresumedLoc(Decl->getLocation());
DocComment Comment;
@@ -679,6 +645,13 @@ bool ExtractAPIVisitorBase<Derived>::VisitCXXRecordDecl(
Record->KindForDisplay = getKindForDisplay(Decl);
Record->Bases = getBases(Decl);
+ // Visit nested records
+ for (const auto *NestedDecl : Decl->decls()) {
+ if (auto *NestedRecord = dyn_cast<CXXRecordDecl>(NestedDecl)) {
+ VisitCXXRecordDecl(NestedRecord);
+ }
+ }
+
return true;
}
@@ -1320,31 +1293,26 @@ bool ExtractAPIVisitorBase<Derived>::VisitFieldDecl(const FieldDecl *Decl) {
DeclarationFragments SubHeading =
DeclarationFragmentsBuilder::getSubHeading(Decl);
- RecordContext *NewRecord = nullptr;
if (isa<CXXRecordDecl>(Decl->getDeclContext())) {
AccessControl Access = DeclarationFragmentsBuilder::getAccessControl(Decl);
- NewRecord = API.createRecord<CXXFieldRecord>(
+ API.createRecord<CXXFieldRecord>(
USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
AvailabilityInfo::createFromDecl(Decl), Comment, Declaration,
SubHeading, Access, isInSystemHeader(Decl));
} else if (auto *RD = dyn_cast<RecordDecl>(Decl->getDeclContext())) {
if (RD->isUnion())
- NewRecord = API.createRecord<UnionFieldRecord>(
+ API.createRecord<UnionFieldRecord>(
USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
AvailabilityInfo::createFromDecl(Decl), Comment, Declaration,
SubHeading, isInSystemHeader(Decl));
else
- NewRecord = API.createRecord<StructFieldRecord>(
+ API.createRecord<StructFieldRecord>(
USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
AvailabilityInfo::createFromDecl(Decl), Comment, Declaration,
SubHeading, isInSystemHeader(Decl));
}
- // If this field has a non typedef'd anonymous tag type let's pretend the
- // type's child records are under us in the hierarchy.
- maybeMergeWithAnonymousTag(*Decl, NewRecord);
-
return true;
}
diff --git a/clang/test/ExtractAPI/anonymous_record_no_typedef.c b/clang/test/ExtractAPI/anonymous_record_no_typedef.c
index d278cbb8ec348..f6d8cb7b0f3d4 100644
--- a/clang/test/ExtractAPI/anonymous_record_no_typedef.c
+++ b/clang/test/ExtractAPI/anonymous_record_no_typedef.c
@@ -43,19 +43,19 @@ struct { char *prefix; char *content; } global;
// GLOBAL-NEXT: "global"
// GLOBAL-NEXT:]
-// PREFIX: "!testRelLabel": "memberOf $ c:@S at anonymous_record_no_typedef.c@{{[0-9]+}}@FI at prefix $ c:@global"
+// PREFIX: "!testRelLabel": "memberOf $ c:@S at anonymous_record_no_typedef.c@{{[0-9]+}}@FI at prefix $ c:@S at anonymous_record_no_typedef.c@{{[0-9]+}}"
// PREFIX-LABEL: "!testLabel": "c:@S at anonymous_record_no_typedef.c@{{[0-9]+}}@FI at prefix"
// PREFIX: "title": "prefix"
// PREFIX: "pathComponents": [
-// PREFIX-NEXT: "global",
+// PREFIX-NEXT: "",
// PREFIX-NEXT: "prefix"
// PREFIX-NEXT: ]
-// CONTENT: "!testRelLabel": "memberOf $ c:@S at anonymous_record_no_typedef.c@{{[0-9]+}}@FI at content $ c:@global"
+// CONTENT: "!testRelLabel": "memberOf $ c:@S at anonymous_record_no_typedef.c@{{[0-9]+}}@FI at content $ c:@S at anonymous_record_no_typedef.c@{{[0-9]+}}"
// CONTENT-LABEL: "!testLabel": "c:@S at anonymous_record_no_typedef.c@{{[0-9]+}}@FI at content"
// CONTENT: "title": "content"
// CONTENT: "pathComponents": [
-// CONTENT-NEXT: "global",
+// CONTENT-NEXT: "",
// CONTENT-NEXT: "content"
// CONTENT-NEXT: ]
@@ -139,21 +139,21 @@ struct Vehicle {
// INFORMATION: "text": "The information about the vehicle."
// INFORMATION: "title": "information"
- // WHEELS: "!testRelLabel": "memberOf $ c:@S at Vehicle@U at anonymous_record_no_typedef.c@{{[0-9]+}}@FI at wheels $ c:@S at Vehicle@FI at information"
+ // WHEELS: "!testRelLabel": "memberOf $ c:@S at Vehicle@U at anonymous_record_no_typedef.c@{{[0-9]+}}@FI at wheels $ c:@S at Vehicle@U at anonymous_record_no_typedef.c@{{[0-9]+}}"
// WHEELS-LABEL: "!testLabel": "c:@S at Vehicle@U at anonymous_record_no_typedef.c@{{[0-9]+}}@FI at wheels"
// WHEELS: "title": "wheels"
// WHEELS: "pathComponents": [
// WHEELS-NEXT: "Vehicle",
- // WHEELS-NEXT: "information",
+ // WHEELS-NEXT: "",
// WHEELS-NEXT: "wheels"
// WHEELS-NEXT: ]
- // NAME: "!testRelLabel": "memberOf $ c:@S at Vehicle@U at anonymous_record_no_typedef.c@{{[0-9]+}}@FI at name $ c:@S at Vehicle@FI at information"
+ // NAME: "!testRelLabel": "memberOf $ c:@S at Vehicle@U at anonymous_record_no_typedef.c@{{[0-9]+}}@FI at name $ c:@S at Vehicle@U at anonymous_record_no_typedef.c@{{[0-9]+}}"
// NAME-LABEL: "!testLabel": "c:@S at Vehicle@U at anonymous_record_no_typedef.c@{{[0-9]+}}@FI at name"
// NAME: "title": "name"
// NAME: "pathComponents": [
// NAME-NEXT: "Vehicle",
- // NAME-NEXT: "information",
+ // NAME-NEXT: "",
// NAME-NEXT: "name"
// NAME-NEXT: ]
};
@@ -188,11 +188,9 @@ union Vector {
float Data[2];
};
// VEC-DAG: "!testRelLabel": "memberOf $ c:@U at Vector@FI at Data $ c:@U at Vector"
-// VEC-DAG: "!testRelLabel": "memberOf $ c:@U at Vector@Sa at FI@X $ c:@U at Vector"
-// VEC-DAG: "!testRelLabel": "memberOf $ c:@U at Vector@Sa at FI@Y $ c:@U at Vector"
+// VEC-DAG: "!testRelLabel": "memberOf $ c:@U at Vector@Sa at FI@X $ c:@U at Vector@Sa"
+// VEC-DAG: "!testRelLabel": "memberOf $ c:@U at Vector@Sa at FI@Y $ c:@U at Vector@Sa"
-// RUN: FileCheck %s --input-file %t/output-c.symbols.json --check-prefix MYSTRUCT
-// RUN: FileCheck %s --input-file %t/output-cxx.symbols.json --check-prefix MYSTRUCT
// RUN: FileCheck %s --input-file %t/output-c.symbols.json --check-prefix COUNTS
// RUN: FileCheck %s --input-file %t/output-cxx.symbols.json --check-prefix COUNTS
struct MyStruct {
@@ -200,8 +198,6 @@ struct MyStruct {
int count;
} counts[1];
};
-// MYSTRUCT-NOT: "spelling": ""
-// MYSTRUCT-NOT: "title": ""
// COUNTS-LABEL: "!testLabel": "c:@S at MyStruct@FI at counts"
// COUNTS: "declarationFragments": [
diff --git a/clang/test/ExtractAPI/nested_types.cpp b/clang/test/ExtractAPI/nested_types.cpp
new file mode 100644
index 0000000000000..88d709a2c0772
--- /dev/null
+++ b/clang/test/ExtractAPI/nested_types.cpp
@@ -0,0 +1,290 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.output.json.in >> %t/reference.output.json
+// RUN: %clang_cc1 -extract-api --pretty-sgf -triple arm64-apple-macosx \
+// RUN: -x c++-header %t/input.h -o %t/output.json -verify
+
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/output.json >> %t/output-normalized.json
+// RUN: diff %t/reference.output.json %t/output-normalized.json
+
+//--- input.h
+struct MyStruct {
+ struct {
+ int count;
+ } counts[1];
+};
+
+/// expected-no-diagnostics
+
+//--- reference.output.json.in
+{
+ "metadata": {
+ "formatVersion": {
+ "major": 0,
+ "minor": 5,
+ "patch": 3
+ },
+ "generator": "?"
+ },
+ "module": {
+ "name": "",
+ "platform": {
+ "architecture": "arm64",
+ "operatingSystem": {
+ "minimumVersion": {
+ "major": 11,
+ "minor": 0,
+ "patch": 0
+ },
+ "name": "macosx"
+ },
+ "vendor": "apple"
+ }
+ },
+ "relationships": [
+ {
+ "kind": "memberOf",
+ "source": "c:@S at MyStruct@S at input.h@22",
+ "target": "c:@S at MyStruct",
+ "targetFallback": "MyStruct"
+ },
+ {
+ "kind": "memberOf",
+ "source": "c:@S at MyStruct@S at input.h@22 at FI@count",
+ "target": "c:@S at MyStruct@S at input.h@22",
+ "targetFallback": ""
+ },
+ {
+ "kind": "memberOf",
+ "source": "c:@S at MyStruct@FI at counts",
+ "target": "c:@S at MyStruct",
+ "targetFallback": "MyStruct"
+ }
+ ],
+ "symbols": [
+ {
+ "accessLevel": "public",
+ "declarationFragments": [
+ {
+ "kind": "keyword",
+ "spelling": "struct"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "MyStruct"
+ },
+ {
+ "kind": "text",
+ "spelling": ";"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "c++",
+ "precise": "c:@S at MyStruct"
+ },
+ "kind": {
+ "displayName": "Structure",
+ "identifier": "c++.struct"
+ },
+ "location": {
+ "position": {
+ "character": 7,
+ "line": 0
+ },
+ "uri": "file://INPUT_DIR/input.h"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "MyStruct"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "MyStruct"
+ }
+ ],
+ "title": "MyStruct"
+ },
+ "pathComponents": [
+ "MyStruct"
+ ]
+ },
+ {
+ "accessLevel": "public",
+ "declarationFragments": [
+ {
+ "kind": "keyword",
+ "spelling": "struct"
+ },
+ {
+ "kind": "text",
+ "spelling": ";"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "c++",
+ "precise": "c:@S at MyStruct@S at input.h@22"
+ },
+ "kind": {
+ "displayName": "Structure",
+ "identifier": "c++.struct"
+ },
+ "location": {
+ "position": {
+ "character": 4,
+ "line": 1
+ },
+ "uri": "file://INPUT_DIR/input.h"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": ""
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": ""
+ }
+ ],
+ "title": ""
+ },
+ "pathComponents": [
+ "MyStruct",
+ ""
+ ]
+ },
+ {
+ "accessLevel": "public",
+ "declarationFragments": [
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:I",
+ "spelling": "int"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "count"
+ },
+ {
+ "kind": "text",
+ "spelling": ";"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "c++",
+ "precise": "c:@S at MyStruct@S at input.h@22 at FI@count"
+ },
+ "kind": {
+ "displayName": "Instance Property",
+ "identifier": "c++.property"
+ },
+ "location": {
+ "position": {
+ "character": 12,
+ "line": 2
+ },
+ "uri": "file://INPUT_DIR/input.h"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "count"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "count"
+ }
+ ],
+ "title": "count"
+ },
+ "pathComponents": [
+ "MyStruct",
+ "",
+ "count"
+ ]
+ },
+ {
+ "accessLevel": "public",
+ "declarationFragments": [
+ {
+ "kind": "keyword",
+ "spelling": "struct"
+ },
+ {
+ "kind": "text",
+ "spelling": " { ... } "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "counts"
+ },
+ {
+ "kind": "text",
+ "spelling": "["
+ },
+ {
+ "kind": "number",
+ "spelling": "1"
+ },
+ {
+ "kind": "text",
+ "spelling": "];"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "c++",
+ "precise": "c:@S at MyStruct@FI at counts"
+ },
+ "kind": {
+ "displayName": "Instance Property",
+ "identifier": "c++.property"
+ },
+ "location": {
+ "position": {
+ "character": 6,
+ "line": 3
+ },
+ "uri": "file://INPUT_DIR/input.h"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "counts"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "counts"
+ }
+ ],
+ "title": "counts"
+ },
+ "pathComponents": [
+ "MyStruct",
+ "counts"
+ ]
+ }
+ ]
+}
diff --git a/clang/test/ExtractAPI/nested_types_deep.cpp b/clang/test/ExtractAPI/nested_types_deep.cpp
new file mode 100644
index 0000000000000..4ef85734fe7c2
--- /dev/null
+++ b/clang/test/ExtractAPI/nested_types_deep.cpp
@@ -0,0 +1,532 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.output.json.in >> %t/reference.output.json
+// RUN: %clang_cc1 -extract-api --pretty-sgf -triple arm64-apple-macosx \
+// RUN: -x c++-header %t/input.h -o %t/output.json -verify
+
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/output.json >> %t/output-normalized.json
+// RUN: diff %t/reference.output.json %t/output-normalized.json
+
+//--- input.h
+class Outer {
+ class Inner1 {
+ class DeeplyNested {
+ int value;
+ };
+ };
+
+ struct Inner2 {
+ enum Status { READY, PENDING };
+ };
+};
+
+/// expected-no-diagnostics
+
+//--- reference.output.json.in
+{
+ "metadata": {
+ "formatVersion": {
+ "major": 0,
+ "minor": 5,
+ "patch": 3
+ },
+ "generator": "?"
+ },
+ "module": {
+ "name": "",
+ "platform": {
+ "architecture": "arm64",
+ "operatingSystem": {
+ "minimumVersion": {
+ "major": 11,
+ "minor": 0,
+ "patch": 0
+ },
+ "name": "macosx"
+ },
+ "vendor": "apple"
+ }
+ },
+ "relationships": [
+ {
+ "kind": "memberOf",
+ "source": "c:@S at Outer@S at Inner1",
+ "target": "c:@S at Outer",
+ "targetFallback": "Outer"
+ },
+ {
+ "kind": "memberOf",
+ "source": "c:@S at Outer@S at Inner1@S at DeeplyNested",
+ "target": "c:@S at Outer@S at Inner1",
+ "targetFallback": "Inner1"
+ },
+ {
+ "kind": "memberOf",
+ "source": "c:@S at Outer@S at Inner1@S at DeeplyNested@FI at value",
+ "target": "c:@S at Outer@S at Inner1@S at DeeplyNested",
+ "targetFallback": "DeeplyNested"
+ },
+ {
+ "kind": "memberOf",
+ "source": "c:@S at Outer@S at Inner2",
+ "target": "c:@S at Outer",
+ "targetFallback": "Outer"
+ },
+ {
+ "kind": "memberOf",
+ "source": "c:@S at Outer@S at Inner2@E at Status",
+ "target": "c:@S at Outer@S at Inner2",
+ "targetFallback": "Inner2"
+ },
+ {
+ "kind": "memberOf",
+ "source": "c:@S at Outer@S at Inner2@E at Status@READY",
+ "target": "c:@S at Outer@S at Inner2@E at Status",
+ "targetFallback": "Status"
+ },
+ {
+ "kind": "memberOf",
+ "source": "c:@S at Outer@S at Inner2@E at Status@PENDING",
+ "target": "c:@S at Outer@S at Inner2@E at Status",
+ "targetFallback": "Status"
+ }
+ ],
+ "symbols": [
+ {
+ "accessLevel": "public",
+ "declarationFragments": [
+ {
+ "kind": "keyword",
+ "spelling": "class"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "Outer"
+ },
+ {
+ "kind": "text",
+ "spelling": ";"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "c++",
+ "precise": "c:@S at Outer"
+ },
+ "kind": {
+ "displayName": "Class",
+ "identifier": "c++.class"
+ },
+ "location": {
+ "position": {
+ "character": 6,
+ "line": 0
+ },
+ "uri": "file://INPUT_DIR/input.h"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "Outer"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "Outer"
+ }
+ ],
+ "title": "Outer"
+ },
+ "pathComponents": [
+ "Outer"
+ ]
+ },
+ {
+ "accessLevel": "private",
+ "declarationFragments": [
+ {
+ "kind": "keyword",
+ "spelling": "class"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "Inner1"
+ },
+ {
+ "kind": "text",
+ "spelling": ";"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "c++",
+ "precise": "c:@S at Outer@S at Inner1"
+ },
+ "kind": {
+ "displayName": "Class",
+ "identifier": "c++.class"
+ },
+ "location": {
+ "position": {
+ "character": 8,
+ "line": 1
+ },
+ "uri": "file://INPUT_DIR/input.h"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "Inner1"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "Inner1"
+ }
+ ],
+ "title": "Inner1"
+ },
+ "pathComponents": [
+ "Outer",
+ "Inner1"
+ ]
+ },
+ {
+ "accessLevel": "private",
+ "declarationFragments": [
+ {
+ "kind": "keyword",
+ "spelling": "class"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "DeeplyNested"
+ },
+ {
+ "kind": "text",
+ "spelling": ";"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "c++",
+ "precise": "c:@S at Outer@S at Inner1@S at DeeplyNested"
+ },
+ "kind": {
+ "displayName": "Class",
+ "identifier": "c++.class"
+ },
+ "location": {
+ "position": {
+ "character": 10,
+ "line": 2
+ },
+ "uri": "file://INPUT_DIR/input.h"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "DeeplyNested"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "DeeplyNested"
+ }
+ ],
+ "title": "DeeplyNested"
+ },
+ "pathComponents": [
+ "Outer",
+ "Inner1",
+ "DeeplyNested"
+ ]
+ },
+ {
+ "accessLevel": "private",
+ "declarationFragments": [
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:I",
+ "spelling": "int"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "value"
+ },
+ {
+ "kind": "text",
+ "spelling": ";"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "c++",
+ "precise": "c:@S at Outer@S at Inner1@S at DeeplyNested@FI at value"
+ },
+ "kind": {
+ "displayName": "Instance Property",
+ "identifier": "c++.property"
+ },
+ "location": {
+ "position": {
+ "character": 10,
+ "line": 3
+ },
+ "uri": "file://INPUT_DIR/input.h"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "value"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "value"
+ }
+ ],
+ "title": "value"
+ },
+ "pathComponents": [
+ "Outer",
+ "Inner1",
+ "DeeplyNested",
+ "value"
+ ]
+ },
+ {
+ "accessLevel": "private",
+ "declarationFragments": [
+ {
+ "kind": "keyword",
+ "spelling": "struct"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "Inner2"
+ },
+ {
+ "kind": "text",
+ "spelling": ";"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "c++",
+ "precise": "c:@S at Outer@S at Inner2"
+ },
+ "kind": {
+ "displayName": "Structure",
+ "identifier": "c++.struct"
+ },
+ "location": {
+ "position": {
+ "character": 9,
+ "line": 7
+ },
+ "uri": "file://INPUT_DIR/input.h"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "Inner2"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "Inner2"
+ }
+ ],
+ "title": "Inner2"
+ },
+ "pathComponents": [
+ "Outer",
+ "Inner2"
+ ]
+ },
+ {
+ "accessLevel": "public",
+ "declarationFragments": [
+ {
+ "kind": "keyword",
+ "spelling": "enum"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "Status"
+ },
+ {
+ "kind": "text",
+ "spelling": " : "
+ },
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:i",
+ "spelling": "unsigned int"
+ },
+ {
+ "kind": "text",
+ "spelling": ";"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "c++",
+ "precise": "c:@S at Outer@S at Inner2@E at Status"
+ },
+ "kind": {
+ "displayName": "Enumeration",
+ "identifier": "c++.enum"
+ },
+ "location": {
+ "position": {
+ "character": 9,
+ "line": 8
+ },
+ "uri": "file://INPUT_DIR/input.h"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "Status"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "Status"
+ }
+ ],
+ "title": "Status"
+ },
+ "pathComponents": [
+ "Outer",
+ "Inner2",
+ "Status"
+ ]
+ },
+ {
+ "accessLevel": "public",
+ "declarationFragments": [
+ {
+ "kind": "identifier",
+ "spelling": "READY"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "c++",
+ "precise": "c:@S at Outer@S at Inner2@E at Status@READY"
+ },
+ "kind": {
+ "displayName": "Enumeration Case",
+ "identifier": "c++.enum.case"
+ },
+ "location": {
+ "position": {
+ "character": 18,
+ "line": 8
+ },
+ "uri": "file://INPUT_DIR/input.h"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "READY"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "READY"
+ }
+ ],
+ "title": "READY"
+ },
+ "pathComponents": [
+ "Outer",
+ "Inner2",
+ "Status",
+ "READY"
+ ]
+ },
+ {
+ "accessLevel": "public",
+ "declarationFragments": [
+ {
+ "kind": "identifier",
+ "spelling": "PENDING"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "c++",
+ "precise": "c:@S at Outer@S at Inner2@E at Status@PENDING"
+ },
+ "kind": {
+ "displayName": "Enumeration Case",
+ "identifier": "c++.enum.case"
+ },
+ "location": {
+ "position": {
+ "character": 25,
+ "line": 8
+ },
+ "uri": "file://INPUT_DIR/input.h"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "PENDING"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "PENDING"
+ }
+ ],
+ "title": "PENDING"
+ },
+ "pathComponents": [
+ "Outer",
+ "Inner2",
+ "Status",
+ "PENDING"
+ ]
+ }
+ ]
+}
More information about the cfe-commits
mailing list