1 //===- ExtractAPI/ExtractAPIVisitor.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 ExtractAPIVisitor an ASTVisitor to collect API
11 /// information.
12 ///
13 //===----------------------------------------------------------------------===//
14 
15 #include "clang/ExtractAPI/ExtractAPIVisitor.h"
16 
17 #include "TypedefUnderlyingTypeResolver.h"
18 #include "clang/AST/ASTConsumer.h"
19 #include "clang/AST/ASTContext.h"
20 #include "clang/AST/Decl.h"
21 #include "clang/AST/DeclCXX.h"
22 #include "clang/AST/ParentMapContext.h"
23 #include "clang/AST/RawCommentList.h"
24 #include "clang/Basic/SourceLocation.h"
25 #include "clang/Basic/SourceManager.h"
26 #include "clang/Basic/TargetInfo.h"
27 #include "clang/ExtractAPI/API.h"
28 #include "clang/ExtractAPI/AvailabilityInfo.h"
29 #include "clang/ExtractAPI/DeclarationFragments.h"
30 #include "clang/Frontend/ASTConsumers.h"
31 #include "clang/Frontend/FrontendOptions.h"
32 #include "llvm/Support/raw_ostream.h"
33 
34 using namespace clang;
35 using namespace extractapi;
36 
37 namespace {
38 
39 StringRef getTypedefName(const TagDecl *Decl) {
40   if (const auto *TypedefDecl = Decl->getTypedefNameForAnonDecl())
41     return TypedefDecl->getName();
42 
43   return {};
44 }
45 
46 template <class DeclTy>
47 bool isInSystemHeader(const ASTContext &Context, const DeclTy *D) {
48   return Context.getSourceManager().isInSystemHeader(D->getLocation());
49 }
50 
51 } // namespace
52 
53 bool ExtractAPIVisitor::VisitVarDecl(const VarDecl *Decl) {
54   // skip function parameters.
55   if (isa<ParmVarDecl>(Decl))
56     return true;
57 
58   // Skip non-global variables in records (struct/union/class).
59   if (Decl->getDeclContext()->isRecord())
60     return true;
61 
62   // Skip local variables inside function or method.
63   if (!Decl->isDefinedOutsideFunctionOrMethod())
64     return true;
65 
66   // If this is a template but not specialization or instantiation, skip.
67   if (Decl->getASTContext().getTemplateOrSpecializationInfo(Decl) &&
68       Decl->getTemplateSpecializationKind() == TSK_Undeclared)
69     return true;
70 
71   if (!LocationChecker(Decl->getLocation()))
72     return true;
73 
74   // Collect symbol information.
75   StringRef Name = Decl->getName();
76   StringRef USR = API.recordUSR(Decl);
77   PresumedLoc Loc =
78       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
79   LinkageInfo Linkage = Decl->getLinkageAndVisibility();
80   DocComment Comment;
81   if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
82     Comment = RawComment->getFormattedLines(Context.getSourceManager(),
83                                             Context.getDiagnostics());
84 
85   // Build declaration fragments and sub-heading for the variable.
86   DeclarationFragments Declaration =
87       DeclarationFragmentsBuilder::getFragmentsForVar(Decl);
88   DeclarationFragments SubHeading =
89       DeclarationFragmentsBuilder::getSubHeading(Decl);
90 
91   // Add the global variable record to the API set.
92   API.addGlobalVar(Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment,
93                    Declaration, SubHeading, isInSystemHeader(Context, Decl));
94   return true;
95 }
96 
97 bool ExtractAPIVisitor::VisitFunctionDecl(const FunctionDecl *Decl) {
98   if (const auto *Method = dyn_cast<CXXMethodDecl>(Decl)) {
99     // Skip member function in class templates.
100     if (Method->getParent()->getDescribedClassTemplate() != nullptr)
101       return true;
102 
103     // Skip methods in records.
104     for (auto P : Context.getParents(*Method)) {
105       if (P.get<CXXRecordDecl>())
106         return true;
107     }
108 
109     // Skip ConstructorDecl and DestructorDecl.
110     if (isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(Method))
111       return true;
112   }
113 
114   // Skip templated functions.
115   switch (Decl->getTemplatedKind()) {
116   case FunctionDecl::TK_NonTemplate:
117   case FunctionDecl::TK_DependentNonTemplate:
118     break;
119   case FunctionDecl::TK_MemberSpecialization:
120   case FunctionDecl::TK_FunctionTemplateSpecialization:
121     if (auto *TemplateInfo = Decl->getTemplateSpecializationInfo()) {
122       if (!TemplateInfo->isExplicitInstantiationOrSpecialization())
123         return true;
124     }
125     break;
126   case FunctionDecl::TK_FunctionTemplate:
127   case FunctionDecl::TK_DependentFunctionTemplateSpecialization:
128     return true;
129   }
130 
131   if (!LocationChecker(Decl->getLocation()))
132     return true;
133 
134   // Collect symbol information.
135   StringRef Name = Decl->getName();
136   StringRef USR = API.recordUSR(Decl);
137   PresumedLoc Loc =
138       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
139   LinkageInfo Linkage = Decl->getLinkageAndVisibility();
140   DocComment Comment;
141   if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
142     Comment = RawComment->getFormattedLines(Context.getSourceManager(),
143                                             Context.getDiagnostics());
144 
145   // Build declaration fragments, sub-heading, and signature of the function.
146   DeclarationFragments Declaration =
147       DeclarationFragmentsBuilder::getFragmentsForFunction(Decl);
148   DeclarationFragments SubHeading =
149       DeclarationFragmentsBuilder::getSubHeading(Decl);
150   FunctionSignature Signature =
151       DeclarationFragmentsBuilder::getFunctionSignature(Decl);
152 
153   // Add the function record to the API set.
154   API.addGlobalFunction(Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment,
155                         Declaration, SubHeading, Signature,
156                         isInSystemHeader(Context, Decl));
157   return true;
158 }
159 
160 bool ExtractAPIVisitor::VisitEnumDecl(const EnumDecl *Decl) {
161   if (!Decl->isComplete())
162     return true;
163 
164   // Skip forward declaration.
165   if (!Decl->isThisDeclarationADefinition())
166     return true;
167 
168   if (!LocationChecker(Decl->getLocation()))
169     return true;
170 
171   SmallString<128> QualifiedNameBuffer;
172   // Collect symbol information.
173   StringRef Name = Decl->getName();
174   if (Name.empty())
175     Name = getTypedefName(Decl);
176   if (Name.empty()) {
177     llvm::raw_svector_ostream OS(QualifiedNameBuffer);
178     Decl->printQualifiedName(OS);
179     Name = QualifiedNameBuffer.str();
180   }
181 
182   StringRef USR = API.recordUSR(Decl);
183   PresumedLoc Loc =
184       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
185   DocComment Comment;
186   if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
187     Comment = RawComment->getFormattedLines(Context.getSourceManager(),
188                                             Context.getDiagnostics());
189 
190   // Build declaration fragments and sub-heading for the enum.
191   DeclarationFragments Declaration =
192       DeclarationFragmentsBuilder::getFragmentsForEnum(Decl);
193   DeclarationFragments SubHeading =
194       DeclarationFragmentsBuilder::getSubHeading(Decl);
195 
196   EnumRecord *EnumRecord = API.addEnum(
197       API.copyString(Name), USR, Loc, AvailabilitySet(Decl), Comment,
198       Declaration, SubHeading, isInSystemHeader(Context, Decl));
199 
200   // Now collect information about the enumerators in this enum.
201   recordEnumConstants(EnumRecord, Decl->enumerators());
202 
203   return true;
204 }
205 
206 bool ExtractAPIVisitor::VisitRecordDecl(const RecordDecl *Decl) {
207   if (!Decl->isCompleteDefinition())
208     return true;
209 
210   // Skip C++ structs/classes/unions
211   // TODO: support C++ records
212   if (isa<CXXRecordDecl>(Decl))
213     return true;
214 
215   if (!LocationChecker(Decl->getLocation()))
216     return true;
217 
218   // Collect symbol information.
219   StringRef Name = Decl->getName();
220   if (Name.empty())
221     Name = getTypedefName(Decl);
222   if (Name.empty())
223     return true;
224 
225   StringRef USR = API.recordUSR(Decl);
226   PresumedLoc Loc =
227       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
228   DocComment Comment;
229   if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
230     Comment = RawComment->getFormattedLines(Context.getSourceManager(),
231                                             Context.getDiagnostics());
232 
233   // Build declaration fragments and sub-heading for the struct.
234   DeclarationFragments Declaration =
235       DeclarationFragmentsBuilder::getFragmentsForStruct(Decl);
236   DeclarationFragments SubHeading =
237       DeclarationFragmentsBuilder::getSubHeading(Decl);
238 
239   StructRecord *StructRecord =
240       API.addStruct(Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration,
241                     SubHeading, isInSystemHeader(Context, Decl));
242 
243   // Now collect information about the fields in this struct.
244   recordStructFields(StructRecord, Decl->fields());
245 
246   return true;
247 }
248 
249 bool ExtractAPIVisitor::VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl) {
250   // Skip forward declaration for classes (@class)
251   if (!Decl->isThisDeclarationADefinition())
252     return true;
253 
254   if (!LocationChecker(Decl->getLocation()))
255     return true;
256 
257   // Collect symbol information.
258   StringRef Name = Decl->getName();
259   StringRef USR = API.recordUSR(Decl);
260   PresumedLoc Loc =
261       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
262   LinkageInfo Linkage = Decl->getLinkageAndVisibility();
263   DocComment Comment;
264   if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
265     Comment = RawComment->getFormattedLines(Context.getSourceManager(),
266                                             Context.getDiagnostics());
267 
268   // Build declaration fragments and sub-heading for the interface.
269   DeclarationFragments Declaration =
270       DeclarationFragmentsBuilder::getFragmentsForObjCInterface(Decl);
271   DeclarationFragments SubHeading =
272       DeclarationFragmentsBuilder::getSubHeading(Decl);
273 
274   // Collect super class information.
275   SymbolReference SuperClass;
276   if (const auto *SuperClassDecl = Decl->getSuperClass()) {
277     SuperClass.Name = SuperClassDecl->getObjCRuntimeNameAsString();
278     SuperClass.USR = API.recordUSR(SuperClassDecl);
279   }
280 
281   ObjCInterfaceRecord *ObjCInterfaceRecord = API.addObjCInterface(
282       Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment, Declaration,
283       SubHeading, SuperClass, isInSystemHeader(Context, Decl));
284 
285   // Record all methods (selectors). This doesn't include automatically
286   // synthesized property methods.
287   recordObjCMethods(ObjCInterfaceRecord, Decl->methods());
288   recordObjCProperties(ObjCInterfaceRecord, Decl->properties());
289   recordObjCInstanceVariables(ObjCInterfaceRecord, Decl->ivars());
290   recordObjCProtocols(ObjCInterfaceRecord, Decl->protocols());
291 
292   return true;
293 }
294 
295 bool ExtractAPIVisitor::VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl) {
296   // Skip forward declaration for protocols (@protocol).
297   if (!Decl->isThisDeclarationADefinition())
298     return true;
299 
300   if (!LocationChecker(Decl->getLocation()))
301     return true;
302 
303   // Collect symbol information.
304   StringRef Name = Decl->getName();
305   StringRef USR = API.recordUSR(Decl);
306   PresumedLoc Loc =
307       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
308   DocComment Comment;
309   if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
310     Comment = RawComment->getFormattedLines(Context.getSourceManager(),
311                                             Context.getDiagnostics());
312 
313   // Build declaration fragments and sub-heading for the protocol.
314   DeclarationFragments Declaration =
315       DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(Decl);
316   DeclarationFragments SubHeading =
317       DeclarationFragmentsBuilder::getSubHeading(Decl);
318 
319   ObjCProtocolRecord *ObjCProtocolRecord = API.addObjCProtocol(
320       Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration, SubHeading,
321       isInSystemHeader(Context, Decl));
322 
323   recordObjCMethods(ObjCProtocolRecord, Decl->methods());
324   recordObjCProperties(ObjCProtocolRecord, Decl->properties());
325   recordObjCProtocols(ObjCProtocolRecord, Decl->protocols());
326 
327   return true;
328 }
329 
330 bool ExtractAPIVisitor::VisitTypedefNameDecl(const TypedefNameDecl *Decl) {
331   // Skip ObjC Type Parameter for now.
332   if (isa<ObjCTypeParamDecl>(Decl))
333     return true;
334 
335   if (!Decl->isDefinedOutsideFunctionOrMethod())
336     return true;
337 
338   if (!LocationChecker(Decl->getLocation()))
339     return true;
340 
341   PresumedLoc Loc =
342       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
343   StringRef Name = Decl->getName();
344   StringRef USR = API.recordUSR(Decl);
345   DocComment Comment;
346   if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
347     Comment = RawComment->getFormattedLines(Context.getSourceManager(),
348                                             Context.getDiagnostics());
349 
350   QualType Type = Decl->getUnderlyingType();
351   SymbolReference SymRef =
352       TypedefUnderlyingTypeResolver(Context).getSymbolReferenceForType(Type,
353                                                                        API);
354 
355   API.addTypedef(Name, USR, Loc, AvailabilitySet(Decl), Comment,
356                  DeclarationFragmentsBuilder::getFragmentsForTypedef(Decl),
357                  DeclarationFragmentsBuilder::getSubHeading(Decl), SymRef,
358                  isInSystemHeader(Context, Decl));
359 
360   return true;
361 }
362 
363 bool ExtractAPIVisitor::VisitObjCCategoryDecl(const ObjCCategoryDecl *Decl) {
364   // Collect symbol information.
365   StringRef Name = Decl->getName();
366   StringRef USR = API.recordUSR(Decl);
367   PresumedLoc Loc =
368       Context.getSourceManager().getPresumedLoc(Decl->getLocation());
369   DocComment Comment;
370   if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
371     Comment = RawComment->getFormattedLines(Context.getSourceManager(),
372                                             Context.getDiagnostics());
373   // Build declaration fragments and sub-heading for the category.
374   DeclarationFragments Declaration =
375       DeclarationFragmentsBuilder::getFragmentsForObjCCategory(Decl);
376   DeclarationFragments SubHeading =
377       DeclarationFragmentsBuilder::getSubHeading(Decl);
378 
379   const ObjCInterfaceDecl *InterfaceDecl = Decl->getClassInterface();
380   SymbolReference Interface(InterfaceDecl->getName(),
381                             API.recordUSR(InterfaceDecl));
382 
383   ObjCCategoryRecord *ObjCCategoryRecord = API.addObjCCategory(
384       Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration, SubHeading,
385       Interface, isInSystemHeader(Context, Decl));
386 
387   recordObjCMethods(ObjCCategoryRecord, Decl->methods());
388   recordObjCProperties(ObjCCategoryRecord, Decl->properties());
389   recordObjCInstanceVariables(ObjCCategoryRecord, Decl->ivars());
390   recordObjCProtocols(ObjCCategoryRecord, Decl->protocols());
391 
392   return true;
393 }
394 
395 /// Collect API information for the enum constants and associate with the
396 /// parent enum.
397 void ExtractAPIVisitor::recordEnumConstants(
398     EnumRecord *EnumRecord, const EnumDecl::enumerator_range Constants) {
399   for (const auto *Constant : Constants) {
400     // Collect symbol information.
401     StringRef Name = Constant->getName();
402     StringRef USR = API.recordUSR(Constant);
403     PresumedLoc Loc =
404         Context.getSourceManager().getPresumedLoc(Constant->getLocation());
405     DocComment Comment;
406     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Constant))
407       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
408                                               Context.getDiagnostics());
409 
410     // Build declaration fragments and sub-heading for the enum constant.
411     DeclarationFragments Declaration =
412         DeclarationFragmentsBuilder::getFragmentsForEnumConstant(Constant);
413     DeclarationFragments SubHeading =
414         DeclarationFragmentsBuilder::getSubHeading(Constant);
415 
416     API.addEnumConstant(EnumRecord, Name, USR, Loc, AvailabilitySet(Constant),
417                         Comment, Declaration, SubHeading,
418                         isInSystemHeader(Context, Constant));
419   }
420 }
421 
422 /// Collect API information for the struct fields and associate with the
423 /// parent struct.
424 void ExtractAPIVisitor::recordStructFields(
425     StructRecord *StructRecord, const RecordDecl::field_range Fields) {
426   for (const auto *Field : Fields) {
427     // Collect symbol information.
428     StringRef Name = Field->getName();
429     StringRef USR = API.recordUSR(Field);
430     PresumedLoc Loc =
431         Context.getSourceManager().getPresumedLoc(Field->getLocation());
432     DocComment Comment;
433     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Field))
434       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
435                                               Context.getDiagnostics());
436 
437     // Build declaration fragments and sub-heading for the struct field.
438     DeclarationFragments Declaration =
439         DeclarationFragmentsBuilder::getFragmentsForField(Field);
440     DeclarationFragments SubHeading =
441         DeclarationFragmentsBuilder::getSubHeading(Field);
442 
443     API.addStructField(StructRecord, Name, USR, Loc, AvailabilitySet(Field),
444                        Comment, Declaration, SubHeading,
445                        isInSystemHeader(Context, Field));
446   }
447 }
448 
449 /// Collect API information for the Objective-C methods and associate with the
450 /// parent container.
451 void ExtractAPIVisitor::recordObjCMethods(
452     ObjCContainerRecord *Container,
453     const ObjCContainerDecl::method_range Methods) {
454   for (const auto *Method : Methods) {
455     // Don't record selectors for properties.
456     if (Method->isPropertyAccessor())
457       continue;
458 
459     StringRef Name = API.copyString(Method->getSelector().getAsString());
460     StringRef USR = API.recordUSR(Method);
461     PresumedLoc Loc =
462         Context.getSourceManager().getPresumedLoc(Method->getLocation());
463     DocComment Comment;
464     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Method))
465       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
466                                               Context.getDiagnostics());
467 
468     // Build declaration fragments, sub-heading, and signature for the method.
469     DeclarationFragments Declaration =
470         DeclarationFragmentsBuilder::getFragmentsForObjCMethod(Method);
471     DeclarationFragments SubHeading =
472         DeclarationFragmentsBuilder::getSubHeading(Method);
473     FunctionSignature Signature =
474         DeclarationFragmentsBuilder::getFunctionSignature(Method);
475 
476     API.addObjCMethod(Container, Name, USR, Loc, AvailabilitySet(Method),
477                       Comment, Declaration, SubHeading, Signature,
478                       Method->isInstanceMethod(),
479                       isInSystemHeader(Context, Method));
480   }
481 }
482 
483 void ExtractAPIVisitor::recordObjCProperties(
484     ObjCContainerRecord *Container,
485     const ObjCContainerDecl::prop_range Properties) {
486   for (const auto *Property : Properties) {
487     StringRef Name = Property->getName();
488     StringRef USR = API.recordUSR(Property);
489     PresumedLoc Loc =
490         Context.getSourceManager().getPresumedLoc(Property->getLocation());
491     DocComment Comment;
492     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Property))
493       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
494                                               Context.getDiagnostics());
495 
496     // Build declaration fragments and sub-heading for the property.
497     DeclarationFragments Declaration =
498         DeclarationFragmentsBuilder::getFragmentsForObjCProperty(Property);
499     DeclarationFragments SubHeading =
500         DeclarationFragmentsBuilder::getSubHeading(Property);
501 
502     StringRef GetterName =
503         API.copyString(Property->getGetterName().getAsString());
504     StringRef SetterName =
505         API.copyString(Property->getSetterName().getAsString());
506 
507     // Get the attributes for property.
508     unsigned Attributes = ObjCPropertyRecord::NoAttr;
509     if (Property->getPropertyAttributes() &
510         ObjCPropertyAttribute::kind_readonly)
511       Attributes |= ObjCPropertyRecord::ReadOnly;
512 
513     API.addObjCProperty(
514         Container, Name, USR, Loc, AvailabilitySet(Property), Comment,
515         Declaration, SubHeading,
516         static_cast<ObjCPropertyRecord::AttributeKind>(Attributes), GetterName,
517         SetterName, Property->isOptional(),
518         !(Property->getPropertyAttributes() &
519           ObjCPropertyAttribute::kind_class),
520         isInSystemHeader(Context, Property));
521   }
522 }
523 
524 void ExtractAPIVisitor::recordObjCInstanceVariables(
525     ObjCContainerRecord *Container,
526     const llvm::iterator_range<
527         DeclContext::specific_decl_iterator<ObjCIvarDecl>>
528         Ivars) {
529   for (const auto *Ivar : Ivars) {
530     StringRef Name = Ivar->getName();
531     StringRef USR = API.recordUSR(Ivar);
532     PresumedLoc Loc =
533         Context.getSourceManager().getPresumedLoc(Ivar->getLocation());
534     DocComment Comment;
535     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Ivar))
536       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
537                                               Context.getDiagnostics());
538 
539     // Build declaration fragments and sub-heading for the instance variable.
540     DeclarationFragments Declaration =
541         DeclarationFragmentsBuilder::getFragmentsForField(Ivar);
542     DeclarationFragments SubHeading =
543         DeclarationFragmentsBuilder::getSubHeading(Ivar);
544 
545     ObjCInstanceVariableRecord::AccessControl Access =
546         Ivar->getCanonicalAccessControl();
547 
548     API.addObjCInstanceVariable(
549         Container, Name, USR, Loc, AvailabilitySet(Ivar), Comment, Declaration,
550         SubHeading, Access, isInSystemHeader(Context, Ivar));
551   }
552 }
553 
554 void ExtractAPIVisitor::recordObjCProtocols(
555     ObjCContainerRecord *Container,
556     ObjCInterfaceDecl::protocol_range Protocols) {
557   for (const auto *Protocol : Protocols)
558     Container->Protocols.emplace_back(Protocol->getName(),
559                                       API.recordUSR(Protocol));
560 }
561