Skip to content

Commit 1aa2a9e

Browse files
committed
[LLVM][Option] Support multiline option parsing
Consider arguments as belonging even if they are defined on (multiple) lines after the introducing argument. Empty lines between arguments are ignored as well.
1 parent c3acafc commit 1aa2a9e

File tree

3 files changed

+182
-30
lines changed

3 files changed

+182
-30
lines changed

llvm/lib/Option/OptTable.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,7 @@ InputArgList OptTable::internalParseArgs(
537537
MissingArgIndex = MissingArgCount = 0;
538538
unsigned Index = 0, End = ArgArr.size();
539539
while (Index < End) {
540-
// Ingore nullptrs, they are response file's EOL markers
540+
// Ignore nullptrs, they are response file's EOL markers.
541541
if (Args.getArgString(Index) == nullptr) {
542542
++Index;
543543
continue;

llvm/lib/Option/Option.cpp

Lines changed: 60 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,21 @@ bool Option::matches(OptSpecifier Opt) const {
109109
return false;
110110
}
111111

112+
/// Advances to and returns the next argument string skipping
113+
/// newlines and empty lines (which are modeled by `nullptr`
114+
/// elements in the argument list).
115+
static const char *advanceToNextArgString(const ArgList &Args,
116+
unsigned &Index) {
117+
const unsigned int NumArgs = Args.getNumInputArgStrings();
118+
while (Index <= NumArgs) {
119+
if (const char *ArgStr = Args.getArgString(Index - 1)) {
120+
return ArgStr;
121+
}
122+
++Index;
123+
}
124+
return nullptr;
125+
}
126+
112127
std::unique_ptr<Arg> Option::acceptInternal(const ArgList &Args,
113128
StringRef Spelling,
114129
unsigned &Index) const {
@@ -152,31 +167,40 @@ std::unique_ptr<Arg> Option::acceptInternal(const ArgList &Args,
152167

153168
return A;
154169
}
155-
case SeparateClass:
170+
case SeparateClass: {
156171
// Matches iff this is an exact match.
157172
if (SpellingSize != ArgStringSize)
158173
return nullptr;
159174

175+
unsigned StartIndex = Index;
160176
Index += 2;
161-
if (Index > Args.getNumInputArgStrings() ||
162-
Args.getArgString(Index - 1) == nullptr)
177+
178+
const char *ArgString = advanceToNextArgString(Args, Index);
179+
if (!ArgString)
163180
return nullptr;
164181

165-
return std::make_unique<Arg>(*this, Spelling, Index - 2,
166-
Args.getArgString(Index - 1));
182+
return std::make_unique<Arg>(*this, Spelling, StartIndex, ArgString);
183+
}
167184
case MultiArgClass: {
168185
// Matches iff this is an exact match.
169186
if (SpellingSize != ArgStringSize)
170187
return nullptr;
171188

172-
Index += 1 + getNumArgs();
173-
if (Index > Args.getNumInputArgStrings())
174-
return nullptr;
189+
unsigned StartIndex = Index;
190+
Index += 2;
191+
192+
SmallVector<const char *, 4> Values;
193+
for (unsigned i = 0; i != getNumArgs(); ++i) {
194+
const char *ArgString = advanceToNextArgString(Args, Index);
195+
if (!ArgString)
196+
return nullptr;
197+
Values.push_back(ArgString);
198+
++Index;
199+
}
175200

176-
auto A = std::make_unique<Arg>(*this, Spelling, Index - 1 - getNumArgs(),
177-
Args.getArgString(Index - getNumArgs()));
201+
auto A = std::make_unique<Arg>(*this, Spelling, StartIndex, Values[0]);
178202
for (unsigned i = 1; i != getNumArgs(); ++i)
179-
A->getValues().push_back(Args.getArgString(Index - getNumArgs() + i));
203+
A->getValues().push_back(Values[i]);
180204
return A;
181205
}
182206
case JoinedOrSeparateClass: {
@@ -187,32 +211,38 @@ std::unique_ptr<Arg> Option::acceptInternal(const ArgList &Args,
187211
}
188212

189213
// Otherwise it must be separate.
214+
unsigned StartIndex = Index;
190215
Index += 2;
191-
if (Index > Args.getNumInputArgStrings() ||
192-
Args.getArgString(Index - 1) == nullptr)
216+
217+
const char *ArgString = advanceToNextArgString(Args, Index);
218+
if (!ArgString)
193219
return nullptr;
194220

195-
return std::make_unique<Arg>(*this, Spelling, Index - 2,
196-
Args.getArgString(Index - 1));
221+
return std::make_unique<Arg>(*this, Spelling, StartIndex, ArgString);
197222
}
198-
case JoinedAndSeparateClass:
223+
case JoinedAndSeparateClass: {
199224
// Always matches.
225+
unsigned StartIndex = Index;
226+
const char *JoinedValue = Args.getArgString(StartIndex) + SpellingSize;
200227
Index += 2;
201-
if (Index > Args.getNumInputArgStrings() ||
202-
Args.getArgString(Index - 1) == nullptr)
228+
229+
const char *SeparateValue = advanceToNextArgString(Args, Index);
230+
if (!SeparateValue)
203231
return nullptr;
204232

205-
return std::make_unique<Arg>(*this, Spelling, Index - 2,
206-
Args.getArgString(Index - 2) + SpellingSize,
207-
Args.getArgString(Index - 1));
233+
return std::make_unique<Arg>(*this, Spelling, StartIndex, JoinedValue,
234+
SeparateValue);
235+
}
208236
case RemainingArgsClass: {
209237
// Matches iff this is an exact match.
210238
if (SpellingSize != ArgStringSize)
211239
return nullptr;
212-
auto A = std::make_unique<Arg>(*this, Spelling, Index++);
213-
while (Index < Args.getNumInputArgStrings() &&
214-
Args.getArgString(Index) != nullptr)
215-
A->getValues().push_back(Args.getArgString(Index++));
240+
auto A = std::make_unique<Arg>(*this, Spelling, Index);
241+
Index += 2;
242+
while (const char *ArgString = advanceToNextArgString(Args, Index)) {
243+
A->getValues().push_back(ArgString);
244+
Index++;
245+
}
216246
return A;
217247
}
218248
case RemainingArgsJoinedClass: {
@@ -221,10 +251,11 @@ std::unique_ptr<Arg> Option::acceptInternal(const ArgList &Args,
221251
// An inexact match means there is a joined arg.
222252
A->getValues().push_back(Args.getArgString(Index) + SpellingSize);
223253
}
224-
Index++;
225-
while (Index < Args.getNumInputArgStrings() &&
226-
Args.getArgString(Index) != nullptr)
227-
A->getValues().push_back(Args.getArgString(Index++));
254+
Index += 2;
255+
while (const char *ArgString = advanceToNextArgString(Args, Index)) {
256+
A->getValues().push_back(ArgString);
257+
Index++;
258+
}
228259
return A;
229260
}
230261

llvm/unittests/Option/OptionParsingTest.cpp

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,3 +559,124 @@ USAGE: usage
559559
multiple lines in it
560560
)");
561561
}
562+
563+
TYPED_TEST(OptTableTest, ResponseFileNullptrMarkers) {
564+
TypeParam T;
565+
unsigned MAI, MAC;
566+
567+
// This simulates a response file with:
568+
// -C
569+
// value
570+
const char *Args1[] = {"-C", nullptr, "value"};
571+
InputArgList AL = T.ParseArgs(Args1, MAI, MAC);
572+
EXPECT_TRUE(AL.hasArg(OPT_C));
573+
EXPECT_EQ("value", AL.getLastArgValue(OPT_C));
574+
EXPECT_EQ(0U, MAC); // No missing arguments
575+
576+
// This simulates blank lines in a response file:
577+
// -C
578+
//
579+
//
580+
// value
581+
const char *Args2[] = {"-C", nullptr, nullptr, nullptr, "value"};
582+
InputArgList AL2 = T.ParseArgs(Args2, MAI, MAC);
583+
EXPECT_TRUE(AL2.hasArg(OPT_C));
584+
EXPECT_EQ("value", AL2.getLastArgValue(OPT_C));
585+
EXPECT_EQ(0U, MAC);
586+
587+
// This simulates a response file with -E takes 2 arguments:
588+
// -E
589+
// arg1
590+
// arg2
591+
const char *Args3[] = {"-E", nullptr, "arg1", nullptr, "arg2"};
592+
InputArgList AL3 = T.ParseArgs(Args3, MAI, MAC);
593+
EXPECT_TRUE(AL3.hasArg(OPT_E));
594+
std::vector<std::string> EValues = AL3.getAllArgValues(OPT_E);
595+
ASSERT_EQ(2U, EValues.size());
596+
EXPECT_EQ("arg1", EValues[0]);
597+
EXPECT_EQ("arg2", EValues[1]);
598+
EXPECT_EQ(0U, MAC);
599+
600+
// -F can be either joined or separate, test the separate case:
601+
// -F
602+
// value
603+
const char *Args4[] = {"-F", nullptr, "value"};
604+
InputArgList AL4 = T.ParseArgs(Args4, MAI, MAC);
605+
EXPECT_TRUE(AL4.hasArg(OPT_F));
606+
EXPECT_EQ("value", AL4.getLastArgValue(OPT_F));
607+
EXPECT_EQ(0U, MAC);
608+
609+
// Test that missing argument is still detected correctly with missing
610+
// arguments at the end.
611+
const char *Args5[] = {"-C", nullptr, nullptr};
612+
InputArgList AL5 = T.ParseArgs(Args5, MAI, MAC);
613+
EXPECT_FALSE(AL5.hasArg(OPT_C));
614+
EXPECT_GT(MAC, 0U);
615+
EXPECT_EQ(0U, MAI);
616+
617+
// -G joined to "joined" and followed by "separate":
618+
// -Gjoined
619+
//
620+
// separate
621+
const char *Args7[] = {"-Gjoined", nullptr, nullptr, "separate"};
622+
InputArgList AL7 = T.ParseArgs(Args7, MAI, MAC);
623+
EXPECT_TRUE(AL7.hasArg(OPT_G));
624+
EXPECT_EQ("joined", AL7.getLastArgValue(OPT_G));
625+
EXPECT_EQ(2U, AL7.getAllArgValues(OPT_G).size());
626+
EXPECT_EQ("joined", AL7.getAllArgValues(OPT_G)[0]);
627+
EXPECT_EQ("separate", AL7.getAllArgValues(OPT_G)[1]);
628+
EXPECT_EQ(0U, MAC);
629+
630+
// -slurp consumes all remaining args, even with empty lines in between:
631+
// -slurp
632+
// arg1
633+
//
634+
// arg2
635+
const char *Args8[] = {"-slurp", "arg1", nullptr, nullptr, "arg2"};
636+
InputArgList AL8 = T.ParseArgs(Args8, MAI, MAC);
637+
EXPECT_TRUE(AL8.hasArg(OPT_Slurp));
638+
std::vector<std::string> SlurpValues = AL8.getAllArgValues(OPT_Slurp);
639+
ASSERT_EQ(2U, SlurpValues.size());
640+
EXPECT_EQ("arg1", SlurpValues[0]);
641+
EXPECT_EQ("arg2", SlurpValues[1]);
642+
EXPECT_EQ(0U, MAC);
643+
644+
// -slurpjoined consumes joined arg and all remaining args:
645+
// -slurpjoinedjoined
646+
// arg1
647+
//
648+
// arg2
649+
const char *Args9[] = {"-slurpjoinedjoined", "arg1", nullptr, nullptr,
650+
"arg2"};
651+
InputArgList AL9 = T.ParseArgs(Args9, MAI, MAC);
652+
EXPECT_TRUE(AL9.hasArg(OPT_SlurpJoined));
653+
std::vector<std::string> SlurpJoinedValues =
654+
AL9.getAllArgValues(OPT_SlurpJoined);
655+
ASSERT_EQ(3U, SlurpJoinedValues.size());
656+
EXPECT_EQ("joined", SlurpJoinedValues[0]);
657+
EXPECT_EQ("arg1", SlurpJoinedValues[1]);
658+
EXPECT_EQ("arg2", SlurpJoinedValues[2]);
659+
EXPECT_EQ(0U, MAC);
660+
}
661+
662+
TYPED_TEST(OptTableTest, ResponseFileIntegration) {
663+
TypeParam T;
664+
unsigned MAI, MAC;
665+
666+
const char *ExpandedArgs[] = {
667+
"-A", nullptr,
668+
"-C", nullptr,
669+
nullptr,
670+
"cvalue", nullptr,
671+
"-F", nullptr,
672+
"fvalue",
673+
};
674+
675+
InputArgList AL = T.ParseArgs(ExpandedArgs, MAI, MAC);
676+
EXPECT_TRUE(AL.hasArg(OPT_A));
677+
EXPECT_TRUE(AL.hasArg(OPT_C));
678+
EXPECT_TRUE(AL.hasArg(OPT_F));
679+
EXPECT_EQ("cvalue", AL.getLastArgValue(OPT_C));
680+
EXPECT_EQ("fvalue", AL.getLastArgValue(OPT_F));
681+
EXPECT_EQ(0U, MAC);
682+
}

0 commit comments

Comments
 (0)