1 //===- ExtractAPI/API.cpp ---------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 /// This file implements the APIRecord and derived record structs,
11 /// and the APISet class.
12 ///
13 //===----------------------------------------------------------------------===//
14
15 #include "clang/ExtractAPI/API.h"
16 #include "clang/AST/CommentCommandTraits.h"
17 #include "clang/AST/CommentLexer.h"
18 #include "clang/AST/RawCommentList.h"
19 #include "clang/Index/USRGeneration.h"
20 #include "llvm/ADT/STLFunctionalExtras.h"
21 #include "llvm/ADT/StringRef.h"
22 #include <memory>
23
24 using namespace clang::extractapi;
25 using namespace llvm;
26
27 namespace {
28
29 template <typename RecordTy, typename... CtorArgsTy>
30 RecordTy *addTopLevelRecord(DenseMap<StringRef, APIRecord *> &USRLookupTable,
31 APISet::RecordMap<RecordTy> &RecordMap,
32 StringRef USR, CtorArgsTy &&...CtorArgs) {
33 auto Result = RecordMap.insert({USR, nullptr});
34
35 // Create the record if it does not already exist
36 if (Result.second)
37 Result.first->second =
38 std::make_unique<RecordTy>(USR, std::forward<CtorArgsTy>(CtorArgs)...);
39
40 auto *Record = Result.first->second.get();
41 USRLookupTable.insert({USR, Record});
42 return Record;
43 }
44
45 } // namespace
46
47 GlobalVariableRecord *
addGlobalVar(StringRef Name,StringRef USR,PresumedLoc Loc,AvailabilitySet Availabilities,LinkageInfo Linkage,const DocComment & Comment,DeclarationFragments Fragments,DeclarationFragments SubHeading,bool IsFromSystemHeader)48 APISet::addGlobalVar(StringRef Name, StringRef USR, PresumedLoc Loc,
49 AvailabilitySet Availabilities, LinkageInfo Linkage,
50 const DocComment &Comment, DeclarationFragments Fragments,
51 DeclarationFragments SubHeading, bool IsFromSystemHeader) {
52 return addTopLevelRecord(USRBasedLookupTable, GlobalVariables, USR, Name, Loc,
53 std::move(Availabilities), Linkage, Comment,
54 Fragments, SubHeading, IsFromSystemHeader);
55 }
56
addGlobalFunction(StringRef Name,StringRef USR,PresumedLoc Loc,AvailabilitySet Availabilities,LinkageInfo Linkage,const DocComment & Comment,DeclarationFragments Fragments,DeclarationFragments SubHeading,FunctionSignature Signature,bool IsFromSystemHeader)57 GlobalFunctionRecord *APISet::addGlobalFunction(
58 StringRef Name, StringRef USR, PresumedLoc Loc,
59 AvailabilitySet Availabilities, LinkageInfo Linkage,
60 const DocComment &Comment, DeclarationFragments Fragments,
61 DeclarationFragments SubHeading, FunctionSignature Signature,
62 bool IsFromSystemHeader) {
63 return addTopLevelRecord(USRBasedLookupTable, GlobalFunctions, USR, Name, Loc,
64 std::move(Availabilities), Linkage, Comment,
65 Fragments, SubHeading, Signature,
66 IsFromSystemHeader);
67 }
68
addEnumConstant(EnumRecord * Enum,StringRef Name,StringRef USR,PresumedLoc Loc,AvailabilitySet Availabilities,const DocComment & Comment,DeclarationFragments Declaration,DeclarationFragments SubHeading,bool IsFromSystemHeader)69 EnumConstantRecord *APISet::addEnumConstant(EnumRecord *Enum, StringRef Name,
70 StringRef USR, PresumedLoc Loc,
71 AvailabilitySet Availabilities,
72 const DocComment &Comment,
73 DeclarationFragments Declaration,
74 DeclarationFragments SubHeading,
75 bool IsFromSystemHeader) {
76 auto Record = std::make_unique<EnumConstantRecord>(
77 USR, Name, Loc, std::move(Availabilities), Comment, Declaration,
78 SubHeading, IsFromSystemHeader);
79 Record->ParentInformation = APIRecord::HierarchyInformation(
80 Enum->USR, Enum->Name, Enum->getKind(), Enum);
81 USRBasedLookupTable.insert({USR, Record.get()});
82 return Enum->Constants.emplace_back(std::move(Record)).get();
83 }
84
addEnum(StringRef Name,StringRef USR,PresumedLoc Loc,AvailabilitySet Availabilities,const DocComment & Comment,DeclarationFragments Declaration,DeclarationFragments SubHeading,bool IsFromSystemHeader)85 EnumRecord *APISet::addEnum(StringRef Name, StringRef USR, PresumedLoc Loc,
86 AvailabilitySet Availabilities,
87 const DocComment &Comment,
88 DeclarationFragments Declaration,
89 DeclarationFragments SubHeading,
90 bool IsFromSystemHeader) {
91 return addTopLevelRecord(USRBasedLookupTable, Enums, USR, Name, Loc,
92 std::move(Availabilities), Comment, Declaration,
93 SubHeading, IsFromSystemHeader);
94 }
95
addStructField(StructRecord * Struct,StringRef Name,StringRef USR,PresumedLoc Loc,AvailabilitySet Availabilities,const DocComment & Comment,DeclarationFragments Declaration,DeclarationFragments SubHeading,bool IsFromSystemHeader)96 StructFieldRecord *APISet::addStructField(StructRecord *Struct, StringRef Name,
97 StringRef USR, PresumedLoc Loc,
98 AvailabilitySet Availabilities,
99 const DocComment &Comment,
100 DeclarationFragments Declaration,
101 DeclarationFragments SubHeading,
102 bool IsFromSystemHeader) {
103 auto Record = std::make_unique<StructFieldRecord>(
104 USR, Name, Loc, std::move(Availabilities), Comment, Declaration,
105 SubHeading, IsFromSystemHeader);
106 Record->ParentInformation = APIRecord::HierarchyInformation(
107 Struct->USR, Struct->Name, Struct->getKind(), Struct);
108 USRBasedLookupTable.insert({USR, Record.get()});
109 return Struct->Fields.emplace_back(std::move(Record)).get();
110 }
111
addStruct(StringRef Name,StringRef USR,PresumedLoc Loc,AvailabilitySet Availabilities,const DocComment & Comment,DeclarationFragments Declaration,DeclarationFragments SubHeading,bool IsFromSystemHeader)112 StructRecord *APISet::addStruct(StringRef Name, StringRef USR, PresumedLoc Loc,
113 AvailabilitySet Availabilities,
114 const DocComment &Comment,
115 DeclarationFragments Declaration,
116 DeclarationFragments SubHeading,
117 bool IsFromSystemHeader) {
118 return addTopLevelRecord(USRBasedLookupTable, Structs, USR, Name, Loc,
119 std::move(Availabilities), Comment, Declaration,
120 SubHeading, IsFromSystemHeader);
121 }
122
addObjCCategory(StringRef Name,StringRef USR,PresumedLoc Loc,AvailabilitySet Availabilities,const DocComment & Comment,DeclarationFragments Declaration,DeclarationFragments SubHeading,SymbolReference Interface,bool IsFromSystemHeader)123 ObjCCategoryRecord *APISet::addObjCCategory(
124 StringRef Name, StringRef USR, PresumedLoc Loc,
125 AvailabilitySet Availabilities, const DocComment &Comment,
126 DeclarationFragments Declaration, DeclarationFragments SubHeading,
127 SymbolReference Interface, bool IsFromSystemHeader) {
128 // Create the category record.
129 auto *Record =
130 addTopLevelRecord(USRBasedLookupTable, ObjCCategories, USR, Name, Loc,
131 std::move(Availabilities), Comment, Declaration,
132 SubHeading, Interface, IsFromSystemHeader);
133
134 // If this category is extending a known interface, associate it with the
135 // ObjCInterfaceRecord.
136 auto It = ObjCInterfaces.find(Interface.USR);
137 if (It != ObjCInterfaces.end())
138 It->second->Categories.push_back(Record);
139
140 return Record;
141 }
142
143 ObjCInterfaceRecord *
addObjCInterface(StringRef Name,StringRef USR,PresumedLoc Loc,AvailabilitySet Availabilities,LinkageInfo Linkage,const DocComment & Comment,DeclarationFragments Declaration,DeclarationFragments SubHeading,SymbolReference SuperClass,bool IsFromSystemHeader)144 APISet::addObjCInterface(StringRef Name, StringRef USR, PresumedLoc Loc,
145 AvailabilitySet Availabilities, LinkageInfo Linkage,
146 const DocComment &Comment,
147 DeclarationFragments Declaration,
148 DeclarationFragments SubHeading,
149 SymbolReference SuperClass, bool IsFromSystemHeader) {
150 return addTopLevelRecord(USRBasedLookupTable, ObjCInterfaces, USR, Name, Loc,
151 std::move(Availabilities), Linkage, Comment,
152 Declaration, SubHeading, SuperClass,
153 IsFromSystemHeader);
154 }
155
addObjCMethod(ObjCContainerRecord * Container,StringRef Name,StringRef USR,PresumedLoc Loc,AvailabilitySet Availabilities,const DocComment & Comment,DeclarationFragments Declaration,DeclarationFragments SubHeading,FunctionSignature Signature,bool IsInstanceMethod,bool IsFromSystemHeader)156 ObjCMethodRecord *APISet::addObjCMethod(
157 ObjCContainerRecord *Container, StringRef Name, StringRef USR,
158 PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment,
159 DeclarationFragments Declaration, DeclarationFragments SubHeading,
160 FunctionSignature Signature, bool IsInstanceMethod,
161 bool IsFromSystemHeader) {
162 std::unique_ptr<ObjCMethodRecord> Record;
163 if (IsInstanceMethod)
164 Record = std::make_unique<ObjCInstanceMethodRecord>(
165 USR, Name, Loc, std::move(Availabilities), Comment, Declaration,
166 SubHeading, Signature, IsFromSystemHeader);
167 else
168 Record = std::make_unique<ObjCClassMethodRecord>(
169 USR, Name, Loc, std::move(Availabilities), Comment, Declaration,
170 SubHeading, Signature, IsFromSystemHeader);
171
172 Record->ParentInformation = APIRecord::HierarchyInformation(
173 Container->USR, Container->Name, Container->getKind(), Container);
174 USRBasedLookupTable.insert({USR, Record.get()});
175 return Container->Methods.emplace_back(std::move(Record)).get();
176 }
177
addObjCProperty(ObjCContainerRecord * Container,StringRef Name,StringRef USR,PresumedLoc Loc,AvailabilitySet Availabilities,const DocComment & Comment,DeclarationFragments Declaration,DeclarationFragments SubHeading,ObjCPropertyRecord::AttributeKind Attributes,StringRef GetterName,StringRef SetterName,bool IsOptional,bool IsInstanceProperty,bool IsFromSystemHeader)178 ObjCPropertyRecord *APISet::addObjCProperty(
179 ObjCContainerRecord *Container, StringRef Name, StringRef USR,
180 PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment,
181 DeclarationFragments Declaration, DeclarationFragments SubHeading,
182 ObjCPropertyRecord::AttributeKind Attributes, StringRef GetterName,
183 StringRef SetterName, bool IsOptional, bool IsInstanceProperty,
184 bool IsFromSystemHeader) {
185 std::unique_ptr<ObjCPropertyRecord> Record;
186 if (IsInstanceProperty)
187 Record = std::make_unique<ObjCInstancePropertyRecord>(
188 USR, Name, Loc, std::move(Availabilities), Comment, Declaration,
189 SubHeading, Attributes, GetterName, SetterName, IsOptional,
190 IsFromSystemHeader);
191 else
192 Record = std::make_unique<ObjCClassPropertyRecord>(
193 USR, Name, Loc, std::move(Availabilities), Comment, Declaration,
194 SubHeading, Attributes, GetterName, SetterName, IsOptional,
195 IsFromSystemHeader);
196 Record->ParentInformation = APIRecord::HierarchyInformation(
197 Container->USR, Container->Name, Container->getKind(), Container);
198 USRBasedLookupTable.insert({USR, Record.get()});
199 return Container->Properties.emplace_back(std::move(Record)).get();
200 }
201
addObjCInstanceVariable(ObjCContainerRecord * Container,StringRef Name,StringRef USR,PresumedLoc Loc,AvailabilitySet Availabilities,const DocComment & Comment,DeclarationFragments Declaration,DeclarationFragments SubHeading,ObjCInstanceVariableRecord::AccessControl Access,bool IsFromSystemHeader)202 ObjCInstanceVariableRecord *APISet::addObjCInstanceVariable(
203 ObjCContainerRecord *Container, StringRef Name, StringRef USR,
204 PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment,
205 DeclarationFragments Declaration, DeclarationFragments SubHeading,
206 ObjCInstanceVariableRecord::AccessControl Access, bool IsFromSystemHeader) {
207 auto Record = std::make_unique<ObjCInstanceVariableRecord>(
208 USR, Name, Loc, std::move(Availabilities), Comment, Declaration,
209 SubHeading, Access, IsFromSystemHeader);
210 Record->ParentInformation = APIRecord::HierarchyInformation(
211 Container->USR, Container->Name, Container->getKind(), Container);
212 USRBasedLookupTable.insert({USR, Record.get()});
213 return Container->Ivars.emplace_back(std::move(Record)).get();
214 }
215
addObjCProtocol(StringRef Name,StringRef USR,PresumedLoc Loc,AvailabilitySet Availabilities,const DocComment & Comment,DeclarationFragments Declaration,DeclarationFragments SubHeading,bool IsFromSystemHeader)216 ObjCProtocolRecord *APISet::addObjCProtocol(StringRef Name, StringRef USR,
217 PresumedLoc Loc,
218 AvailabilitySet Availabilities,
219 const DocComment &Comment,
220 DeclarationFragments Declaration,
221 DeclarationFragments SubHeading,
222 bool IsFromSystemHeader) {
223 return addTopLevelRecord(USRBasedLookupTable, ObjCProtocols, USR, Name, Loc,
224 std::move(Availabilities), Comment, Declaration,
225 SubHeading, IsFromSystemHeader);
226 }
227
228 MacroDefinitionRecord *
addMacroDefinition(StringRef Name,StringRef USR,PresumedLoc Loc,DeclarationFragments Declaration,DeclarationFragments SubHeading,bool IsFromSystemHeader)229 APISet::addMacroDefinition(StringRef Name, StringRef USR, PresumedLoc Loc,
230 DeclarationFragments Declaration,
231 DeclarationFragments SubHeading,
232 bool IsFromSystemHeader) {
233 return addTopLevelRecord(USRBasedLookupTable, Macros, USR, Name, Loc,
234 Declaration, SubHeading, IsFromSystemHeader);
235 }
236
237 TypedefRecord *
addTypedef(StringRef Name,StringRef USR,PresumedLoc Loc,AvailabilitySet Availabilities,const DocComment & Comment,DeclarationFragments Declaration,DeclarationFragments SubHeading,SymbolReference UnderlyingType,bool IsFromSystemHeader)238 APISet::addTypedef(StringRef Name, StringRef USR, PresumedLoc Loc,
239 AvailabilitySet Availabilities, const DocComment &Comment,
240 DeclarationFragments Declaration,
241 DeclarationFragments SubHeading,
242 SymbolReference UnderlyingType, bool IsFromSystemHeader) {
243 return addTopLevelRecord(USRBasedLookupTable, Typedefs, USR, Name, Loc,
244 std::move(Availabilities), Comment, Declaration,
245 SubHeading, UnderlyingType, IsFromSystemHeader);
246 }
247
findRecordForUSR(StringRef USR) const248 APIRecord *APISet::findRecordForUSR(StringRef USR) const {
249 if (USR.empty())
250 return nullptr;
251
252 auto It = USRBasedLookupTable.find(USR);
253 if (It != USRBasedLookupTable.end())
254 return It->second;
255 return nullptr;
256 }
257
recordUSR(const Decl * D)258 StringRef APISet::recordUSR(const Decl *D) {
259 SmallString<128> USR;
260 index::generateUSRForDecl(D, USR);
261 return copyString(USR);
262 }
263
recordUSRForMacro(StringRef Name,SourceLocation SL,const SourceManager & SM)264 StringRef APISet::recordUSRForMacro(StringRef Name, SourceLocation SL,
265 const SourceManager &SM) {
266 SmallString<128> USR;
267 index::generateUSRForMacro(Name, SL, SM, USR);
268 return copyString(USR);
269 }
270
copyString(StringRef String)271 StringRef APISet::copyString(StringRef String) {
272 if (String.empty())
273 return {};
274
275 // No need to allocate memory and copy if the string has already been stored.
276 if (StringAllocator.identifyObject(String.data()))
277 return String;
278
279 void *Ptr = StringAllocator.Allocate(String.size(), 1);
280 memcpy(Ptr, String.data(), String.size());
281 return StringRef(reinterpret_cast<const char *>(Ptr), String.size());
282 }
283
~APIRecord()284 APIRecord::~APIRecord() {}
~ObjCContainerRecord()285 ObjCContainerRecord::~ObjCContainerRecord() {}
~ObjCMethodRecord()286 ObjCMethodRecord::~ObjCMethodRecord() {}
~ObjCPropertyRecord()287 ObjCPropertyRecord::~ObjCPropertyRecord() {}
288
anchor()289 void GlobalFunctionRecord::anchor() {}
anchor()290 void GlobalVariableRecord::anchor() {}
anchor()291 void EnumConstantRecord::anchor() {}
anchor()292 void EnumRecord::anchor() {}
anchor()293 void StructFieldRecord::anchor() {}
anchor()294 void StructRecord::anchor() {}
anchor()295 void ObjCInstancePropertyRecord::anchor() {}
anchor()296 void ObjCClassPropertyRecord::anchor() {}
anchor()297 void ObjCInstanceVariableRecord::anchor() {}
anchor()298 void ObjCInstanceMethodRecord::anchor() {}
anchor()299 void ObjCClassMethodRecord::anchor() {}
anchor()300 void ObjCCategoryRecord::anchor() {}
anchor()301 void ObjCInterfaceRecord::anchor() {}
anchor()302 void ObjCProtocolRecord::anchor() {}
anchor()303 void MacroDefinitionRecord::anchor() {}
anchor()304 void TypedefRecord::anchor() {}
305