37#include "llvm/ADT/DenseSet.h"
38#include "llvm/ADT/STLExtras.h"
39#include "llvm/ADT/SmallString.h"
40#include "llvm/ADT/SmallVector.h"
41#include "llvm/Support/Casting.h"
42#include "llvm/Support/Error.h"
43#include "llvm/Support/FileSystem.h"
44#include "llvm/Support/MemoryBuffer.h"
45#include "llvm/Support/Path.h"
46#include "llvm/Support/Regex.h"
47#include "llvm/Support/raw_ostream.h"
53using namespace extractapi;
59 bool *IsQuoted =
nullptr) {
61 "CompilerInstance does not have a FileNamager!");
63 using namespace llvm::sys;
65 const llvm::Regex Rule(
"/(.+)\\.framework/(.+)?Headers/(.+)");
70 FS.makeAbsolute(FilePath);
71 path::remove_dots(FilePath,
true);
72 FilePath = path::convert_to_slash(FilePath);
77 auto CheckDir = [&](llvm::StringRef Dir) ->
unsigned {
79 FS.makeAbsolute(DirPath);
80 path::remove_dots(DirPath,
true);
82 for (
auto NI = path::begin(
File), NE = path::end(
File),
83 DI = path::begin(Dir), DE = path::end(Dir);
86 while (NI != NE && *NI ==
".")
92 while (DI != DE && *DI ==
".")
98 return NI - path::begin(
File);
101 if (NI->size() == 1 && DI->size() == 1 &&
102 path::is_separator(NI->front()) && path::is_separator(DI->front()))
108 if (NI->endswith(
".sdk") && DI->endswith(
".sdk")) {
109 StringRef NBasename = path::stem(*NI);
110 StringRef DBasename = path::stem(*DI);
111 if (DBasename.startswith(NBasename))
121 unsigned PrefixLength = 0;
134 StringRef SpelledFilename = HMap->reverseLookupFilename(
File);
135 if (!SpelledFilename.empty())
136 return SpelledFilename.str();
144 PrefixLength = CheckDir(Entry.Path);
145 if (PrefixLength > 0) {
148 if (Entry.IsFramework) {
150 Rule.match(
File, &Matches);
152 if (Matches.size() != 4)
155 return path::convert_to_slash(
156 (Matches[1].drop_front(Matches[1].rfind(
'/') + 1) +
"/" +
163 return path::convert_to_slash(
File.drop_front(PrefixLength));
171std::optional<std::string> getRelativeIncludeName(
const CompilerInstance &CI,
173 bool *IsQuoted =
nullptr) {
177struct LocationFileChecker {
182 auto FileLoc =
SM.getFileLoc(Loc);
191 if (KnownFileEntries.count(*
File))
194 if (ExternalFileEntries.count(*
File))
198 bool IsQuoted =
false;
199 if (
auto IncludeName = getRelativeIncludeName(CI, *
File, &IsQuoted))
200 if (llvm::any_of(KnownFiles,
201 [&IsQuoted, &IncludeName](
const auto &KnownFile) {
202 return KnownFile.first.equals(*IncludeName) &&
203 KnownFile.second == IsQuoted;
205 KnownFileEntries.insert(*
File);
211 ExternalFileEntries.insert(*
File);
217 : CI(CI), KnownFiles(KnownFiles), ExternalFileEntries() {
218 for (
const auto &KnownFile : KnownFiles)
232 bool ShouldBeIncluded =
true;
234 if (
auto *TD = llvm::dyn_cast<TagDecl>(D))
235 ShouldBeIncluded = TD->isThisDeclarationADefinition();
236 else if (
auto *
Interface = llvm::dyn_cast<ObjCInterfaceDecl>(D))
237 ShouldBeIncluded =
Interface->isThisDeclarationADefinition();
238 else if (
auto *Protocol = llvm::dyn_cast<ObjCProtocolDecl>(D))
239 ShouldBeIncluded =
Protocol->isThisDeclarationADefinition();
241 ShouldBeIncluded = ShouldBeIncluded && LCF(D->
getLocation());
242 return ShouldBeIncluded;
245 BatchExtractAPIVisitor(LocationFileChecker &LCF,
ASTContext &Context,
250 LocationFileChecker &LCF;
253class WrappingExtractAPIConsumer :
public ASTConsumer {
256 : Visitor(Context, API) {}
270 std::unique_ptr<LocationFileChecker> LCF,
APISet &API)
271 : Visitor(*LCF, Context, API), LCF(
std::move(LCF)) {}
279 BatchExtractAPIVisitor Visitor;
280 std::unique_ptr<LocationFileChecker> LCF;
286 :
SM(
SM), API(API), PP(PP) {}
296 if (
SM.isWrittenInBuiltinFile(SourceLoc) ||
297 SM.isWrittenInCommandLineFile(SourceLoc))
300 PendingMacros.emplace_back(MacroNameToken, MD);
313 llvm::erase_if(PendingMacros, [&MD,
this](
const PendingMacro &PM) {
314 return MD.
getMacroInfo()->isIdenticalTo(*PM.MD->getMacroInfo(), PP,
320 for (
auto &PM : PendingMacros) {
323 if (PM.MD->getMacroInfo()->isUsedForHeaderGuard())
326 if (!shouldMacroBeIncluded(PM))
329 StringRef Name = PM.MacroNameToken.getIdentifierInfo()->getName();
330 PresumedLoc Loc =
SM.getPresumedLoc(PM.MacroNameToken.getLocation());
332 API.recordUSRForMacro(Name, PM.MacroNameToken.getLocation(), SM);
334 API.addMacroDefinition(
338 SM.isInSystemHeader(PM.MacroNameToken.getLocation()));
341 PendingMacros.clear();
345 struct PendingMacro {
346 Token MacroNameToken;
350 : MacroNameToken(MacroNameToken), MD(MD) {}
353 virtual bool shouldMacroBeIncluded(
const PendingMacro &PM) {
return true; }
361class APIMacroCallback :
public MacroCallback {
364 LocationFileChecker &LCF)
365 : MacroCallback(
SM, API, PP), LCF(LCF) {}
367 bool shouldMacroBeIncluded(
const PendingMacro &PM)
override {
369 return LCF(PM.MacroNameToken.getLocation());
373 LocationFileChecker &LCF;
390std::unique_ptr<raw_pwrite_stream>
392 std::unique_ptr<raw_pwrite_stream>
OS;
401std::unique_ptr<ASTConsumer>
403 OS = CreateOutputFile(CI, InFile);
412 API = std::make_unique<APISet>(
416 auto LCF = std::make_unique<LocationFileChecker>(CI, KnownInputFiles);
427 llvm::handleAllErrors(
432 CI.getDiagnostics().Report(
433 diag::err_extract_api_ignores_file_not_found)
438 return std::make_unique<ExtractAPIConsumer>(CI.
getASTContext(),
439 std::move(LCF), *
API);
451 auto Kind = Inputs[0].getKind();
455 bool IsQuoted =
false;
457 if (Kind.isObjectiveC())
458 HeaderContents +=
"#import";
460 HeaderContents +=
"#include";
462 StringRef FilePath = FIF.getFile();
463 if (
auto RelativeName = getRelativeIncludeName(CI, FilePath, &IsQuoted)) {
465 HeaderContents +=
" \"";
467 HeaderContents +=
" <";
469 HeaderContents += *RelativeName;
472 HeaderContents +=
"\"\n";
474 HeaderContents +=
">\n";
475 KnownInputFiles.emplace_back(
static_cast<SmallString<32>>(*RelativeName),
478 HeaderContents +=
" \"";
479 HeaderContents += FilePath;
480 HeaderContents +=
"\"\n";
481 KnownInputFiles.emplace_back(FilePath,
true);
487 << HeaderContents <<
"\n";
489 Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents,
490 getInputBufferName());
494 Inputs.emplace_back(
Buffer->getMemBufferRef(), Kind,
false);
501std::unique_ptr<ASTConsumer>
508 CreatedASTConsumer =
true;
510 OS = CreateOutputFile(CI, InFile);
518 API = std::make_unique<APISet>(
531 llvm::handleAllErrors(
536 CI.getDiagnostics().Report(
537 diag::err_extract_api_ignores_file_not_found)
542 auto WrappingConsumer =
544 std::vector<std::unique_ptr<ASTConsumer>> Consumers;
545 Consumers.push_back(std::move(OtherConsumer));
546 Consumers.push_back(std::move(WrappingConsumer));
548 return std::make_unique<MultiplexConsumer>(std::move(Consumers));
551void WrappingExtractAPIAction::EndSourceFileAction() {
555 if (CreatedASTConsumer) {
560std::unique_ptr<raw_pwrite_stream>
563 std::unique_ptr<raw_pwrite_stream>
OS;
570 auto Seperator = llvm::sys::path::get_separator();
571 auto Infilename = llvm::sys::path::filename(InFile);
572 OutFilePath.append({Seperator, Infilename});
573 llvm::sys::path::replace_extension(OutFilePath,
"json");
This file defines the APIRecord-based structs and the APISet class.
This file provides AST data structures related to concepts.
Defines the clang::ASTContext interface.
Defines interfaces for clang::FileEntry and clang::FileEntryRef.
Defines the clang::MacroInfo and clang::MacroDirective classes.
Defines the PPCallbacks interface.
Defines the clang::Preprocessor interface.
Defines the clang::SourceLocation class and associated facilities.
Defines the SourceManager interface.
This file defines the SymbolGraphSerializer class.
ASTConsumer - This is an abstract interface that should be implemented by clients that read ASTs.
virtual void HandleTranslationUnit(ASTContext &Ctx)
HandleTranslationUnit - This method is called when the ASTs for entire translation unit have been par...
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
TranslationUnitDecl * getTranslationUnitDecl() const
const clang::PrintingPolicy & getPrintingPolicy() const
void setPrintingPolicy(const clang::PrintingPolicy &Policy)
CompilerInstance - Helper class for managing a single instance of the Clang compiler.
FileManager * createFileManager(IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS=nullptr)
Create the file manager and replace any existing one with it.
bool hasFileManager() const
raw_ostream & getVerboseOutputStream()
Get the current stream for verbose output.
std::unique_ptr< raw_pwrite_stream > createDefaultOutputFile(bool Binary=true, StringRef BaseInput="", StringRef Extension="", bool RemoveFileOnSignal=true, bool CreateMissingDirectories=false, bool ForceUseTemporary=false)
Create the default output file (from the invocation's options) and add it to the list of tracked outp...
FileManager & getFileManager() const
Return the current file manager to the caller.
std::unique_ptr< raw_pwrite_stream > createOutputFile(StringRef OutputPath, bool Binary, bool RemoveFileOnSignal, bool UseTemporary, bool CreateMissingDirectories=false)
Create a new output file, optionally deriving the output path name, and add it to the list of tracked...
Preprocessor & getPreprocessor() const
Return the current preprocessor.
ASTContext & getASTContext() const
FrontendOptions & getFrontendOpts()
HeaderSearchOptions & getHeaderSearchOpts()
TargetInfo & getTarget() const
llvm::vfs::FileSystem & getVirtualFileSystem() const
SourceManager & getSourceManager() const
Return the current source manager.
Decl - This represents one declaration (or definition), e.g.
SourceLocation getLocation() const
A reference to a FileEntry that includes the name of the file as it was accessed by the FileManager's...
StringRef getNameAsRequested() const
The name of this FileEntry, as originally requested without applying any remappings for VFS 'use-exte...
Cached information about one file (either on disk or in the virtual file system).
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
OptionalFileEntryRef getOptionalFileRef(StringRef Filename, bool OpenFile=false, bool CacheFailure=true)
Get a FileEntryRef if it exists, without doing anything on error.
llvm::ErrorOr< const FileEntry * > getFile(StringRef Filename, bool OpenFile=false, bool CacheFailure=true)
Lookup, cache, and verify the specified file (real or virtual).
std::string ProductName
The name of the product the input files belong too.
std::string SymbolGraphOutputDir
SmallVector< FrontendInputFile, 0 > Inputs
The input files and their types.
std::vector< std::string > ExtractAPIIgnoresFileList
A description of the current definition of a macro.
MacroInfo * getMacroInfo() const
Get the MacroInfo that should be used for this definition.
Encapsulates changes to the "macros namespace" (the location where the macro name became active,...
const MacroInfo * getMacroInfo() const
Encapsulates the data about a macro definition (e.g.
bool isBuiltinMacro() const
Return true if this macro requires processing before expansion.
This interface provides a way to observe the actions of the preprocessor as it does its thing.
virtual void EndOfMainFile()
Callback invoked when the end of the main file is reached.
virtual void MacroUndefined(const Token &MacroNameTok, const MacroDefinition &MD, const MacroDirective *Undef)
Hook called whenever a macro #undef is seen.
virtual void MacroDefined(const Token &MacroNameTok, const MacroDirective *MD)
Hook called whenever a macro definition is seen.
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
void addPPCallbacks(std::unique_ptr< PPCallbacks > C)
Represents an unpacked "presumed" location which can be presented to the user.
Encodes a location in the source.
This class handles loading and caching of source files into memory.
const llvm::Triple & getTriple() const
Returns the target triple of the primary target.
Token - This structure provides full information about a lexed token.
SourceLocation getLocation() const
Return a source location identifier for the specified offset in the current file.
void EndSourceFileAction() override
Callback at the end of processing a single input.
std::unique_ptr< ASTConsumer > CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override
Create the AST consumer object for this action, if supported.
Defines the clang::TargetInfo interface.
@ Quoted
'#include ""' paths, added by 'gcc -iquote'.
@ Interface
The "__interface" keyword introduces the elaborated-type-specifier.
Describes how types, statements, expressions, and declarations should be printed.
unsigned AnonymousTagLocations
When printing an anonymous tag name, also print the location of that entity (e.g.,...