Skip to content

Commit

Permalink
[clang][extract-api] Add support for macros
Browse files Browse the repository at this point in the history
To achieve this we hook into the preprocessor during the
ExtractAPIAction and record definitions for macros that don't get
undefined during preprocessing.
  • Loading branch information
daniel-grumberg committed Mar 30, 2022
1 parent a9909d2 commit 529a057
Show file tree
Hide file tree
Showing 9 changed files with 877 additions and 79 deletions.
96 changes: 61 additions & 35 deletions clang/include/clang/ExtractAPI/API.h
Expand Up @@ -30,6 +30,7 @@
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Casting.h"
#include <memory>
#include <type_traits>

namespace clang {
namespace extractapi {
Expand All @@ -49,6 +50,9 @@ namespace extractapi {
/// \endcode
using DocComment = std::vector<RawComment::CommentLine>;

// Classes deriving from APIRecord need to have Name be the first constructor
// argument. This is so that they are compatible with `addTopLevelRecord`
// defined in API.cpp
/// The base representation of an API record. Holds common symbol information.
struct APIRecord {
StringRef Name;
Expand Down Expand Up @@ -83,6 +87,7 @@ struct APIRecord {
RK_ObjCMethod,
RK_ObjCInterface,
RK_ObjCProtocol,
RK_MacroDefinition,
};

private:
Expand Down Expand Up @@ -119,10 +124,11 @@ struct GlobalRecord : APIRecord {
/// The function signature of the record if it is a function.
FunctionSignature Signature;

GlobalRecord(GVKind Kind, StringRef Name, StringRef USR, PresumedLoc Loc,
GlobalRecord(StringRef Name, StringRef USR, PresumedLoc Loc,
const AvailabilityInfo &Availability, LinkageInfo Linkage,
const DocComment &Comment, DeclarationFragments Declaration,
DeclarationFragments SubHeading, FunctionSignature Signature)
DeclarationFragments SubHeading, GVKind Kind,
FunctionSignature Signature)
: APIRecord(RK_Global, Name, USR, Loc, Availability, Linkage, Comment,
Declaration, SubHeading),
GlobalKind(Kind), Signature(Signature) {}
Expand Down Expand Up @@ -374,6 +380,21 @@ struct ObjCProtocolRecord : ObjCContainerRecord {
virtual void anchor();
};

struct MacroDefinitionRecord : APIRecord {
MacroDefinitionRecord(StringRef Name, StringRef USR, PresumedLoc Loc,
DeclarationFragments Declaration,
DeclarationFragments SubHeading)
: APIRecord(RK_MacroDefinition, Name, USR, Loc, AvailabilityInfo(),
LinkageInfo(), {}, Declaration, SubHeading) {}

static bool classof(const APIRecord *Record) {
return Record->getKind() == RK_MacroDefinition;
}

private:
virtual void anchor();
};

/// APISet holds the set of API records collected from given inputs.
class APISet {
public:
Expand Down Expand Up @@ -530,45 +551,41 @@ class APISet {
DeclarationFragments Declaration,
DeclarationFragments SubHeading);

/// A map to store the set of GlobalRecord%s with the declaration name as the
/// key.
using GlobalRecordMap =
llvm::MapVector<StringRef, std::unique_ptr<GlobalRecord>>;

/// A map to store the set of EnumRecord%s with the declaration name as the
/// key.
using EnumRecordMap = llvm::MapVector<StringRef, std::unique_ptr<EnumRecord>>;

/// A map to store the set of StructRecord%s with the declaration name as the
/// key.
using StructRecordMap =
llvm::MapVector<StringRef, std::unique_ptr<StructRecord>>;

/// A map to store the set of ObjCInterfaceRecord%s with the declaration name
/// as the key.
using ObjCInterfaceRecordMap =
llvm::MapVector<StringRef, std::unique_ptr<ObjCInterfaceRecord>>;

/// A map to store the set of ObjCProtocolRecord%s with the declaration name
/// as the key.
using ObjCProtocolRecordMap =
llvm::MapVector<StringRef, std::unique_ptr<ObjCProtocolRecord>>;
/// Create a macro definition record into the API set.
///
/// Note: the caller is responsible for keeping the StringRef \p Name and
/// \p USR alive. APISet::copyString provides a way to copy strings into
/// APISet itself, and APISet::recordUSRForMacro(StringRef Name,
/// SourceLocation SL, const SourceManager &SM) is a helper method to generate
/// the USR for the macro and keep it alive in APISet.
MacroDefinitionRecord *addMacroDefinition(StringRef Name, StringRef USR,
PresumedLoc Loc,
DeclarationFragments Declaration,
DeclarationFragments SubHeading);

/// A mapping type to store a set of APIRecord%s with the declaration name as
/// the key.
template <typename RecordTy,
typename =
std::enable_if_t<std::is_base_of<APIRecord, RecordTy>::value>>
using RecordMap = llvm::MapVector<StringRef, std::unique_ptr<RecordTy>>;

/// Get the target triple for the ExtractAPI invocation.
const llvm::Triple &getTarget() const { return Target; }

/// Get the language used by the APIs.
Language getLanguage() const { return Lang; }

const GlobalRecordMap &getGlobals() const { return Globals; }
const EnumRecordMap &getEnums() const { return Enums; }
const StructRecordMap &getStructs() const { return Structs; }
const ObjCInterfaceRecordMap &getObjCInterfaces() const {
const RecordMap<GlobalRecord> &getGlobals() const { return Globals; }
const RecordMap<EnumRecord> &getEnums() const { return Enums; }
const RecordMap<StructRecord> &getStructs() const { return Structs; }
const RecordMap<ObjCInterfaceRecord> &getObjCInterfaces() const {
return ObjCInterfaces;
}
const ObjCProtocolRecordMap &getObjCProtocols() const {
const RecordMap<ObjCProtocolRecord> &getObjCProtocols() const {
return ObjCProtocols;
}
const RecordMap<MacroDefinitionRecord> &getMacros() const { return Macros; }

/// Generate and store the USR of declaration \p D.
///
Expand All @@ -577,6 +594,14 @@ class APISet {
/// \returns a StringRef of the generated USR string.
StringRef recordUSR(const Decl *D);

/// Generate and store the USR for a macro \p Name.
///
/// Note: The USR string is stored in and owned by Allocator.
///
/// \returns a StringRef to the generate USR string.
StringRef recordUSRForMacro(StringRef Name, SourceLocation SL,
const SourceManager &SM);

/// Copy \p String into the Allocator in this APISet.
///
/// \returns a StringRef of the copied string in APISet::Allocator.
Expand All @@ -594,11 +619,12 @@ class APISet {
const llvm::Triple Target;
const Language Lang;

GlobalRecordMap Globals;
EnumRecordMap Enums;
StructRecordMap Structs;
ObjCInterfaceRecordMap ObjCInterfaces;
ObjCProtocolRecordMap ObjCProtocols;
RecordMap<GlobalRecord> Globals;
RecordMap<EnumRecord> Enums;
RecordMap<StructRecord> Structs;
RecordMap<ObjCInterfaceRecord> ObjCInterfaces;
RecordMap<ObjCProtocolRecord> ObjCProtocols;
RecordMap<MacroDefinitionRecord> Macros;
};

} // namespace extractapi
Expand Down
11 changes: 11 additions & 0 deletions clang/include/clang/ExtractAPI/DeclarationFragments.h
Expand Up @@ -22,6 +22,7 @@
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
#include "clang/Lex/MacroInfo.h"
#include "llvm/ADT/StringRef.h"
#include <vector>

Expand Down Expand Up @@ -222,12 +223,22 @@ class DeclarationFragmentsBuilder {
static DeclarationFragments
getFragmentsForObjCProtocol(const ObjCProtocolDecl *);

/// Build DeclarationFragments for a macro.
///
/// \param Name name of the macro.
/// \param MD the associated MacroDirective.
static DeclarationFragments getFragmentsForMacro(StringRef Name,
const MacroDirective *MD);

/// Build sub-heading fragments for a NamedDecl.
static DeclarationFragments getSubHeading(const NamedDecl *);

/// Build sub-heading fragments for an Objective-C method.
static DeclarationFragments getSubHeading(const ObjCMethodDecl *);

/// Build a sub-heading for macro \p Name.
static DeclarationFragments getSubHeadingForMacro(StringRef Name);

/// Build FunctionSignature for a function-like declaration \c FunctionT like
/// FunctionDecl or ObjCMethodDecl.
///
Expand Down
Expand Up @@ -123,6 +123,9 @@ class SymbolGraphSerializer : public APISerializer {
/// Serialize an Objective-C container record.
void serializeObjCContainerRecord(const ObjCContainerRecord &Record);

/// Serialize a macro defintion record.
void serializeMacroDefinitionRecord(const MacroDefinitionRecord &Record);

public:
SymbolGraphSerializer(const APISet &API, StringRef ProductName,
APISerializerOption Options = {})
Expand Down
87 changes: 45 additions & 42 deletions clang/lib/ExtractAPI/API.cpp
Expand Up @@ -17,27 +17,39 @@
#include "clang/AST/CommentLexer.h"
#include "clang/AST/RawCommentList.h"
#include "clang/Index/USRGeneration.h"
#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/ADT/StringRef.h"
#include <memory>

using namespace clang::extractapi;
using namespace llvm;

namespace {

template <typename RecordTy, typename... CtorArgsTy>
RecordTy *addTopLevelRecord(APISet::RecordMap<RecordTy> &RecordMap,
StringRef Name, CtorArgsTy &&...CtorArgs) {
auto Result = RecordMap.insert({Name, nullptr});

// Create the record if it does not already exist
if (Result.second)
Result.first->second =
std::make_unique<RecordTy>(Name, std::forward<CtorArgsTy>(CtorArgs)...);

return Result.first->second.get();
}

} // namespace

GlobalRecord *APISet::addGlobal(GVKind Kind, StringRef Name, StringRef USR,
PresumedLoc Loc,
const AvailabilityInfo &Availability,
LinkageInfo Linkage, const DocComment &Comment,
DeclarationFragments Fragments,
DeclarationFragments SubHeading,
FunctionSignature Signature) {
auto Result = Globals.insert({Name, nullptr});
if (Result.second) {
// Create the record if it does not already exist.
auto Record = std::make_unique<GlobalRecord>(
Kind, Name, USR, Loc, Availability, Linkage, Comment, Fragments,
SubHeading, Signature);
Result.first->second = std::move(Record);
}
return Result.first->second.get();
return addTopLevelRecord(Globals, Name, USR, Loc, Availability, Linkage,
Comment, Fragments, SubHeading, Kind, Signature);
}

GlobalRecord *
Expand Down Expand Up @@ -73,14 +85,8 @@ EnumRecord *APISet::addEnum(StringRef Name, StringRef USR, PresumedLoc Loc,
const DocComment &Comment,
DeclarationFragments Declaration,
DeclarationFragments SubHeading) {
auto Result = Enums.insert({Name, nullptr});
if (Result.second) {
// Create the record if it does not already exist.
auto Record = std::make_unique<EnumRecord>(
Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
Result.first->second = std::move(Record);
}
return Result.first->second.get();
return addTopLevelRecord(Enums, Name, USR, Loc, Availability, Comment,
Declaration, SubHeading);
}

StructFieldRecord *APISet::addStructField(StructRecord *Struct, StringRef Name,
Expand All @@ -99,30 +105,18 @@ StructRecord *APISet::addStruct(StringRef Name, StringRef USR, PresumedLoc Loc,
const DocComment &Comment,
DeclarationFragments Declaration,
DeclarationFragments SubHeading) {
auto Result = Structs.insert({Name, nullptr});
if (Result.second) {
// Create the record if it does not already exist.
auto Record = std::make_unique<StructRecord>(
Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
Result.first->second = std::move(Record);
}
return Result.first->second.get();
return addTopLevelRecord(Structs, Name, USR, Loc, Availability, Comment,
Declaration, SubHeading);
}

ObjCInterfaceRecord *APISet::addObjCInterface(
StringRef Name, StringRef USR, PresumedLoc Loc,
const AvailabilityInfo &Availability, LinkageInfo Linkage,
const DocComment &Comment, DeclarationFragments Declaration,
DeclarationFragments SubHeading, SymbolReference SuperClass) {
auto Result = ObjCInterfaces.insert({Name, nullptr});
if (Result.second) {
// Create the record if it does not already exist.
auto Record = std::make_unique<ObjCInterfaceRecord>(
Name, USR, Loc, Availability, Linkage, Comment, Declaration, SubHeading,
SuperClass);
Result.first->second = std::move(Record);
}
return Result.first->second.get();
return addTopLevelRecord(ObjCInterfaces, Name, USR, Loc, Availability,
Linkage, Comment, Declaration, SubHeading,
SuperClass);
}

ObjCMethodRecord *APISet::addObjCMethod(
Expand Down Expand Up @@ -165,14 +159,15 @@ ObjCProtocolRecord *APISet::addObjCProtocol(
StringRef Name, StringRef USR, PresumedLoc Loc,
const AvailabilityInfo &Availability, const DocComment &Comment,
DeclarationFragments Declaration, DeclarationFragments SubHeading) {
auto Result = ObjCProtocols.insert({Name, nullptr});
if (Result.second) {
// Create the record if it does not already exist.
auto Record = std::make_unique<ObjCProtocolRecord>(
Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
Result.first->second = std::move(Record);
}
return Result.first->second.get();
return addTopLevelRecord(ObjCProtocols, Name, USR, Loc, Availability, Comment,
Declaration, SubHeading);
}

MacroDefinitionRecord *
APISet::addMacroDefinition(StringRef Name, StringRef USR, PresumedLoc Loc,
DeclarationFragments Declaration,
DeclarationFragments SubHeading) {
return addTopLevelRecord(Macros, Name, USR, Loc, Declaration, SubHeading);
}

StringRef APISet::recordUSR(const Decl *D) {
Expand All @@ -181,6 +176,13 @@ StringRef APISet::recordUSR(const Decl *D) {
return copyString(USR);
}

StringRef APISet::recordUSRForMacro(StringRef Name, SourceLocation SL,
const SourceManager &SM) {
SmallString<128> USR;
index::generateUSRForMacro(Name, SL, SM, USR);
return copyString(USR);
}

StringRef APISet::copyString(StringRef String) {
if (String.empty())
return {};
Expand Down Expand Up @@ -208,3 +210,4 @@ void ObjCInstanceVariableRecord::anchor() {}
void ObjCMethodRecord::anchor() {}
void ObjCInterfaceRecord::anchor() {}
void ObjCProtocolRecord::anchor() {}
void MacroDefinitionRecord::anchor() {}

0 comments on commit 529a057

Please sign in to comment.