[docs] Add TableGen-based generator for command line argument documentation,

and generate documentation for all (non-hidden) options supported by the
'clang' driver.

llvm-svn: 292968
This commit is contained in:
Richard Smith 2017-01-24 19:39:46 +00:00
parent 1ee7bf6313
commit 081ad4d3e5
10 changed files with 3820 additions and 61 deletions

File diff suppressed because it is too large Load Diff

View File

@ -19,6 +19,7 @@ Using Clang as a Compiler
UsersManual
Toolchain
LanguageExtensions
ClangCommandLineReference
AttributeReference
DiagnosticsReference
CrossCompilation

View File

@ -11,7 +11,7 @@
//
//===----------------------------------------------------------------------===//
def cl_Group : OptionGroup<"<clang-cl options>">,
def cl_Group : OptionGroup<"<clang-cl options>">, Flags<[CLOption]>,
HelpText<"CL.EXE COMPATIBILITY OPTIONS">;
def cl_compile_Group : OptionGroup<"<clang-cl compile-only options>">,

View File

@ -0,0 +1,36 @@
//==--- ClangOptionDocs.td - Option documentation -------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
def GlobalDocumentation {
code Intro =[{..
-------------------------------------------------------------------
NOTE: This file is automatically generated by running clang-tblgen
-gen-opt-docs. Do not edit this file by hand!!
-------------------------------------------------------------------
=====================================
Clang command line argument reference
=====================================
.. contents::
:local:
Introduction
============
This page lists the command line arguments currently supported by the
GCC-compatible ``clang`` and ``clang++`` drivers.
}];
string Program = "clang";
list<string> ExcludedFlags = ["HelpHidden", "NoDriverOption",
"CLOption", "Unsupported", "Ignored"];
}
include "Options.td"

View File

@ -33,7 +33,8 @@ enum ClangFlags {
CLOption = (1 << 9),
CC1Option = (1 << 10),
CC1AsOption = (1 << 11),
NoDriverOption = (1 << 12)
NoDriverOption = (1 << 12),
Ignored = (1 << 13)
};
enum ID {

View File

@ -33,6 +33,9 @@ def NoArgumentUnused : OptionFlag;
// lines that use it.
def Unsupported : OptionFlag;
// Ignored - The option is unsupported, and the driver will silently ignore it.
def Ignored : OptionFlag;
// CoreOption - This is considered a "core" Clang option, available in both
// clang and clang-cl modes.
def CoreOption : OptionFlag;
@ -50,72 +53,130 @@ def CC1AsOption : OptionFlag;
// NoDriverOption - This option should not be accepted by the driver.
def NoDriverOption : OptionFlag;
// A short name to show in documentation. The name will be interpreted as rST.
class DocName<string name> { string DocName = name; }
// A brief description to show in documentation, interpreted as rST.
class DocBrief<code descr> { code DocBrief = descr; }
// Indicates that this group should be flattened into its parent when generating
// documentation.
class DocFlatten { bit DocFlatten = 1; }
/////////
// Groups
def Action_Group : OptionGroup<"<action group>">, DocName<"Actions">,
DocBrief<[{The action to perform on the input.}]>;
// Meta-group for options which are only used for compilation,
// and not linking etc.
def CompileOnly_Group : OptionGroup<"<CompileOnly group>">;
def CompileOnly_Group : OptionGroup<"<CompileOnly group>">,
DocName<"Compilation flags">, DocBrief<[{
Flags controlling the behavior of Clang during compilation. These flags have
no effect during actions that do not perform compilation.}]>;
def Action_Group : OptionGroup<"<action group>">;
def IncludePath_Group : OptionGroup<"<I/i group>">, Group<CompileOnly_Group>,
DocName<"Include path management">,
DocBrief<[{
Flags controlling how ``#include``\s are resolved to files.}]>;
def I_Group : OptionGroup<"<I group>">, Group<CompileOnly_Group>;
def M_Group : OptionGroup<"<M group>">, Group<CompileOnly_Group>;
def T_Group : OptionGroup<"<T group>">;
def O_Group : OptionGroup<"<O group>">, Group<CompileOnly_Group>;
def R_Group : OptionGroup<"<R group>">, Group<CompileOnly_Group>;
def R_value_Group : OptionGroup<"<R (with value) group>">, Group<R_Group>;
def W_Group : OptionGroup<"<W group>">, Group<CompileOnly_Group>;
def W_value_Group : OptionGroup<"<W (with value) group>">, Group<W_Group>;
def d_Group : OptionGroup<"<d group>">;
def f_Group : OptionGroup<"<f group>">, Group<CompileOnly_Group>;
def f_clang_Group : OptionGroup<"<f (clang-only) group>">, Group<CompileOnly_Group>;
def g_Group : OptionGroup<"<g group>">;
def gN_Group : OptionGroup<"<gN group>">, Group<g_Group>;
def ggdbN_Group : OptionGroup<"<ggdbN group>">, Group<gN_Group>;
def gTune_Group : OptionGroup<"<gTune group>">, Group<g_Group>;
def g_flags_Group : OptionGroup<"<g flags group>">;
def i_Group : OptionGroup<"<i group>">, Group<CompileOnly_Group>;
def clang_i_Group : OptionGroup<"<clang i group>">, Group<i_Group>;
def m_Group : OptionGroup<"<m group>">, Group<CompileOnly_Group>;
def opencl_Group : OptionGroup<"<opencl group>">, Group<CompileOnly_Group>;
def I_Group : OptionGroup<"<I group>">, Group<IncludePath_Group>, DocFlatten;
def i_Group : OptionGroup<"<i group>">, Group<IncludePath_Group>, DocFlatten;
def clang_i_Group : OptionGroup<"<clang i group>">, Group<i_Group>, DocFlatten;
def M_Group : OptionGroup<"<M group>">, Group<CompileOnly_Group>,
DocName<"Dependency file generation">, DocBrief<[{
Flags controlling generation of a dependency file for ``make``-like build
systems.}]>;
def d_Group : OptionGroup<"<d group>">, Group<CompileOnly_Group>,
DocName<"Dumping preprocessor state">, DocBrief<[{
Flags allowing the state of the preprocessor to be dumped in various ways.}]>;
def Diag_Group : OptionGroup<"<W/R group>">, Group<CompileOnly_Group>,
DocName<"Diagnostic flags">, DocBrief<[{
Flags controlling which warnings, errors, and remarks Clang will generate.
See the :doc:`full list of warning and remark flags <DiagnosticsReference>`.}]>;
def R_Group : OptionGroup<"<R group>">, Group<Diag_Group>, DocFlatten;
def R_value_Group : OptionGroup<"<R (with value) group>">, Group<R_Group>,
DocFlatten;
def W_Group : OptionGroup<"<W group>">, Group<Diag_Group>, DocFlatten;
def W_value_Group : OptionGroup<"<W (with value) group>">, Group<W_Group>,
DocFlatten;
def f_Group : OptionGroup<"<f group>">, Group<CompileOnly_Group>,
DocName<"Target-independent compilation options">;
def f_clang_Group : OptionGroup<"<f (clang-only) group>">,
Group<CompileOnly_Group>, DocFlatten;
def pedantic_Group : OptionGroup<"<pedantic group>">, Group<f_Group>,
DocFlatten;
def opencl_Group : OptionGroup<"<opencl group>">, Group<f_Group>,
DocName<"OpenCL flags">;
def m_Group : OptionGroup<"<m group>">, Group<CompileOnly_Group>,
DocName<"Target-dependent compilation options">;
// Feature groups - these take command line options that correspond directly to
// target specific features and can be translated directly from command line
// options.
def m_x86_Features_Group : OptionGroup<"<x86 features group>">,
Group<m_Group>,
Flags<[CoreOption]>;
def m_hexagon_Features_Group : OptionGroup<"<hexagon features group>">,
Group<m_Group>;
def m_arm_Features_Group : OptionGroup<"<arm features group>">,
Group<m_Group>;
def m_aarch64_Features_Group : OptionGroup<"<aarch64 features group>">,
Group<m_Group>;
def m_ppc_Features_Group : OptionGroup<"<ppc features group>">,
Group<m_Group>;
def m_wasm_Features_Group : OptionGroup<"<wasm features group>">,
Group<m_Group>;
Group<m_Group>, DocName<"AARCH64">;
def m_amdgpu_Features_Group : OptionGroup<"<amdgpu features group>">,
Group<m_Group>;
Group<m_Group>, DocName<"AMDGPU">;
def m_arm_Features_Group : OptionGroup<"<arm features group>">,
Group<m_Group>, DocName<"ARM">;
def m_hexagon_Features_Group : OptionGroup<"<hexagon features group>">,
Group<m_Group>, DocName<"Hexagon">;
def m_ppc_Features_Group : OptionGroup<"<ppc features group>">,
Group<m_Group>, DocName<"PowerPC">;
def m_wasm_Features_Group : OptionGroup<"<wasm features group>">,
Group<m_Group>, DocName<"WebAssembly">;
def m_x86_Features_Group : OptionGroup<"<x86 features group>">,
Group<m_Group>, Flags<[CoreOption]>, DocName<"X86">;
def m_libc_Group : OptionGroup<"<m libc group>">, Group<m_Group>;
def u_Group : OptionGroup<"<u group>">;
def m_libc_Group : OptionGroup<"<m libc group>">, Group<m_Group>,
Flags<[HelpHidden]>;
def pedantic_Group : OptionGroup<"<pedantic group>">,
Group<CompileOnly_Group>;
def reserved_lib_Group : OptionGroup<"<reserved libs group>">;
def O_Group : OptionGroup<"<O group>">, Group<CompileOnly_Group>,
DocName<"Optimization level">, DocBrief<[{
Flags controlling how much optimization should be performed.}]>;
def DebugInfo_Group : OptionGroup<"<g group>">, Group<CompileOnly_Group>,
DocName<"Debug information generation">, DocBrief<[{
Flags controlling how much and what kind of debug information should be
generated.}]>;
def g_Group : OptionGroup<"<g group>">, Group<DebugInfo_Group>,
DocName<"Kind and level of debug information">;
def gN_Group : OptionGroup<"<gN group>">, Group<g_Group>,
DocName<"Debug level">;
def ggdbN_Group : OptionGroup<"<ggdbN group>">, Group<gN_Group>, DocFlatten;
def gTune_Group : OptionGroup<"<gTune group>">, Group<g_Group>,
DocName<"Debugger to tune debug information for">;
def g_flags_Group : OptionGroup<"<g flags group>">, Group<DebugInfo_Group>,
DocName<"Debug information flags">;
def Link_Group : OptionGroup<"<T/e/s/t/u group>">, DocName<"Linker flags">,
DocBrief<[{Flags that are passed on to the linker}]>;
def T_Group : OptionGroup<"<T group>">, Group<Link_Group>, DocFlatten;
def u_Group : OptionGroup<"<u group>">, Group<Link_Group>, DocFlatten;
def reserved_lib_Group : OptionGroup<"<reserved libs group>">,
Flags<[Unsupported]>;
// Temporary groups for clang options which we know we don't support,
// but don't want to verbosely warn the user about.
def clang_ignored_f_Group : OptionGroup<"<clang ignored f group>">,
Group<f_Group>;
Group<f_Group>, Flags<[Ignored]>;
def clang_ignored_m_Group : OptionGroup<"<clang ignored m group>">,
Group<m_Group>;
Group<m_Group>, Flags<[Ignored]>;
// Group that ignores all gcc optimizations that won't be implemented
def clang_ignored_gcc_optimization_f_Group : OptionGroup<
"<clang_ignored_gcc_optimization_f_Group>">, Group<f_Group>;
"<clang_ignored_gcc_optimization_f_Group>">, Group<f_Group>, Flags<[Ignored]>;
/////////
// Options
@ -141,7 +202,7 @@ def clang_ignored_gcc_optimization_f_Group : OptionGroup<
// Developer Driver Options
def internal_Group : OptionGroup<"<clang internal options>">;
def internal_Group : OptionGroup<"<clang internal options>">, Flags<[HelpHidden]>;
def internal_driver_Group : OptionGroup<"<clang driver internal options>">,
Group<internal_Group>, HelpText<"DRIVER OPTIONS">;
def internal_debug_Group :
@ -252,7 +313,7 @@ def H : Flag<["-"], "H">, Flags<[CC1Option]>,
def I_ : Flag<["-"], "I-">, Group<I_Group>;
def I : JoinedOrSeparate<["-"], "I">, Group<I_Group>, Flags<[CC1Option,CC1AsOption]>,
HelpText<"Add directory to include search path">;
def L : JoinedOrSeparate<["-"], "L">, Flags<[RenderJoined]>;
def L : JoinedOrSeparate<["-"], "L">, Flags<[RenderJoined]>, Group<Link_Group>;
def MD : Flag<["-"], "MD">, Group<M_Group>,
HelpText<"Write a depfile containing user and system headers">;
def MMD : Flag<["-"], "MMD">, Group<M_Group>,
@ -323,7 +384,7 @@ def Wno_deprecated : Flag<["-"], "Wno-deprecated">, Group<W_Group>, Flags<[CC1Op
def Wextra : Flag<["-"], "Wextra">, Group<W_Group>, Flags<[CC1Option]>;
def Wl_COMMA : CommaJoined<["-"], "Wl,">, Flags<[LinkerInput, RenderAsInput]>,
HelpText<"Pass the comma separated arguments in <arg> to the linker">,
MetaVarName<"<arg>">;
MetaVarName<"<arg>">, Group<Link_Group>;
// FIXME: This is broken; these should not be Joined arguments.
def Wno_nonportable_cfstrings : Joined<["-"], "Wno-nonportable-cfstrings">, Group<W_Group>,
Flags<[CC1Option]>;
@ -349,14 +410,16 @@ def Xcuda_fatbinary : Separate<["-"], "Xcuda-fatbinary">,
def Xcuda_ptxas : Separate<["-"], "Xcuda-ptxas">,
HelpText<"Pass <arg> to the ptxas assembler">, MetaVarName<"<arg>">;
def z : Separate<["-"], "z">, Flags<[LinkerInput, RenderAsInput]>,
HelpText<"Pass -z <arg> to the linker">, MetaVarName<"<arg>">;
HelpText<"Pass -z <arg> to the linker">, MetaVarName<"<arg>">,
Group<Link_Group>;
def Xlinker : Separate<["-"], "Xlinker">, Flags<[LinkerInput, RenderAsInput]>,
HelpText<"Pass <arg> to the linker">, MetaVarName<"<arg>">;
HelpText<"Pass <arg> to the linker">, MetaVarName<"<arg>">,
Group<Link_Group>;
def Xpreprocessor : Separate<["-"], "Xpreprocessor">,
HelpText<"Pass <arg> to the preprocessor">, MetaVarName<"<arg>">;
def X_Flag : Flag<["-"], "X">;
def X_Joined : Joined<["-"], "X">;
def Z_Flag : Flag<["-"], "Z">;
def Z_Flag : Flag<["-"], "Z">, Group<Link_Group>;
def Z_Joined : Joined<["-"], "Z">;
def all__load : Flag<["-"], "all_load">;
def allowable__client : Separate<["-"], "allowable_client">;
@ -462,7 +525,7 @@ def emit_ast : Flag<["-"], "emit-ast">,
def emit_llvm : Flag<["-"], "emit-llvm">, Flags<[CC1Option]>, Group<Action_Group>,
HelpText<"Use the LLVM representation for assembler and object files">;
def exported__symbols__list : Separate<["-"], "exported_symbols_list">;
def e : JoinedOrSeparate<["-"], "e">;
def e : JoinedOrSeparate<["-"], "e">, Group<Link_Group>;
def fPIC : Flag<["-"], "fPIC">, Group<f_Group>;
def fno_PIC : Flag<["-"], "fno-PIC">, Group<f_Group>;
def fPIE : Flag<["-"], "fPIE">, Group<f_Group>;
@ -822,7 +885,8 @@ def fno_gnu89_inline : Flag<["-"], "fno-gnu89-inline">, Group<f_Group>;
def fgnu_runtime : Flag<["-"], "fgnu-runtime">, Group<f_Group>,
HelpText<"Generate output compatible with the standard GNU Objective-C runtime">;
def fheinous_gnu_extensions : Flag<["-"], "fheinous-gnu-extensions">, Flags<[CC1Option]>;
def filelist : Separate<["-"], "filelist">, Flags<[LinkerInput]>;
def filelist : Separate<["-"], "filelist">, Flags<[LinkerInput]>,
Group<Link_Group>;
def : Flag<["-"], "findirect-virtual-calls">, Alias<fapple_kext>;
def finline_functions : Flag<["-"], "finline-functions">, Group<f_clang_Group>, Flags<[CC1Option]>,
HelpText<"Inline suitable functions">;
@ -1455,7 +1519,8 @@ def ivfsoverlay : JoinedOrSeparate<["-"], "ivfsoverlay">, Group<clang_i_Group>,
HelpText<"Overlay the virtual filesystem described by file over the real file system">;
def i : Joined<["-"], "i">, Group<i_Group>;
def keep__private__externs : Flag<["-"], "keep_private_externs">;
def l : JoinedOrSeparate<["-"], "l">, Flags<[LinkerInput, RenderJoined]>;
def l : JoinedOrSeparate<["-"], "l">, Flags<[LinkerInput, RenderJoined]>,
Group<Link_Group>;
def lazy__framework : Separate<["-"], "lazy_framework">, Flags<[LinkerInput]>;
def lazy__library : Separate<["-"], "lazy_library">, Flags<[LinkerInput]>;
def mlittle_endian : Flag<["-"], "mlittle-endian">, Flags<[DriverOption]>;
@ -1965,10 +2030,11 @@ def resource_dir : Separate<["-"], "resource-dir">,
HelpText<"The directory which holds the compiler resource files">;
def resource_dir_EQ : Joined<["-"], "resource-dir=">, Flags<[DriverOption, CoreOption]>,
Alias<resource_dir>;
def rpath : Separate<["-"], "rpath">, Flags<[LinkerInput]>;
def rpath : Separate<["-"], "rpath">, Flags<[LinkerInput]>, Group<Link_Group>;
def rtlib_EQ : Joined<["-", "--"], "rtlib=">,
HelpText<"Compiler runtime library to use">;
def r : Flag<["-"], "r">, Flags<[LinkerInput,NoArgumentUnused]>;
def r : Flag<["-"], "r">, Flags<[LinkerInput,NoArgumentUnused]>,
Group<Link_Group>;
def save_temps_EQ : Joined<["-", "--"], "save-temps=">, Flags<[DriverOption]>,
HelpText<"Save intermediate compilation results.">;
def save_temps : Flag<["-", "--"], "save-temps">, Flags<[DriverOption]>,
@ -2020,7 +2086,7 @@ def no_system_header_prefix : Joined<["--"], "no-system-header-prefix=">,
HelpText<"Treat all #include paths starting with <prefix> as not including a "
"system header.">;
def : Separate<["--"], "no-system-header-prefix">, Alias<no_system_header_prefix>;
def s : Flag<["-"], "s">;
def s : Flag<["-"], "s">, Group<Link_Group>;
def target : Joined<["--"], "target=">, Flags<[DriverOption, CoreOption]>,
HelpText<"Generate code for the given target">;
def gcc_toolchain : Joined<["--"], "gcc-toolchain=">, Flags<[DriverOption]>,
@ -2034,7 +2100,7 @@ def trigraphs : Flag<["-", "--"], "trigraphs">, Alias<ftrigraphs>,
HelpText<"Process trigraph sequences">;
def twolevel__namespace__hints : Flag<["-"], "twolevel_namespace_hints">;
def twolevel__namespace : Flag<["-"], "twolevel_namespace">;
def t : Flag<["-"], "t">;
def t : Flag<["-"], "t">, Group<Link_Group>;
def umbrella : Separate<["-"], "umbrella">;
def undefined : JoinedOrSeparate<["-"], "undefined">, Group<u_Group>;
def undef : Flag<["-"], "undef">, Group<u_Group>, Flags<[CC1Option]>,
@ -2330,7 +2396,10 @@ defm devirtualize_speculatively : BooleanFFlag<"devirtualize-speculatively">,
// gfortran options that we recognize in the driver and pass along when
// invoking GCC to compile Fortran code.
def gfortran_Group : OptionGroup<"gfortran Group">;
def gfortran_Group : OptionGroup<"<gfortran group>">,
DocName<"Fortran compilation flags">, DocBrief<[{
Flags that will be passed onto the ``gfortran`` compiler when Clang is given
a Fortran input.}]>;
// Generic gfortran options.
def A_DASH : Joined<["-"], "A-">, Group<gfortran_Group>;

View File

@ -7,6 +7,7 @@ add_tablegen(clang-tblgen CLANG
ClangCommentHTMLNamedCharacterReferenceEmitter.cpp
ClangCommentHTMLTagsEmitter.cpp
ClangDiagnosticsEmitter.cpp
ClangOptionDocEmitter.cpp
ClangSACheckersEmitter.cpp
NeonEmitter.cpp
TableGen.cpp

View File

@ -0,0 +1,358 @@
//===- ClangOptionDocEmitter.cpp - Documentation for command line flags ---===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// FIXME: Once this has stabilized, consider moving it to LLVM.
//
//===----------------------------------------------------------------------===//
#include "llvm/TableGen/Error.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/Twine.h"
#include "llvm/TableGen/Record.h"
#include "llvm/TableGen/TableGenBackend.h"
#include <cctype>
#include <cstring>
#include <map>
using namespace llvm;
namespace clang {
namespace docs {
namespace {
struct DocumentedOption {
Record *Option;
std::vector<Record*> Aliases;
};
struct DocumentedGroup;
struct Documentation {
std::vector<DocumentedGroup> Groups;
std::vector<DocumentedOption> Options;
};
struct DocumentedGroup : Documentation {
Record *Group;
};
// Reorganize the records into a suitable form for emitting documentation.
Documentation extractDocumentation(RecordKeeper &Records) {
Documentation Result;
// Build the tree of groups. The root in the tree is the fake option group
// (Record*)nullptr, which contains all top-level groups and options.
std::map<Record*, std::vector<Record*> > OptionsInGroup;
std::map<Record*, std::vector<Record*> > GroupsInGroup;
std::map<Record*, std::vector<Record*> > Aliases;
std::map<std::string, Record*> OptionsByName;
for (Record *R : Records.getAllDerivedDefinitions("Option"))
OptionsByName[R->getValueAsString("Name")] = R;
auto Flatten = [](Record *R) {
return R->getValue("DocFlatten") && R->getValueAsBit("DocFlatten");
};
auto SkipFlattened = [&](Record *R) -> Record* {
while (R && Flatten(R)) {
auto *G = dyn_cast<DefInit>(R->getValueInit("Group"));
if (!G)
return nullptr;
R = G->getDef();
}
return R;
};
for (Record *R : Records.getAllDerivedDefinitions("OptionGroup")) {
if (Flatten(R))
continue;
Record *Group = nullptr;
if (auto *G = dyn_cast<DefInit>(R->getValueInit("Group")))
Group = SkipFlattened(G->getDef());
GroupsInGroup[Group].push_back(R);
}
for (Record *R : Records.getAllDerivedDefinitions("Option")) {
if (auto *A = dyn_cast<DefInit>(R->getValueInit("Alias"))) {
Aliases[A->getDef()].push_back(R);
continue;
}
// Pretend no-X and Xno-Y options are aliases of X and XY.
auto Name = R->getValueAsString("Name");
if (Name.size() >= 4) {
if (Name.substr(0, 3) == "no-" && OptionsByName[Name.substr(3)]) {
Aliases[OptionsByName[Name.substr(3)]].push_back(R);
continue;
}
if (Name.substr(1, 3) == "no-" && OptionsByName[Name[0] + Name.substr(4)]) {
Aliases[OptionsByName[Name[0] + Name.substr(4)]].push_back(R);
continue;
}
}
Record *Group = nullptr;
if (auto *G = dyn_cast<DefInit>(R->getValueInit("Group")))
Group = SkipFlattened(G->getDef());
OptionsInGroup[Group].push_back(R);
}
auto CompareByName = [](Record *A, Record *B) {
return A->getValueAsString("Name") < B->getValueAsString("Name");
};
auto CompareByLocation = [](Record *A, Record *B) {
return A->getLoc()[0].getPointer() < B->getLoc()[0].getPointer();
};
auto DocumentationForOption = [&](Record *R) -> DocumentedOption {
auto &A = Aliases[R];
std::sort(A.begin(), A.end(), CompareByName);
return {R, std::move(A)};
};
std::function<Documentation(Record *)> DocumentationForGroup =
[&](Record *R) -> Documentation {
Documentation D;
auto &Groups = GroupsInGroup[R];
std::sort(Groups.begin(), Groups.end(), CompareByLocation);
for (Record *G : Groups) {
D.Groups.emplace_back();
D.Groups.back().Group = G;
Documentation &Base = D.Groups.back();
Base = DocumentationForGroup(G);
}
auto &Options = OptionsInGroup[R];
std::sort(Options.begin(), Options.end(), CompareByName);
for (Record *O : Options)
D.Options.push_back(DocumentationForOption(O));
return D;
};
return DocumentationForGroup(nullptr);
}
// Get the first and successive separators to use for an OptionKind.
std::pair<StringRef,StringRef> getSeparatorsForKind(Record *OptionKind) {
return StringSwitch<std::pair<StringRef, StringRef>>(OptionKind->getName())
.Cases("KIND_JOINED", "KIND_JOINED_OR_SEPARATE",
"KIND_JOINED_AND_SEPARATE",
"KIND_REMAINING_ARGS_JOINED", {"", " "})
.Case("KIND_COMMAJOINED", {"", ","})
.Default({" ", " "});
}
const unsigned UnlimitedArgs = unsigned(-1);
// Get the number of arguments expected for an option, or -1 if any number of
// arguments are accepted.
unsigned getNumArgsForKind(Record *OptionKind, Record *Option) {
return StringSwitch<unsigned>(OptionKind->getName())
.Cases("KIND_JOINED", "KIND_JOINED_OR_SEPARATE", "KIND_SEPARATE", 1)
.Cases("KIND_REMAINING_ARGS", "KIND_REMAINING_ARGS_JOINED",
"KIND_COMMAJOINED", UnlimitedArgs)
.Case("KIND_JOINED_AND_SEPARATE", 2)
.Case("KIND_MULTIARG", Option->getValueAsInt("NumArgs"))
.Default(0);
}
bool hasFlag(const Record *OptionOrGroup, StringRef OptionFlag) {
for (const Record *Flag : OptionOrGroup->getValueAsListOfDefs("Flags"))
if (Flag->getName() == OptionFlag)
return true;
return false;
}
bool isExcluded(const Record *OptionOrGroup, const Record *DocInfo) {
// FIXME: Provide a flag to specify the set of exclusions.
for (StringRef Exclusion : DocInfo->getValueAsListOfStrings("ExcludedFlags"))
if (hasFlag(OptionOrGroup, Exclusion))
return true;
return false;
}
std::string escapeRST(StringRef Str) {
std::string Out;
for (auto K : Str) {
if (StringRef("`*|_[]\\").count(K))
Out.push_back('\\');
Out.push_back(K);
}
return Out;
}
bool canSphinxCopeWithOption(const Record *Option) {
// HACK: Work arond sphinx's inability to cope with punctuation-only options
// such as /? by suppressing them from the option list.
for (char C : Option->getValueAsString("Name"))
if (isalnum(C))
return true;
return false;
}
void emitHeading(int Depth, std::string Heading, raw_ostream &OS) {
assert(Depth < 8 && "groups nested too deeply");
OS << Heading << '\n'
<< std::string(Heading.size(), "=~-_'+<>"[Depth]) << "\n";
}
/// Get the value of field \p Primary, if possible. If \p Primary does not
/// exist, get the value of \p Fallback and escape it for rST emission.
std::string getRSTStringWithTextFallback(const Record *R, StringRef Primary,
StringRef Fallback) {
for (auto Field : {Primary, Fallback}) {
if (auto *V = R->getValue(Field)) {
StringRef Value;
if (auto *SV = dyn_cast_or_null<StringInit>(V->getValue()))
Value = SV->getValue();
else if (auto *CV = dyn_cast_or_null<CodeInit>(V->getValue()))
Value = CV->getValue();
if (!Value.empty())
return Field == Primary ? Value.str() : escapeRST(Value);
}
}
return StringRef();
}
void emitOptionWithArgs(StringRef Prefix, Record *Option,
ArrayRef<std::string> Args, raw_ostream &OS) {
OS << Prefix << escapeRST(Option->getValueAsString("Name"));
std::pair<StringRef, StringRef> Separators =
getSeparatorsForKind(Option->getValueAsDef("Kind"));
StringRef Separator = Separators.first;
for (auto Arg : Args) {
OS << Separator << escapeRST(Arg);
Separator = Separators.second;
}
}
void emitOptionName(StringRef Prefix, Record *Option, raw_ostream &OS) {
// Find the arguments to list after the option.
unsigned NumArgs = getNumArgsForKind(Option->getValueAsDef("Kind"), Option);
std::vector<std::string> Args;
if (!Option->isValueUnset("MetaVarName"))
Args.push_back(Option->getValueAsString("MetaVarName"));
else if (NumArgs == 1)
Args.push_back("<arg>");
while (Args.size() < NumArgs) {
Args.push_back(("<arg" + Twine(Args.size() + 1) + ">").str());
// Use '--args <arg1> <arg2>...' if any number of args are allowed.
if (Args.size() == 2 && NumArgs == UnlimitedArgs) {
Args.back() += "...";
break;
}
}
emitOptionWithArgs(Prefix, Option, Args, OS);
auto AliasArgs = Option->getValueAsListOfStrings("AliasArgs");
if (!AliasArgs.empty()) {
Record *Alias = Option->getValueAsDef("Alias");
OS << " (equivalent to ";
emitOptionWithArgs(Alias->getValueAsListOfStrings("Prefixes").front(),
Alias, Option->getValueAsListOfStrings("AliasArgs"), OS);
OS << ")";
}
}
bool emitOptionNames(Record *Option, raw_ostream &OS, bool EmittedAny) {
for (auto &Prefix : Option->getValueAsListOfStrings("Prefixes")) {
if (EmittedAny)
OS << ", ";
emitOptionName(Prefix, Option, OS);
EmittedAny = true;
}
return EmittedAny;
}
void emitOption(const DocumentedOption &Option, const Record *DocInfo,
raw_ostream &OS) {
if (isExcluded(Option.Option, DocInfo))
return;
if (Option.Option->getValueAsDef("Kind")->getName() == "KIND_UNKNOWN" ||
Option.Option->getValueAsDef("Kind")->getName() == "KIND_INPUT")
return;
if (!canSphinxCopeWithOption(Option.Option))
return;
// HACK: Emit a different program name with each option to work around
// sphinx's inability to cope with options that differ only by punctuation
// (eg -ObjC vs -ObjC++, -G vs -G=).
static int Emitted = 0;
OS << ".. program:: " << DocInfo->getValueAsString("Program") << Emitted++
<< "\n";
// Emit the names of the option.
OS << ".. option:: ";
bool EmittedAny = emitOptionNames(Option.Option, OS, false);
for (auto *Alias : Option.Aliases)
if (!isExcluded(Alias, DocInfo) && canSphinxCopeWithOption(Option.Option))
EmittedAny = emitOptionNames(Alias, OS, EmittedAny);
assert(EmittedAny && "no flags for option");
OS << "\n\n";
// Emit the description, if we have one.
std::string Description =
getRSTStringWithTextFallback(Option.Option, "DocBrief", "HelpText");
if (!Description.empty())
OS << Description << "\n\n";
}
void emitDocumentation(int Depth, const Documentation &Doc,
const Record *DocInfo, raw_ostream &OS);
void emitGroup(int Depth, const DocumentedGroup &Group, const Record *DocInfo,
raw_ostream &OS) {
if (isExcluded(Group.Group, DocInfo))
return;
emitHeading(Depth,
getRSTStringWithTextFallback(Group.Group, "DocName", "Name"), OS);
// Emit the description, if we have one.
std::string Description =
getRSTStringWithTextFallback(Group.Group, "DocBrief", "HelpText");
if (!Description.empty())
OS << Description << "\n\n";
// Emit contained options and groups.
emitDocumentation(Depth + 1, Group, DocInfo, OS);
}
void emitDocumentation(int Depth, const Documentation &Doc,
const Record *DocInfo, raw_ostream &OS) {
for (auto &O : Doc.Options)
emitOption(O, DocInfo, OS);
for (auto &G : Doc.Groups)
emitGroup(Depth, G, DocInfo, OS);
}
} // namespace
} // namespace docs
void EmitClangOptDocs(RecordKeeper &Records, raw_ostream &OS) {
using namespace docs;
const Record *DocInfo = Records.getDef("GlobalDocumentation");
if (!DocInfo) {
PrintFatalError("The GlobalDocumentation top-level definition is missing, "
"no documentation will be generated.");
return;
}
OS << DocInfo->getValueAsString("Intro") << "\n";
OS << ".. program:: " << DocInfo->getValueAsString("Program") << "\n";
emitDocumentation(0, extractDocumentation(Records), DocInfo, OS);
}
} // end namespace clang

View File

@ -53,7 +53,8 @@ enum ActionType {
GenArmNeonSema,
GenArmNeonTest,
GenAttrDocs,
GenDiagDocs
GenDiagDocs,
GenOptDocs
};
namespace {
@ -135,7 +136,9 @@ cl::opt<ActionType> Action(
clEnumValN(GenAttrDocs, "gen-attr-docs",
"Generate attribute documentation"),
clEnumValN(GenDiagDocs, "gen-diag-docs",
"Generate attribute documentation")));
"Generate diagnostic documentation"),
clEnumValN(GenOptDocs, "gen-opt-docs",
"Generate option documentation")));
cl::opt<std::string>
ClangComponent("clang-component",
@ -238,6 +241,9 @@ bool ClangTableGenMain(raw_ostream &OS, RecordKeeper &Records) {
case GenDiagDocs:
EmitClangDiagDocs(Records, OS);
break;
case GenOptDocs:
EmitClangOptDocs(Records, OS);
break;
}
return false;

View File

@ -70,6 +70,7 @@ void EmitNeonTest2(RecordKeeper &Records, raw_ostream &OS);
void EmitClangAttrDocs(RecordKeeper &Records, raw_ostream &OS);
void EmitClangDiagDocs(RecordKeeper &Records, raw_ostream &OS);
void EmitClangOptDocs(RecordKeeper &Records, raw_ostream &OS);
} // end namespace clang