Improve workaround for Sphinx's lack of support for command line options containing '+', '.' etc. to be more stable as the set of options changes.

llvm-svn: 293252
This commit is contained in:
Richard Smith 2017-01-27 01:54:42 +00:00
parent e3ac0fb948
commit b2c82a6970
2 changed files with 123 additions and 853 deletions

File diff suppressed because it is too large Load Diff

View File

@ -140,7 +140,7 @@ Documentation extractDocumentation(RecordKeeper &Records) {
}
// Get the first and successive separators to use for an OptionKind.
std::pair<StringRef,StringRef> getSeparatorsForKind(Record *OptionKind) {
std::pair<StringRef,StringRef> getSeparatorsForKind(const Record *OptionKind) {
return StringSwitch<std::pair<StringRef, StringRef>>(OptionKind->getName())
.Cases("KIND_JOINED", "KIND_JOINED_OR_SEPARATE",
"KIND_JOINED_AND_SEPARATE",
@ -153,7 +153,7 @@ 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) {
unsigned getNumArgsForKind(Record *OptionKind, const 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",
@ -188,6 +188,13 @@ std::string escapeRST(StringRef Str) {
return Out;
}
StringRef getSphinxOptionID(StringRef OptionName) {
for (auto I = OptionName.begin(), E = OptionName.end(); I != E; ++I)
if (!isalnum(*I) && *I != '-')
return OptionName.substr(0, I - OptionName.begin());
return OptionName;
}
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.
@ -221,8 +228,8 @@ std::string getRSTStringWithTextFallback(const Record *R, StringRef Primary,
return StringRef();
}
void emitOptionWithArgs(StringRef Prefix, Record *Option,
ArrayRef<std::string> Args, raw_ostream &OS) {
void emitOptionWithArgs(StringRef Prefix, const Record *Option,
ArrayRef<std::string> Args, raw_ostream &OS) {
OS << Prefix << escapeRST(Option->getValueAsString("Name"));
std::pair<StringRef, StringRef> Separators =
@ -235,7 +242,7 @@ void emitOptionWithArgs(StringRef Prefix, Record *Option,
}
}
void emitOptionName(StringRef Prefix, Record *Option, raw_ostream &OS) {
void emitOptionName(StringRef Prefix, const Record *Option, raw_ostream &OS) {
// Find the arguments to list after the option.
unsigned NumArgs = getNumArgsForKind(Option->getValueAsDef("Kind"), Option);
@ -266,7 +273,7 @@ void emitOptionName(StringRef Prefix, Record *Option, raw_ostream &OS) {
}
}
bool emitOptionNames(Record *Option, raw_ostream &OS, bool EmittedAny) {
bool emitOptionNames(const Record *Option, raw_ostream &OS, bool EmittedAny) {
for (auto &Prefix : Option->getValueAsListOfStrings("Prefixes")) {
if (EmittedAny)
OS << ", ";
@ -276,6 +283,16 @@ bool emitOptionNames(Record *Option, raw_ostream &OS, bool EmittedAny) {
return EmittedAny;
}
template <typename Fn>
void forEachOptionName(const DocumentedOption &Option, const Record *DocInfo,
Fn F) {
F(Option.Option);
for (auto *Alias : Option.Aliases)
if (!isExcluded(Alias, DocInfo) && canSphinxCopeWithOption(Option.Option))
F(Alias);
}
void emitOption(const DocumentedOption &Option, const Record *DocInfo,
raw_ostream &OS) {
if (isExcluded(Option.Option, DocInfo))
@ -289,17 +306,33 @@ void emitOption(const DocumentedOption &Option, const Record *DocInfo,
// 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";
std::vector<std::string> SphinxOptionIDs;
forEachOptionName(Option, DocInfo, [&](const Record *Option) {
for (auto &Prefix : Option->getValueAsListOfStrings("Prefixes"))
SphinxOptionIDs.push_back(
getSphinxOptionID(Prefix + Option->getValueAsString("Name")));
});
assert(!SphinxOptionIDs.empty() && "no flags for option");
static std::map<std::string, int> NextSuffix;
int SphinxWorkaroundSuffix = NextSuffix[*std::max_element(
SphinxOptionIDs.begin(), SphinxOptionIDs.end(),
[&](const std::string &A, const std::string &B) {
return NextSuffix[A] < NextSuffix[B];
})];
for (auto &S : SphinxOptionIDs)
NextSuffix[S] = SphinxWorkaroundSuffix + 1;
if (SphinxWorkaroundSuffix)
OS << ".. program:: " << DocInfo->getValueAsString("Program")
<< SphinxWorkaroundSuffix << "\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");
bool EmittedAny = false;
forEachOptionName(Option, DocInfo, [&](const Record *Option) {
EmittedAny = emitOptionNames(Option, OS, EmittedAny);
});
if (SphinxWorkaroundSuffix)
OS << "\n.. program:: " << DocInfo->getValueAsString("Program");
OS << "\n\n";
// Emit the description, if we have one.