diff --git a/clang/include/clang/ExtractAPI/FrontendActions.h b/clang/include/clang/ExtractAPI/FrontendActions.h --- a/clang/include/clang/ExtractAPI/FrontendActions.h +++ b/clang/include/clang/ExtractAPI/FrontendActions.h @@ -39,6 +39,9 @@ /// files. std::unique_ptr Buffer; + /// The input file originally provided on the command line. + std::vector KnownInputFiles; + /// Prepare to execute the action on the given CompilerInstance. /// /// This is called before executing the action on any inputs. This generates a diff --git a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp --- a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp +++ b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp @@ -19,6 +19,8 @@ #include "clang/AST/ParentMapContext.h" #include "clang/AST/RawCommentList.h" #include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/ExtractAPI/API.h" #include "clang/ExtractAPI/AvailabilityInfo.h" @@ -30,23 +32,59 @@ #include "clang/Frontend/FrontendOptions.h" #include "clang/Lex/MacroInfo.h" #include "clang/Lex/PPCallbacks.h" +#include "clang/Lex/Preprocessor.h" #include "clang/Lex/PreprocessorOptions.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" +#include +#include using namespace clang; using namespace extractapi; namespace { +struct LocationFileChecker { + bool isLocationInKnownFile(SourceLocation Loc) { + // If the loc refers to a macro expansion we need to first get the file + // location of the expansion. + auto FileLoc = SM.getFileLoc(Loc); + FileID FID = SM.getFileID(FileLoc); + if (FID.isInvalid()) + return false; + + const auto *File = SM.getFileEntryForID(FID); + if (!File) + return false; + + if (KnownFileEntries.count(File)) + return true; + + return false; + } + + LocationFileChecker(const SourceManager &SM, + const std::vector &KnownFiles) + : SM(SM) { + for (const auto &KnownFilePath : KnownFiles) + if (auto FileEntry = SM.getFileManager().getFile(KnownFilePath)) + KnownFileEntries.insert(*FileEntry); + } + +private: + const SourceManager &SM; + llvm::DenseSet KnownFileEntries; +}; + /// The RecursiveASTVisitor to traverse symbol declarations and collect API /// information. class ExtractAPIVisitor : public RecursiveASTVisitor { public: - ExtractAPIVisitor(ASTContext &Context, APISet &API) - : Context(Context), API(API) {} + ExtractAPIVisitor(ASTContext &Context, LocationFileChecker &LCF, APISet &API) + : Context(Context), API(API), LCF(LCF) {} const APISet &getAPI() const { return API; } @@ -68,6 +106,9 @@ Decl->getTemplateSpecializationKind() == TSK_Undeclared) return true; + if (!LCF.isLocationInKnownFile(Decl->getLocation())) + return true; + // Collect symbol information. StringRef Name = Decl->getName(); StringRef USR = API.recordUSR(Decl); @@ -125,6 +166,9 @@ return true; } + if (!LCF.isLocationInKnownFile(Decl->getLocation())) + return true; + // Collect symbol information. StringRef Name = Decl->getName(); StringRef USR = API.recordUSR(Decl); @@ -159,6 +203,9 @@ if (!Decl->isThisDeclarationADefinition()) return true; + if (!LCF.isLocationInKnownFile(Decl->getLocation())) + return true; + // Collect symbol information. StringRef Name = Decl->getName(); StringRef USR = API.recordUSR(Decl); @@ -194,6 +241,9 @@ if (isa(Decl)) return true; + if (!LCF.isLocationInKnownFile(Decl->getLocation())) + return true; + // Collect symbol information. StringRef Name = Decl->getName(); StringRef USR = API.recordUSR(Decl); @@ -225,6 +275,9 @@ if (!Decl->isThisDeclarationADefinition()) return true; + if (!LCF.isLocationInKnownFile(Decl->getLocation())) + return true; + // Collect symbol information. StringRef Name = Decl->getName(); StringRef USR = API.recordUSR(Decl); @@ -269,6 +322,9 @@ if (!Decl->isThisDeclarationADefinition()) return true; + if (!LCF.isLocationInKnownFile(Decl->getLocation())) + return true; + // Collect symbol information. StringRef Name = Decl->getName(); StringRef USR = API.recordUSR(Decl); @@ -494,12 +550,14 @@ ASTContext &Context; APISet &API; + LocationFileChecker &LCF; }; class ExtractAPIConsumer : public ASTConsumer { public: - ExtractAPIConsumer(ASTContext &Context, APISet &API) - : Visitor(Context, API) {} + ExtractAPIConsumer(ASTContext &Context, + std::unique_ptr LCF, APISet &API) + : Visitor(Context, *LCF, API), LCF(std::move(LCF)) {} void HandleTranslationUnit(ASTContext &Context) override { // Use ExtractAPIVisitor to traverse symbol declarations in the context. @@ -508,11 +566,13 @@ private: ExtractAPIVisitor Visitor; + std::unique_ptr LCF; }; class MacroCallback : public PPCallbacks { public: - MacroCallback(const SourceManager &SM, APISet &API) : SM(SM), API(API) {} + MacroCallback(const SourceManager &SM, LocationFileChecker &LCF, APISet &API) + : SM(SM), LCF(LCF), API(API) {} void MacroDefined(const Token &MacroNameToken, const MacroDirective *MD) override { @@ -552,6 +612,9 @@ if (PM.MD->getMacroInfo()->isUsedForHeaderGuard()) continue; + if (!LCF.isLocationInKnownFile(PM.MacroNameToken.getLocation())) + continue; + StringRef Name = PM.MacroNameToken.getIdentifierInfo()->getName(); PresumedLoc Loc = SM.getPresumedLoc(PM.MacroNameToken.getLocation()); StringRef USR = @@ -576,6 +639,7 @@ }; const SourceManager &SM; + LocationFileChecker &LCF; APISet &API; llvm::SmallVector PendingMacros; }; @@ -596,11 +660,15 @@ CI.getTarget().getTriple(), CI.getFrontendOpts().Inputs.back().getKind().getLanguage()); + auto LCF = std::make_unique(CI.getSourceManager(), + KnownInputFiles); + // Register preprocessor callbacks that will add macro definitions to API. CI.getPreprocessor().addPPCallbacks( - std::make_unique(CI.getSourceManager(), *API)); + std::make_unique(CI.getSourceManager(), *LCF, *API)); - return std::make_unique(CI.getASTContext(), *API); + return std::make_unique(CI.getASTContext(), + std::move(LCF), *API); } bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) { @@ -620,6 +688,8 @@ HeaderContents += " \""; HeaderContents += FIF.getFile(); HeaderContents += "\"\n"; + + KnownInputFiles.emplace_back(FIF.getFile()); } Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents, diff --git a/clang/test/ExtractAPI/known_files_only.c b/clang/test/ExtractAPI/known_files_only.c new file mode 100644 --- /dev/null +++ b/clang/test/ExtractAPI/known_files_only.c @@ -0,0 +1,94 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s@INPUT_DIR@%/t@g" %t/reference.output.json.in >> \ +// RUN: %t/reference.output.json +// RUN: %clang -extract-api --product-name=GlobalRecord -target arm64-apple-macosx \ +// RUN: %t/input1.h -o %t/output.json | FileCheck -allow-empty %s + +// 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 + +// CHECK-NOT: error: +// CHECK-NOT: warning: + +//--- input1.h +int num; +#include "input2.h" + +//--- input2.h +// Ensure that these symbols are not emitted in the Symbol Graph. +#define HELLO 1 +char not_emitted; +void foo(int); +struct Foo { int a; }; + +//--- reference.output.json.in +{ + "metadata": { + "formatVersion": { + "major": 0, + "minor": 5, + "patch": 3 + }, + "generator": "?" + }, + "module": { + "name": "GlobalRecord", + "platform": { + "architecture": "arm64", + "operatingSystem": { + "minimumVersion": { + "major": 11, + "minor": 0, + "patch": 0 + }, + "name": "macosx" + }, + "vendor": "apple" + } + }, + "relationhips": [], + "symbols": [ + { + "declarationFragments": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "spelling": "int" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "num" + } + ], + "identifier": { + "interfaceLanguage": "c", + "precise": "c:@num" + }, + "kind": { + "displayName": "Global Variable", + "identifier": "c.var" + }, + "location": { + "character": 5, + "line": 1, + "uri": "file://INPUT_DIR/input1.h" + }, + "names": { + "subHeading": [ + { + "kind": "identifier", + "spelling": "num" + } + ], + "title": "num" + } + } + ] +} diff --git a/clang/test/ExtractAPI/known_files_only_hmap.c b/clang/test/ExtractAPI/known_files_only_hmap.c new file mode 100644 --- /dev/null +++ b/clang/test/ExtractAPI/known_files_only_hmap.c @@ -0,0 +1,152 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s@INPUT_DIR@%/t@g" %t/reference.output.json.in >> \ +// RUN: %t/reference.output.json +// RUN: sed -e "s@INPUT_DIR@%/t@g" %t/known_files_only.hmap.json.in >> \ +// RUN: %t/known_files_only.hmap.json +// RUN: %hmaptool write %t/known_files_only.hmap.json %t/known_files_only.hmap +// RUN: %clang -extract-api --product-name=KnownFilesOnlyHmap -target arm64-apple-macosx \ +// RUN: -I%t/known_files_only.hmap -I%t/subdir %t/subdir/subdir1/input.h \ +// RUN: %t/subdir/subdir2/known_file.h -o %t/output.json | FileCheck -allow-empty %s + +// 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 + +// CHECK-NOT: error: +// CHECK-NOT: warning: +//--- known_files_only.hmap.json.in +{ + "mappings" : + { + "subdir2/known_file.h" : "INPUT_DIR/subdir/subdir3/unknown.h" + } +} + +//--- subdir/subdir1/input.h +int num; +#include "subdir2/known_file.h" + +//--- subdir/subdir2/known_file.h +int known_num; + +//--- subdir/subdir3/unknown.h +// Ensure that these symbols are not emitted in the Symbol Graph. +#ifndef INPUT4_H +#define INPUT4_H + +#define HELLO 1 +char not_emitted; +void foo(int); +struct Foo { int a; }; + +#endif + +//--- reference.output.json.in +{ + "metadata": { + "formatVersion": { + "major": 0, + "minor": 5, + "patch": 3 + }, + "generator": "?" + }, + "module": { + "name": "KnownFilesOnlyHmap", + "platform": { + "architecture": "arm64", + "operatingSystem": { + "minimumVersion": { + "major": 11, + "minor": 0, + "patch": 0 + }, + "name": "macosx" + }, + "vendor": "apple" + } + }, + "relationhips": [], + "symbols": [ + { + "declarationFragments": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "spelling": "int" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "num" + } + ], + "identifier": { + "interfaceLanguage": "c", + "precise": "c:@num" + }, + "kind": { + "displayName": "Global Variable", + "identifier": "c.var" + }, + "location": { + "character": 5, + "line": 1, + "uri": "file://INPUT_DIR/subdir/subdir1/input.h" + }, + "names": { + "subHeading": [ + { + "kind": "identifier", + "spelling": "num" + } + ], + "title": "num" + } + }, + { + "declarationFragments": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "spelling": "int" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "known_num" + } + ], + "identifier": { + "interfaceLanguage": "c", + "precise": "c:@known_num" + }, + "kind": { + "displayName": "Global Variable", + "identifier": "c.var" + }, + "location": { + "character": 5, + "line": 1, + "uri": "file://INPUT_DIR/subdir/subdir2/known_file.h" + }, + "names": { + "subHeading": [ + { + "kind": "identifier", + "spelling": "known_num" + } + ], + "title": "known_num" + } + } + ] +}