Skip to content

Commit db25a6a

Browse files
committed
Introduce API to check if a subcommand is registered with a given option.
1 parent 8d11673 commit db25a6a

File tree

6 files changed

+64
-28
lines changed

6 files changed

+64
-28
lines changed

llvm/examples/OptSubcommand/llvm-hello-sub.cpp

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -78,17 +78,30 @@ int main(int argc, char **argv) {
7878
InputArgList Args = T.ParseArgs(ArrayRef(argv + 1, argc - 1), MissingArgIndex,
7979
MissingArgCount);
8080

81-
StringRef Subcommand = Args.getSubcommand(
81+
StringRef SubCommand = Args.getSubCommand(
8282
T.getSubCommands(), HandleMultipleSubcommands, HandleOtherPositionals);
8383
// Handle help. When help options is found, ignore all other options and exit
8484
// after printing help.
8585

8686
if (Args.hasArg(OPT_help)) {
8787
T.printHelp(llvm::outs(), "llvm-hello-sub [subcommand] [options]",
88-
"LLVM Hello Subcommand Example", false, false, Visibility(),
89-
Subcommand);
88+
"LLVM Hello SubCommand Example", false, false, Visibility(),
89+
SubCommand);
9090
return 0;
9191
}
92+
93+
auto HandleSubCommandArg = [&](ID OptionType) {
94+
if (!Args.hasArg(OptionType))
95+
return false;
96+
auto O = T.getOption(OptionType);
97+
if (!O.isRegisteredSC(SubCommand)) {
98+
llvm::errs() << "Option [" << O.getName()
99+
<< "] is not valid for SubCommand [" << SubCommand << "]\n";
100+
return false;
101+
}
102+
return true;
103+
};
104+
92105
bool HasUnknownOptions = false;
93106
for (const Arg *A : Args.filtered(OPT_UNKNOWN)) {
94107
HasUnknownOptions = true;
@@ -98,26 +111,26 @@ int main(int argc, char **argv) {
98111
llvm::errs() << "See `OptSubcommand --help`.\n";
99112
return 1;
100113
}
101-
if (Subcommand.empty()) {
114+
if (SubCommand.empty()) {
102115
if (Args.hasArg(OPT_version))
103-
llvm::outs() << "LLVM Hello Subcommand Example 1.0\n";
104-
} else if (Subcommand == "foo") {
105-
if (Args.hasArg(OPT_uppercase))
116+
llvm::outs() << "LLVM Hello SubCommand Example 1.0\n";
117+
} else if (SubCommand == "foo") {
118+
if (HandleSubCommandArg(OPT_uppercase))
106119
llvm::outs() << "FOO\n";
107-
else if (Args.hasArg(OPT_lowercase))
120+
else if (HandleSubCommandArg(OPT_lowercase))
108121
llvm::outs() << "foo\n";
109122

110-
if (Args.hasArg(OPT_version))
111-
llvm::outs() << "LLVM Hello Subcommand foo Example 1.0\n";
123+
if (HandleSubCommandArg(OPT_version))
124+
llvm::outs() << "LLVM Hello SubCommand foo Example 1.0\n";
112125

113-
} else if (Subcommand == "bar") {
114-
if (Args.hasArg(OPT_lowercase))
126+
} else if (SubCommand == "bar") {
127+
if (HandleSubCommandArg(OPT_lowercase))
115128
llvm::outs() << "bar\n";
116-
else if (Args.hasArg(OPT_uppercase))
129+
else if (HandleSubCommandArg(OPT_uppercase))
117130
llvm::outs() << "BAR\n";
118131

119-
if (Args.hasArg(OPT_version))
120-
llvm::outs() << "LLVM Hello Subcommand bar Example 1.0\n";
132+
if (HandleSubCommandArg(OPT_version))
133+
llvm::outs() << "LLVM Hello SubCommand bar Example 1.0\n";
121134
}
122135

123136
return 0;

llvm/include/llvm/Option/ArgList.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ class ArgList {
281281
/// list.
282282
virtual unsigned getNumInputArgStrings() const = 0;
283283

284-
/// getSubcommand - Find subcommand from the arguments if the usage is valid.
284+
/// getSubCommand - Find subcommand from the arguments if the usage is valid.
285285
///
286286
/// \param AllSubCommands - A list of all valid subcommands.
287287
/// \param HandleMultipleSubcommands - A callback for the case where multiple
@@ -292,7 +292,7 @@ class ArgList {
292292
/// \return The name of the subcommand found. If no subcommand is found,
293293
/// this returns an empty StringRef. If multiple subcommands are found, the
294294
/// first one is returned.
295-
StringRef getSubcommand(
295+
StringRef getSubCommand(
296296
ArrayRef<OptTable::SubCommand> AllSubCommands,
297297
std::function<void(ArrayRef<StringRef>)> HandleMultipleSubcommands,
298298
std::function<void(ArrayRef<StringRef>)> HandleOtherPositionals) const;

llvm/include/llvm/Option/OptTable.h

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ class LLVM_ABI OptTable {
8686
unsigned short AliasID;
8787
const char *AliasArgs;
8888
const char *Values;
89+
// Offset into OptTable's SubCommandIDsTable.
8990
unsigned SubCommandIDsOffset;
9091

9192
bool hasNoPrefix() const { return PrefixesOffset == 0; }
@@ -102,7 +103,7 @@ class LLVM_ABI OptTable {
102103
getNumPrefixes(PrefixesTable));
103104
}
104105

105-
bool hasCommands() const { return SubCommandIDsOffset != 0; }
106+
bool hasSubCommands() const { return SubCommandIDsOffset != 0; }
106107

107108
unsigned getNumCommandIDs(ArrayRef<unsigned> SubCommandIDsTable) const {
108109
// We embed the number of subcommand IDs in the value of the first offset.
@@ -111,10 +112,10 @@ class LLVM_ABI OptTable {
111112

112113
ArrayRef<unsigned>
113114
getCommandIDs(ArrayRef<unsigned> SubCommandIDsTable) const {
114-
return hasCommands() ? SubCommandIDsTable.slice(
115-
SubCommandIDsOffset + 1,
116-
getNumCommandIDs(SubCommandIDsTable))
117-
: ArrayRef<unsigned>();
115+
return hasSubCommands() ? SubCommandIDsTable.slice(
116+
SubCommandIDsOffset + 1,
117+
getNumCommandIDs(SubCommandIDsTable))
118+
: ArrayRef<unsigned>();
118119
}
119120

120121
void appendPrefixes(const StringTable &StrTable,
@@ -142,6 +143,22 @@ class LLVM_ABI OptTable {
142143
}
143144
};
144145

146+
public:
147+
bool isValidForSubCommand(const Info *CandidateInfo,
148+
StringRef SubCommand) const {
149+
assert(!SubCommand.empty() &&
150+
"This helper is only for valid registered subcommands.");
151+
typename ArrayRef<OptTable::SubCommand>::iterator SCIT =
152+
std::find_if(SubCommands.begin(), SubCommands.end(),
153+
[&](const auto &C) { return SubCommand == C.Name; });
154+
assert(SCIT != SubCommands.end() &&
155+
"This helper is only for valid registered subcommands.");
156+
auto SubCommandIDs = CandidateInfo->getCommandIDs(SubCommandIDsTable);
157+
unsigned CurrentSubCommandID = SCIT - &SubCommands[0];
158+
return std::find(SubCommandIDs.begin(), SubCommandIDs.end(),
159+
CurrentSubCommandID) != SubCommandIDs.end();
160+
}
161+
145162
private:
146163
// A unified string table for these options. Individual strings are stored as
147164
// null terminated C-strings at offsets within this table.

llvm/include/llvm/Option/Option.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,12 @@ class Option {
216216
/// always be false.
217217
LLVM_ABI bool matches(OptSpecifier ID) const;
218218

219+
LLVM_ABI bool isRegisteredSC(StringRef SubCommand) const {
220+
assert(Info && "Must have a valid info!");
221+
assert(Owner && "Must have a valid owner!");
222+
return Owner->isValidForSubCommand(Info, SubCommand);
223+
}
224+
219225
/// Potentially accept the current argument, returning a new Arg instance,
220226
/// or 0 if the option does not accept this argument (or the argument is
221227
/// missing values).

llvm/lib/Option/ArgList.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ void ArgList::print(raw_ostream &O) const {
204204
LLVM_DUMP_METHOD void ArgList::dump() const { print(dbgs()); }
205205
#endif
206206

207-
StringRef ArgList::getSubcommand(
207+
StringRef ArgList::getSubCommand(
208208
ArrayRef<OptTable::SubCommand> AllSubCommands,
209209
std::function<void(ArrayRef<StringRef>)> HandleMultipleSubcommands,
210210
std::function<void(ArrayRef<StringRef>)> HandleOtherPositionals) const {

llvm/unittests/Option/OptionSubCommandsTest.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ TYPED_TEST(OptSubCommandTableTest, SubCommandParsing) {
9595
const char *Args[] = {"-version"};
9696
InputArgList AL = T.ParseArgs(Args, MAI, MAC);
9797
EXPECT_TRUE(AL.hasArg(OPT_version));
98-
StringRef SC = AL.getSubcommand(
98+
StringRef SC = AL.getSubCommand(
9999
T.getSubCommands(), HandleMultipleSubcommands, HandleOtherPositionals);
100100
EXPECT_TRUE(SC.empty());
101101
EXPECT_FALSE(AL.hasArg(OPT_uppercase));
@@ -106,7 +106,7 @@ TYPED_TEST(OptSubCommandTableTest, SubCommandParsing) {
106106
// Test case 2: Subcommand 'foo' with its valid options
107107
const char *Args[] = {"foo", "-uppercase"};
108108
InputArgList AL = T.ParseArgs(Args, MAI, MAC);
109-
StringRef SC = AL.getSubcommand(
109+
StringRef SC = AL.getSubCommand(
110110
T.getSubCommands(), HandleMultipleSubcommands, HandleOtherPositionals);
111111
EXPECT_EQ(SC, "foo");
112112
EXPECT_TRUE(AL.hasArg(OPT_uppercase));
@@ -123,7 +123,7 @@ TYPED_TEST(OptSubCommandTableTest, SubCommandParsing) {
123123
// subcommand option.
124124
const char *Args[] = {"-uppercase", "foo"};
125125
InputArgList AL = T.ParseArgs(Args, MAI, MAC);
126-
StringRef SC = AL.getSubcommand(
126+
StringRef SC = AL.getSubCommand(
127127
T.getSubCommands(), HandleMultipleSubcommands, HandleOtherPositionals);
128128
EXPECT_EQ(SC, "foo");
129129
EXPECT_TRUE(AL.hasArg(OPT_uppercase));
@@ -139,7 +139,7 @@ TYPED_TEST(OptSubCommandTableTest, SubCommandParsing) {
139139
// Test case 4: Check invalid use of passing multiple subcommands.
140140
const char *Args[] = {"-uppercase", "foo", "bar"};
141141
InputArgList AL = T.ParseArgs(Args, MAI, MAC);
142-
StringRef SC = AL.getSubcommand(
142+
StringRef SC = AL.getSubCommand(
143143
T.getSubCommands(), HandleMultipleSubcommands, HandleOtherPositionals);
144144
// No valid subcommand should be returned as this is an invalid invocation.
145145
EXPECT_TRUE(SC.empty());
@@ -155,7 +155,7 @@ TYPED_TEST(OptSubCommandTableTest, SubCommandParsing) {
155155
// Test case 5: Check invalid use of passing unregistered subcommands.
156156
const char *Args[] = {"foobar"};
157157
InputArgList AL = T.ParseArgs(Args, MAI, MAC);
158-
StringRef SC = AL.getSubcommand(
158+
StringRef SC = AL.getSubCommand(
159159
T.getSubCommands(), HandleMultipleSubcommands, HandleOtherPositionals);
160160
// No valid subcommand should be returned as this is an invalid invocation.
161161
EXPECT_TRUE(SC.empty());

0 commit comments

Comments
 (0)