diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td --- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td @@ -331,4 +331,7 @@ InGroup; } // end of instrumentation issue category +def err_extract_api_ignores_file_not_found : + Error<"file '%0' specified by '--extract-api-ignores=' not found">, DefaultFatal; + } diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1121,6 +1121,9 @@ HelpText<"Extract API information">; def product_name_EQ: Joined<["--"], "product-name=">, Flags<[CC1Option]>, MarshallingInfoString>; +def extract_api_ignores_EQ: Joined<["--"], "extract-api-ignores=">, Flags<[CC1Option]>, + HelpText<"File containing a new line separated list of API symbols to ignore when extracting API information.">, + MarshallingInfoString>; def e : JoinedOrSeparate<["-"], "e">, Flags<[LinkerInput]>, Group; def fmax_tokens_EQ : Joined<["-"], "fmax-tokens=">, Group, Flags<[CC1Option]>, HelpText<"Max total number of preprocessed tokens for -Wmax-tokens.">, diff --git a/clang/include/clang/ExtractAPI/APIIgnoresList.h b/clang/include/clang/ExtractAPI/APIIgnoresList.h new file mode 100644 --- /dev/null +++ b/clang/include/clang/ExtractAPI/APIIgnoresList.h @@ -0,0 +1,74 @@ +//===- ExtractAPI/APIIgnoresList.h ---------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file This file defines APIIgnoresList which is a type that allows querying +/// a file containing symbols to ignore when extracting API information. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_API_IGNORES_LIST_H +#define LLVM_CLANG_API_IGNORES_LIST_H + +#include "clang/Basic/FileManager.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include + +namespace llvm { +class MemoryBuffer; +} // namespace llvm + +namespace clang { +namespace extractapi { + +struct IgnoresFileNotFound : public llvm::ErrorInfo { + std::string Path; + static char ID; + + explicit IgnoresFileNotFound(StringRef Path) : Path(Path) {} + + virtual void log(llvm::raw_ostream &os) const override; + + virtual std::error_code convertToErrorCode() const override; +}; + +/// A type that provides access to a new line separated list of symbol names to +/// ignore when extracting API information. +struct APIIgnoresList { + /// The API to use for generating from the file at \p IgnoresFilePath. + /// + /// \returns an initialized APIIgnoresList or an Error. + static llvm::Expected create(llvm::StringRef IgnoresFilePath, + FileManager &FM); + + APIIgnoresList() = default; + + /// Check if \p SymbolName is specified in the APIIgnoresList and if it should + /// therefore be ignored. + bool shouldIgnore(llvm::StringRef SymbolName) const; + +private: + using SymbolNameList = llvm::SmallVector; + + APIIgnoresList(SymbolNameList SymbolsToIgnore, + std::unique_ptr Buffer) + : SymbolsToIgnore(std::move(SymbolsToIgnore)), Buffer(std::move(Buffer)) { + } + + SymbolNameList SymbolsToIgnore; + std::unique_ptr Buffer; +}; + +} // namespace extractapi +} // namespace clang + +#endif // LLVM_CLANG_API_IGNORES_LIST_H 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 @@ -15,6 +15,7 @@ #define LLVM_CLANG_EXTRACTAPI_FRONTEND_ACTIONS_H #include "clang/ExtractAPI/API.h" +#include "clang/ExtractAPI/APIIgnoresList.h" #include "clang/Frontend/FrontendAction.h" namespace clang { @@ -39,6 +40,9 @@ /// files. std::unique_ptr Buffer; + /// The list of symbols to ignore during serialization + extractapi::APIIgnoresList IgnoresList; + /// The input file originally provided on the command line. /// /// This captures the spelling used to include the file and whether the diff --git a/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h b/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h --- a/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h +++ b/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h @@ -15,6 +15,7 @@ #define LLVM_CLANG_EXTRACTAPI_SERIALIZATION_SERIALIZERBASE_H #include "clang/ExtractAPI/API.h" +#include "clang/ExtractAPI/APIIgnoresList.h" #include "llvm/Support/raw_ostream.h" namespace clang { @@ -40,6 +41,11 @@ /// Note: This should be used for populating metadata about the API. StringRef ProductName; + /// The list of symbols to ignore. + /// + /// Note: This should be consulted before emitting a symbol. + const APIIgnoresList &IgnoresList; + APISerializerOption Options; public: @@ -51,8 +57,10 @@ protected: APISerializer(const APISet &API, StringRef ProductName, + const APIIgnoresList &IgnoresList, APISerializerOption Options = {}) - : API(API), ProductName(ProductName), Options(Options) {} + : API(API), ProductName(ProductName), IgnoresList(IgnoresList), + Options(Options) {} virtual ~APISerializer() = default; }; diff --git a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h --- a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h +++ b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h @@ -18,6 +18,7 @@ #define LLVM_CLANG_EXTRACTAPI_SERIALIZATION_SYMBOLGRAPHSERIALIZER_H #include "clang/ExtractAPI/API.h" +#include "clang/ExtractAPI/APIIgnoresList.h" #include "clang/ExtractAPI/Serialization/SerializerBase.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/JSON.h" @@ -168,8 +169,9 @@ public: SymbolGraphSerializer(const APISet &API, StringRef ProductName, + const APIIgnoresList &IgnoresList, APISerializerOption Options = {}) - : APISerializer(API, ProductName, Options) {} + : APISerializer(API, ProductName, IgnoresList, Options) {} }; } // namespace extractapi diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h --- a/clang/include/clang/Frontend/FrontendOptions.h +++ b/clang/include/clang/Frontend/FrontendOptions.h @@ -454,6 +454,10 @@ /// The name of the product the input files belong too. std::string ProductName; + // Currently this is only used as part of the `-extract-api` action. + /// The file providing a list of APIs to ignore when extracting documentation + std::string ExtractAPIIgnoresFile; + /// Args to pass to the plugins std::map> PluginArgs; diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -4807,6 +4807,9 @@ CmdArgs.push_back("-extract-api"); if (Arg *ProductNameArg = Args.getLastArg(options::OPT_product_name_EQ)) ProductNameArg->render(Args, CmdArgs); + if (Arg *ExtractAPIIgnoresFileArg = + Args.getLastArg(options::OPT_extract_api_ignores_EQ)) + ExtractAPIIgnoresFileArg->render(Args, CmdArgs); } else { assert((isa(JA) || isa(JA)) && "Invalid action for clang tool."); diff --git a/clang/lib/ExtractAPI/APIIgnoresList.cpp b/clang/lib/ExtractAPI/APIIgnoresList.cpp new file mode 100644 --- /dev/null +++ b/clang/lib/ExtractAPI/APIIgnoresList.cpp @@ -0,0 +1,53 @@ +//===- ExtractAPI/APIIgnoresList.cpp -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements APIIgnoresList that allows users to specifiy a file +/// containing symbols to ignore during API extraction. +/// +//===----------------------------------------------------------------------===// + +#include "clang/ExtractAPI/APIIgnoresList.h" +#include "clang/Basic/FileManager.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Error.h" + +using namespace clang; +using namespace clang::extractapi; +using namespace llvm; + +char IgnoresFileNotFound::ID; + +void IgnoresFileNotFound::log(llvm::raw_ostream &os) const { + os << "Could not find API ignores file " << Path; +} + +std::error_code IgnoresFileNotFound::convertToErrorCode() const { + return llvm::inconvertibleErrorCode(); +} + +Expected APIIgnoresList::create(StringRef IgnoresFilePath, + FileManager &FM) { + auto BufferOrErr = FM.getBufferForFile(IgnoresFilePath); + if (!BufferOrErr) + return make_error(IgnoresFilePath); + + auto Buffer = std::move(BufferOrErr.get()); + SmallVector Lines; + Buffer->getBuffer().split(Lines, '\n', /*MaxSplit*/ -1, /*KeepEmpty*/ false); + // Symbol names don't have spaces in them, let's just remove these in case the + // input is slighlty malformed. + transform(Lines, Lines.begin(), [](StringRef Line) { return Line.trim(); }); + sort(Lines); + return APIIgnoresList(std::move(Lines), std::move(Buffer)); +} + +bool APIIgnoresList::shouldIgnore(StringRef SymbolName) const { + auto It = lower_bound(SymbolsToIgnore, SymbolName); + return (It != SymbolsToIgnore.end()) && (*It == SymbolName); +} diff --git a/clang/lib/ExtractAPI/CMakeLists.txt b/clang/lib/ExtractAPI/CMakeLists.txt --- a/clang/lib/ExtractAPI/CMakeLists.txt +++ b/clang/lib/ExtractAPI/CMakeLists.txt @@ -4,6 +4,7 @@ add_clang_library(clangExtractAPI API.cpp + APIIgnoresList.cpp AvailabilityInfo.cpp ExtractAPIConsumer.cpp DeclarationFragments.cpp 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 @@ -20,10 +20,12 @@ #include "clang/AST/ParentMapContext.h" #include "clang/AST/RawCommentList.h" #include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Basic/DiagnosticFrontend.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/ExtractAPI/API.h" +#include "clang/ExtractAPI/APIIgnoresList.h" #include "clang/ExtractAPI/AvailabilityInfo.h" #include "clang/ExtractAPI/DeclarationFragments.h" #include "clang/ExtractAPI/FrontendActions.h" @@ -38,6 +40,7 @@ #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" @@ -858,6 +861,18 @@ Policy.AnonymousTagLocations = false; CI.getASTContext().setPrintingPolicy(Policy); + if (!CI.getFrontendOpts().ExtractAPIIgnoresFile.empty()) { + llvm::handleAllErrors( + APIIgnoresList::create(CI.getFrontendOpts().ExtractAPIIgnoresFile, + CI.getFileManager()) + .moveInto(IgnoresList), + [&CI](const IgnoresFileNotFound &Err) { + CI.getDiagnostics().Report( + diag::err_extract_api_ignores_file_not_found) + << Err.Path; + }); + } + return std::make_unique(CI.getASTContext(), std::move(LCF), *API); } @@ -926,7 +941,7 @@ // Setup a SymbolGraphSerializer to write out collected API information in // the Symbol Graph format. // FIXME: Make the kind of APISerializer configurable. - SymbolGraphSerializer SGSerializer(*API, ProductName); + SymbolGraphSerializer SGSerializer(*API, ProductName, IgnoresList); SGSerializer.serialize(*OS); OS.reset(); } diff --git a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp --- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp +++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp @@ -14,6 +14,7 @@ #include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h" #include "clang/Basic/Version.h" #include "clang/ExtractAPI/API.h" +#include "clang/ExtractAPI/APIIgnoresList.h" #include "clang/ExtractAPI/DeclarationFragments.h" #include "llvm/Support/JSON.h" #include "llvm/Support/Path.h" @@ -480,6 +481,10 @@ } bool SymbolGraphSerializer::shouldSkip(const APIRecord &Record) const { + // Skip explicitly ignored symbols. + if (IgnoresList.shouldIgnore(Record.Name)) + return true; + // Skip unconditionally unavailable symbols if (Record.Availabilities.isUnconditionallyUnavailable()) return true; diff --git a/clang/test/Driver/extract-api-unknown-ignore-diag.h b/clang/test/Driver/extract-api-unknown-ignore-diag.h new file mode 100644 --- /dev/null +++ b/clang/test/Driver/extract-api-unknown-ignore-diag.h @@ -0,0 +1,6 @@ +// RUN: rm -rf %t +// RUN: not %clang -target x86_64-unknown-unknown -extract-api --extract-api-ignores=does-not-exist %s 2>&1 | FileCheck %s + +// CHECK: fatal error: file 'does-not-exist' specified by '--extract-api-ignores=' not found + +void dummy_function(void); diff --git a/clang/test/ExtractAPI/ignored-symbols.c b/clang/test/ExtractAPI/ignored-symbols.c new file mode 100644 --- /dev/null +++ b/clang/test/ExtractAPI/ignored-symbols.c @@ -0,0 +1,27 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx \ +// RUN: --extract-api-ignores=%t/ignores-list \ +// RUN: -x c-header %t/input.h -verify -o - | FileCheck %t/input.h + +//--- input.h +#define IGNORED_1 1 +#define IGNORED_2 2 +#define IGNORED_3 3 +#define IGNORED_4 4 +typedef int Ignored; +typedef float NonIgnored; + +// CHECK-NOT: IGNORED_1 +// CHECK-NOT: IGNORED_2 +// CHECK-NOT: IGNORED_3 +// CHECK: NonIgnored + +// expected-no-diagnostics + +//--- ignores-list +Ignored +IGNORED_4 +IGNORED_3 +IGNORED_2 +IGNORED_1