Let unaliased Args track which Alias they were created from, and use that in Arg::getAsString() for diagnostics

With this, `clang-cl /source-charset:utf-16 test.cc` now prints `invalid
value 'utf-16' in '/source-charset:utf-16'` instead of `invalid value
'utf-16' in '-finput-charset=utf-16'` before, and several other clang-cl
flags produce much less confusing output as well.

Fixes PR29106.

Since an arg and its alias can have different arg types (joined vs not)
and different values (because of AliasArgs<>), I chose to give the Alias
its own Arg object. For convenience, I just store the alias directly in
the unaliased arg – there aren't many arg objects at runtime, so that
seems ok.

Finally, I changed Arg::getAsString() to use the alias's representation
if it's present – that function was already documented as being the
suitable function for diagnostics, and most callers already used it for
diagnostics.

Implementation-wise, Arg::accept() previously used to parse things as
the unaliased option. The core of that switch is now extracted into a
new function acceptInternal() which parses as the _aliased_ option, and
the previously-intermingled unaliasing is now done as an explicit step
afterwards.

(This also changes one place in lld that didn't use getAsString() for
diagnostics, so that that one place now also prints the flag as the user
wrote it, not as it looks after it went through unaliasing.)

Differential Revision: https://reviews.llvm.org/D64253

llvm-svn: 365413
This commit is contained in:
Nico Weber 2019-07-09 00:34:08 +00:00
parent 11cb39c5fc
commit e3f06b478c
10 changed files with 124 additions and 70 deletions

View File

@ -1,27 +1,27 @@
// RUN: not %clang -c -target thumbv6m-eabi -mexecute-only %s 2>&1 | \ // RUN: not %clang -c -target thumbv6m-eabi -mexecute-only %s 2>&1 | \
// RUN: FileCheck --check-prefix CHECK-EXECUTE-ONLY-NOT-SUPPORTED %s // RUN: FileCheck --check-prefix CHECK-EXECUTE-ONLY-NOT-SUPPORTED %s
// CHECK-EXECUTE-ONLY-NOT-SUPPORTED: error: execute only is not supported for the thumbv6m sub-architecture
// RUN: not %clang -target armv8m.main-eabi -mexecute-only -mno-movt %s 2>&1 \ // RUN: not %clang -target armv8m.main-eabi -mexecute-only -mno-movt %s 2>&1 \
// RUN: | FileCheck %s -check-prefix CHECK-EXECUTE-ONLY-NO-MOVT // RUN: | FileCheck %s -check-prefix CHECK-EXECUTE-ONLY-NO-MOVT
// CHECK-EXECUTE-ONLY-NO-MOVT: error: option '-mexecute-only' cannot be specified with '-mno-movt'
// RUN: not %clang -target armv8m.main-eabi -mexecute-only -mlong-calls %s 2>&1 \ // RUN: not %clang -target armv8m.main-eabi -mexecute-only -mlong-calls %s 2>&1 \
// RUN: | FileCheck %s -check-prefix CHECK-EXECUTE-ONLY-LONG-CALLS // RUN: | FileCheck %s -check-prefix CHECK-EXECUTE-ONLY-LONG-CALLS
// CHECK-EXECUTE-ONLY-LONG-CALLS: error: option '-mexecute-only' cannot be specified with '-mlong-calls'
// RUN: %clang -target armv7m-eabi -x assembler -mexecute-only %s -c -### 2>&1 \
// RUN: | FileCheck %s -check-prefix CHECK-NO-EXECUTE-ONLY -check-prefix CHECK-NO-EXECUTE-ONLY-ASM
// CHECK-NO-EXECUTE-ONLY-ASM: warning: argument unused during compilation: '-mexecute-only'
// -mpure-code flag for GCC compatibility // -mpure-code flag for GCC compatibility
// RUN: not %clang -c -target thumbv6m-eabi -mpure-code %s 2>&1 | \ // RUN: not %clang -c -target thumbv6m-eabi -mpure-code %s 2>&1 | \
// RUN: FileCheck --check-prefix CHECK-EXECUTE-ONLY-NOT-SUPPORTED %s // RUN: FileCheck --check-prefix CHECK-EXECUTE-ONLY-NOT-SUPPORTED %s
// RUN: not %clang -target armv8m.main-eabi -mpure-code -mno-movt %s 2>&1 \ // RUN: not %clang -target armv8m.main-eabi -mpure-code -mno-movt %s 2>&1 \
// RUN: | FileCheck %s -check-prefix CHECK-EXECUTE-ONLY-NO-MOVT // RUN: | FileCheck %s -check-prefix CHECK-PURE-CODE-NO-MOVT
// CHECK-PURE-CODE-NO-MOVT: error: option '-mpure-code' cannot be specified with '-mno-movt'
// RUN: not %clang -target armv8m.main-eabi -mpure-code -mlong-calls %s 2>&1 \ // RUN: not %clang -target armv8m.main-eabi -mpure-code -mlong-calls %s 2>&1 \
// RUN: | FileCheck %s -check-prefix CHECK-EXECUTE-ONLY-LONG-CALLS // RUN: | FileCheck %s -check-prefix CHECK-PURE-CODE-LONG-CALLS
// CHECK-PURE-CODE-LONG-CALLS: error: option '-mpure-code' cannot be specified with '-mlong-calls'
// RUN: %clang -target armv7m-eabi -x assembler -mexecute-only %s -c -### 2>&1 \
// RUN: | FileCheck %s -check-prefix CHECK-NO-EXECUTE-ONLY -check-prefix CHECK-NO-EXECUTE-ONLY-ASM
// CHECK-EXECUTE-ONLY-NOT-SUPPORTED: error: execute only is not supported for the thumbv6m sub-architecture
// CHECK-EXECUTE-ONLY-NO-MOVT: error: option '-mexecute-only' cannot be specified with '-mno-movt'
// CHECK-EXECUTE-ONLY-LONG-CALLS: error: option '-mexecute-only' cannot be specified with '-mlong-calls'
// CHECK-NO-EXECUTE-ONLY-ASM: warning: argument unused during compilation: '-mexecute-only'

View File

@ -8,7 +8,7 @@
// c: -c // c: -c
// RUN: %clang_cl /C -### -- %s 2>&1 | FileCheck -check-prefix=C %s // RUN: %clang_cl /C -### -- %s 2>&1 | FileCheck -check-prefix=C %s
// C: error: invalid argument '-C' only allowed with '/E, /P or /EP' // C: error: invalid argument '/C' only allowed with '/E, /P or /EP'
// RUN: %clang_cl /C /P -### -- %s 2>&1 | FileCheck -check-prefix=C_P %s // RUN: %clang_cl /C /P -### -- %s 2>&1 | FileCheck -check-prefix=C_P %s
// C_P: "-E" // C_P: "-E"
@ -214,11 +214,11 @@
// /source-charset: should warn on everything except UTF-8. // /source-charset: should warn on everything except UTF-8.
// RUN: %clang_cl /source-charset:utf-16 -### -- %s 2>&1 | FileCheck -check-prefix=source-charset-utf-16 %s // RUN: %clang_cl /source-charset:utf-16 -### -- %s 2>&1 | FileCheck -check-prefix=source-charset-utf-16 %s
// source-charset-utf-16: invalid value 'utf-16' // source-charset-utf-16: invalid value 'utf-16' in '/source-charset:utf-16'
// /execution-charset: should warn on everything except UTF-8. // /execution-charset: should warn on everything except UTF-8.
// RUN: %clang_cl /execution-charset:utf-16 -### -- %s 2>&1 | FileCheck -check-prefix=execution-charset-utf-16 %s // RUN: %clang_cl /execution-charset:utf-16 -### -- %s 2>&1 | FileCheck -check-prefix=execution-charset-utf-16 %s
// execution-charset-utf-16: invalid value 'utf-16' // execution-charset-utf-16: invalid value 'utf-16' in '/execution-charset:utf-16'
// //
// RUN: %clang_cl /Umymacro -### -- %s 2>&1 | FileCheck -check-prefix=U %s // RUN: %clang_cl /Umymacro -### -- %s 2>&1 | FileCheck -check-prefix=U %s
// RUN: %clang_cl /U mymacro -### -- %s 2>&1 | FileCheck -check-prefix=U %s // RUN: %clang_cl /U mymacro -### -- %s 2>&1 | FileCheck -check-prefix=U %s

View File

@ -18,7 +18,7 @@
// RUN: %clang -target armv7-apple-ios11.0 -c -### %s 2> %t.err // RUN: %clang -target armv7-apple-ios11.0 -c -### %s 2> %t.err
// RUN: FileCheck --input-file=%t.err --check-prefix=CHECK-VERSION-IOS41 %s // RUN: FileCheck --input-file=%t.err --check-prefix=CHECK-VERSION-IOS41 %s
// CHECK-VERSION-IOS41: invalid iOS deployment version '--target=armv7-apple-ios11.0' // CHECK-VERSION-IOS41: invalid iOS deployment version '-target armv7-apple-ios11.0'
// RUN: %clang -target armv7-apple-darwin -miphoneos-version-min=11.0 -c -### %s 2> %t.err // RUN: %clang -target armv7-apple-darwin -miphoneos-version-min=11.0 -c -### %s 2> %t.err
// RUN: FileCheck --input-file=%t.err --check-prefix=CHECK-VERSION-IOS5 %s // RUN: FileCheck --input-file=%t.err --check-prefix=CHECK-VERSION-IOS5 %s
@ -197,11 +197,11 @@
// RUN: %clang -target x86_64-apple-macos10.11.2 -mmacos-version-min=10.6 -c %s -### 2>&1 | \ // RUN: %clang -target x86_64-apple-macos10.11.2 -mmacos-version-min=10.6 -c %s -### 2>&1 | \
// RUN: FileCheck --check-prefix=CHECK-VERSION-TNO-OSV1 %s // RUN: FileCheck --check-prefix=CHECK-VERSION-TNO-OSV1 %s
// CHECK-VERSION-TNO-OSV1: overriding '-mmacosx-version-min=10.6' option with '--target=x86_64-apple-macos10.11.2' // CHECK-VERSION-TNO-OSV1: overriding '-mmacos-version-min=10.6' option with '-target x86_64-apple-macos10.11.2'
// RUN: %clang -target x86_64-apple-macos -miphoneos-version-min=9.1 -c %s -### 2>&1 | \ // RUN: %clang -target x86_64-apple-macos -miphoneos-version-min=9.1 -c %s -### 2>&1 | \
// RUN: FileCheck --check-prefix=CHECK-VERSION-TNO-OSV2 %s // RUN: FileCheck --check-prefix=CHECK-VERSION-TNO-OSV2 %s
// CHECK-VERSION-TNO-OSV2: overriding '-miphoneos-version-min=9.1' option with '--target=x86_64-apple-macos' // CHECK-VERSION-TNO-OSV2: overriding '-miphoneos-version-min=9.1' option with '-target x86_64-apple-macos'
// RUN: %clang -target x86_64-apple-ios -miphonesimulator-version-min=10.0 -c %s -### 2>&1 | \ // RUN: %clang -target x86_64-apple-ios -miphonesimulator-version-min=10.0 -c %s -### 2>&1 | \
// RUN: FileCheck --check-prefix=CHECK-VERSION-TNO-OSV3 %s // RUN: FileCheck --check-prefix=CHECK-VERSION-TNO-OSV3 %s
@ -211,7 +211,7 @@
// RUN: %clang -target arm64-apple-ios10.1.0 -miphoneos-version-min=10.1.0.1 -c %s -### 2>&1 | \ // RUN: %clang -target arm64-apple-ios10.1.0 -miphoneos-version-min=10.1.0.1 -c %s -### 2>&1 | \
// RUN: FileCheck --check-prefix=CHECK-VERSION-TNO-OSV4 %s // RUN: FileCheck --check-prefix=CHECK-VERSION-TNO-OSV4 %s
// CHECK-VERSION-TNO-OSV4: overriding '-miphoneos-version-min=10.1.0.1' option with '--target=arm64-apple-ios10.1.0' // CHECK-VERSION-TNO-OSV4: overriding '-miphoneos-version-min=10.1.0.1' option with '-target arm64-apple-ios10.1.0'
// RUN: %clang -target x86_64-apple-macos10.6 -mmacos-version-min=10.6 -c %s -### 2>&1 | \ // RUN: %clang -target x86_64-apple-macos10.6 -mmacos-version-min=10.6 -c %s -### 2>&1 | \
// RUN: FileCheck --check-prefix=CHECK-VERSION-TNO-SAME %s // RUN: FileCheck --check-prefix=CHECK-VERSION-TNO-SAME %s

View File

@ -48,6 +48,8 @@ std::string lld::quote(StringRef S) {
return S; return S;
} }
// Converts an Arg to a string representation suitable for a response file.
// To show an Arg in a diagnostic, use Arg::getAsString() instead.
std::string lld::toString(const opt::Arg &Arg) { std::string lld::toString(const opt::Arg &Arg) {
std::string K = Arg.getSpelling(); std::string K = Arg.getSpelling();
if (Arg.getNumValues() == 0) if (Arg.getNumValues() == 0)

View File

@ -588,12 +588,13 @@ static StripPolicy getStrip(opt::InputArgList &Args) {
return StripPolicy::Debug; return StripPolicy::Debug;
} }
static uint64_t parseSectionAddress(StringRef S, const opt::Arg &Arg) { static uint64_t parseSectionAddress(StringRef S, opt::InputArgList &Args,
const opt::Arg &Arg) {
uint64_t VA = 0; uint64_t VA = 0;
if (S.startswith("0x")) if (S.startswith("0x"))
S = S.drop_front(2); S = S.drop_front(2);
if (!to_integer(S, VA, 16)) if (!to_integer(S, VA, 16))
error("invalid argument: " + toString(Arg)); error("invalid argument: " + Arg.getAsString(Args));
return VA; return VA;
} }
@ -603,15 +604,15 @@ static StringMap<uint64_t> getSectionStartMap(opt::InputArgList &Args) {
StringRef Name; StringRef Name;
StringRef Addr; StringRef Addr;
std::tie(Name, Addr) = StringRef(Arg->getValue()).split('='); std::tie(Name, Addr) = StringRef(Arg->getValue()).split('=');
Ret[Name] = parseSectionAddress(Addr, *Arg); Ret[Name] = parseSectionAddress(Addr, Args, *Arg);
} }
if (auto *Arg = Args.getLastArg(OPT_Ttext)) if (auto *Arg = Args.getLastArg(OPT_Ttext))
Ret[".text"] = parseSectionAddress(Arg->getValue(), *Arg); Ret[".text"] = parseSectionAddress(Arg->getValue(), Args, *Arg);
if (auto *Arg = Args.getLastArg(OPT_Tdata)) if (auto *Arg = Args.getLastArg(OPT_Tdata))
Ret[".data"] = parseSectionAddress(Arg->getValue(), *Arg); Ret[".data"] = parseSectionAddress(Arg->getValue(), Args, *Arg);
if (auto *Arg = Args.getLastArg(OPT_Tbss)) if (auto *Arg = Args.getLastArg(OPT_Tbss))
Ret[".bss"] = parseSectionAddress(Arg->getValue(), *Arg); Ret[".bss"] = parseSectionAddress(Arg->getValue(), Args, *Arg);
return Ret; return Ret;
} }

View File

@ -45,15 +45,15 @@
# RUN: not ld.lld %t.o -Ttext=1w0000 -o %t6 2>&1 \ # RUN: not ld.lld %t.o -Ttext=1w0000 -o %t6 2>&1 \
# RUN: | FileCheck -check-prefix=ERR3 %s # RUN: | FileCheck -check-prefix=ERR3 %s
# ERR3: invalid argument: --Ttext 1w0000 # ERR3: invalid argument: -Ttext=1w0000
# RUN: not ld.lld %t.o -Tbss=1w0000 -o %t6 2>&1 \ # RUN: not ld.lld %t.o -Tbss=1w0000 -o %t6 2>&1 \
# RUN: | FileCheck -check-prefix=ERR4 %s # RUN: | FileCheck -check-prefix=ERR4 %s
# ERR4: invalid argument: --Tbss 1w0000 # ERR4: invalid argument: -Tbss=1w0000
# RUN: not ld.lld %t.o -Tdata=1w0000 -o %t6 2>&1 \ # RUN: not ld.lld %t.o -Tdata=1w0000 -o %t6 2>&1 \
# RUN: | FileCheck -check-prefix=ERR5 %s # RUN: | FileCheck -check-prefix=ERR5 %s
# ERR5: invalid argument: --Tdata 1w0000 # ERR5: invalid argument: -Tdata=1w0000
.text .text
.globl _start .globl _start

View File

@ -58,6 +58,11 @@ private:
/// The argument values, as C strings. /// The argument values, as C strings.
SmallVector<const char *, 2> Values; SmallVector<const char *, 2> Values;
/// If this arg was created through an alias, this is the original alias arg.
/// For example, *this might be "-finput-charset=utf-8" and Alias might
/// point to an arg representing "/source-charset:utf-8".
std::unique_ptr<Arg> Alias;
public: public:
Arg(const Option Opt, StringRef Spelling, unsigned Index, Arg(const Option Opt, StringRef Spelling, unsigned Index,
const Arg *BaseArg = nullptr); const Arg *BaseArg = nullptr);
@ -90,6 +95,11 @@ public:
} }
void setBaseArg(const Arg *BaseArg) { this->BaseArg = BaseArg; } void setBaseArg(const Arg *BaseArg) { this->BaseArg = BaseArg; }
/// Args are converted to their unaliased form. For args that originally
/// came from an alias, this returns the alias the arg was produced from.
const Arg* getAlias() const { return Alias.get(); }
void setAlias(std::unique_ptr<Arg> Alias) { this->Alias = std::move(Alias); }
bool getOwnsValues() const { return OwnsValues; } bool getOwnsValues() const { return OwnsValues; }
void setOwnsValues(bool Value) const { OwnsValues = Value; } void setOwnsValues(bool Value) const { OwnsValues = Value; }
@ -127,8 +137,10 @@ public:
void print(raw_ostream &O) const; void print(raw_ostream &O) const;
void dump() const; void dump() const;
/// Return a formatted version of the argument and /// Return a formatted version of the argument and its values, for
/// its values, for debugging and diagnostics. /// diagnostics. Since this is for diagnostics, if this Arg was produced
/// through an alias, this returns the string representation of the alias
/// that the user wrote.
std::string getAsString(const ArgList &Args) const; std::string getAsString(const ArgList &Args) const;
}; };

View File

@ -206,6 +206,11 @@ public:
/// start. /// start.
Arg *accept(const ArgList &Args, unsigned &Index, unsigned ArgSize) const; Arg *accept(const ArgList &Args, unsigned &Index, unsigned ArgSize) const;
private:
Arg *acceptInternal(const ArgList &Args, unsigned &Index,
unsigned ArgSize) const;
public:
void print(raw_ostream &O) const; void print(raw_ostream &O) const;
void dump() const; void dump() const;
}; };

View File

@ -66,6 +66,9 @@ LLVM_DUMP_METHOD void Arg::dump() const { print(dbgs()); }
#endif #endif
std::string Arg::getAsString(const ArgList &Args) const { std::string Arg::getAsString(const ArgList &Args) const {
if (Alias)
return Alias->getAsString(Args);
SmallString<256> Res; SmallString<256> Res;
raw_svector_ostream OS(Res); raw_svector_ostream OS(Res);

View File

@ -106,49 +106,23 @@ bool Option::matches(OptSpecifier Opt) const {
return false; return false;
} }
Arg *Option::accept(const ArgList &Args, Arg *Option::acceptInternal(const ArgList &Args, unsigned &Index,
unsigned &Index, unsigned ArgSize) const {
unsigned ArgSize) const { StringRef Spelling = StringRef(Args.getArgString(Index), ArgSize);
const Option &UnaliasedOption = getUnaliasedOption();
StringRef Spelling;
// If the option was an alias, get the spelling from the unaliased one.
if (getID() == UnaliasedOption.getID()) {
Spelling = StringRef(Args.getArgString(Index), ArgSize);
} else {
Spelling = Args.MakeArgString(Twine(UnaliasedOption.getPrefix()) +
Twine(UnaliasedOption.getName()));
}
switch (getKind()) { switch (getKind()) {
case FlagClass: { case FlagClass: {
if (ArgSize != strlen(Args.getArgString(Index))) if (ArgSize != strlen(Args.getArgString(Index)))
return nullptr; return nullptr;
return new Arg(*this, Spelling, Index++);
Arg *A = new Arg(UnaliasedOption, Spelling, Index++);
if (getAliasArgs()) {
const char *Val = getAliasArgs();
while (*Val != '\0') {
A->getValues().push_back(Val);
// Move past the '\0' to the next argument.
Val += strlen(Val) + 1;
}
}
if (UnaliasedOption.getKind() == JoinedClass && !getAliasArgs())
// A Flag alias for a Joined option must provide an argument.
A->getValues().push_back("");
return A;
} }
case JoinedClass: { case JoinedClass: {
const char *Value = Args.getArgString(Index) + ArgSize; const char *Value = Args.getArgString(Index) + ArgSize;
return new Arg(UnaliasedOption, Spelling, Index++, Value); return new Arg(*this, Spelling, Index++, Value);
} }
case CommaJoinedClass: { case CommaJoinedClass: {
// Always matches. // Always matches.
const char *Str = Args.getArgString(Index) + ArgSize; const char *Str = Args.getArgString(Index) + ArgSize;
Arg *A = new Arg(UnaliasedOption, Spelling, Index++); Arg *A = new Arg(*this, Spelling, Index++);
// Parse out the comma separated values. // Parse out the comma separated values.
const char *Prev = Str; const char *Prev = Str;
@ -184,8 +158,7 @@ Arg *Option::accept(const ArgList &Args,
Args.getArgString(Index - 1) == nullptr) Args.getArgString(Index - 1) == nullptr)
return nullptr; return nullptr;
return new Arg(UnaliasedOption, Spelling, return new Arg(*this, Spelling, Index - 2, Args.getArgString(Index - 1));
Index - 2, Args.getArgString(Index - 1));
case MultiArgClass: { case MultiArgClass: {
// Matches iff this is an exact match. // Matches iff this is an exact match.
// FIXME: Avoid strlen. // FIXME: Avoid strlen.
@ -196,8 +169,8 @@ Arg *Option::accept(const ArgList &Args,
if (Index > Args.getNumInputArgStrings()) if (Index > Args.getNumInputArgStrings())
return nullptr; return nullptr;
Arg *A = new Arg(UnaliasedOption, Spelling, Index - 1 - getNumArgs(), Arg *A = new Arg(*this, Spelling, Index - 1 - getNumArgs(),
Args.getArgString(Index - getNumArgs())); Args.getArgString(Index - getNumArgs()));
for (unsigned i = 1; i != getNumArgs(); ++i) for (unsigned i = 1; i != getNumArgs(); ++i)
A->getValues().push_back(Args.getArgString(Index - getNumArgs() + i)); A->getValues().push_back(Args.getArgString(Index - getNumArgs() + i));
return A; return A;
@ -207,7 +180,7 @@ Arg *Option::accept(const ArgList &Args,
// FIXME: Avoid strlen. // FIXME: Avoid strlen.
if (ArgSize != strlen(Args.getArgString(Index))) { if (ArgSize != strlen(Args.getArgString(Index))) {
const char *Value = Args.getArgString(Index) + ArgSize; const char *Value = Args.getArgString(Index) + ArgSize;
return new Arg(UnaliasedOption, Spelling, Index++, Value); return new Arg(*this, Spelling, Index++, Value);
} }
// Otherwise it must be separate. // Otherwise it must be separate.
@ -216,8 +189,7 @@ Arg *Option::accept(const ArgList &Args,
Args.getArgString(Index - 1) == nullptr) Args.getArgString(Index - 1) == nullptr)
return nullptr; return nullptr;
return new Arg(UnaliasedOption, Spelling, return new Arg(*this, Spelling, Index - 2, Args.getArgString(Index - 1));
Index - 2, Args.getArgString(Index - 1));
} }
case JoinedAndSeparateClass: case JoinedAndSeparateClass:
// Always matches. // Always matches.
@ -226,7 +198,7 @@ Arg *Option::accept(const ArgList &Args,
Args.getArgString(Index - 1) == nullptr) Args.getArgString(Index - 1) == nullptr)
return nullptr; return nullptr;
return new Arg(UnaliasedOption, Spelling, Index - 2, return new Arg(*this, Spelling, Index - 2,
Args.getArgString(Index - 2) + ArgSize, Args.getArgString(Index - 2) + ArgSize,
Args.getArgString(Index - 1)); Args.getArgString(Index - 1));
case RemainingArgsClass: { case RemainingArgsClass: {
@ -234,14 +206,14 @@ Arg *Option::accept(const ArgList &Args,
// FIXME: Avoid strlen. // FIXME: Avoid strlen.
if (ArgSize != strlen(Args.getArgString(Index))) if (ArgSize != strlen(Args.getArgString(Index)))
return nullptr; return nullptr;
Arg *A = new Arg(UnaliasedOption, Spelling, Index++); Arg *A = new Arg(*this, Spelling, Index++);
while (Index < Args.getNumInputArgStrings() && while (Index < Args.getNumInputArgStrings() &&
Args.getArgString(Index) != nullptr) Args.getArgString(Index) != nullptr)
A->getValues().push_back(Args.getArgString(Index++)); A->getValues().push_back(Args.getArgString(Index++));
return A; return A;
} }
case RemainingArgsJoinedClass: { case RemainingArgsJoinedClass: {
Arg *A = new Arg(UnaliasedOption, Spelling, Index); Arg *A = new Arg(*this, Spelling, Index);
if (ArgSize != strlen(Args.getArgString(Index))) { if (ArgSize != strlen(Args.getArgString(Index))) {
// An inexact match means there is a joined arg. // An inexact match means there is a joined arg.
A->getValues().push_back(Args.getArgString(Index) + ArgSize); A->getValues().push_back(Args.getArgString(Index) + ArgSize);
@ -257,3 +229,62 @@ Arg *Option::accept(const ArgList &Args,
llvm_unreachable("Invalid option kind!"); llvm_unreachable("Invalid option kind!");
} }
} }
Arg *Option::accept(const ArgList &Args,
unsigned &Index,
unsigned ArgSize) const {
std::unique_ptr<Arg> A(acceptInternal(Args, Index, ArgSize));
if (!A)
return nullptr;
const Option &UnaliasedOption = getUnaliasedOption();
if (getID() == UnaliasedOption.getID())
return A.release();
// "A" is an alias for a different flag. For most clients it's more convenient
// if this function returns unaliased Args, so create an unaliased arg for
// returning.
// This creates a completely new Arg object for the unaliased Arg because
// the alias and the unaliased arg can have different Kinds and different
// Values (due to AliasArgs<>).
// Get the spelling from the unaliased option.
StringRef UnaliasedSpelling = Args.MakeArgString(
Twine(UnaliasedOption.getPrefix()) + Twine(UnaliasedOption.getName()));
// It's a bit weird that aliased and unaliased arg share one index, but
// the index is mostly use as a memory optimization in render().
// Due to this, ArgList::getArgString(A->getIndex()) will return the spelling
// of the aliased arg always, while A->getSpelling() returns either the
// unaliased or the aliased arg, depending on which Arg object it's called on.
Arg *UnaliasedA = new Arg(UnaliasedOption, UnaliasedSpelling, A->getIndex());
Arg *RawA = A.get();
UnaliasedA->setAlias(std::move(A));
if (getKind() != FlagClass) {
// Values are usually owned by the ArgList. The exception are
// CommaJoined flags, where the Arg owns the values. For aliased flags,
// make the unaliased Arg the owner of the values.
// FIXME: There aren't many uses of CommaJoined -- try removing
// CommaJoined in favor of just calling StringRef::split(',') instead.
UnaliasedA->getValues() = RawA->getValues();
UnaliasedA->setOwnsValues(RawA->getOwnsValues());
RawA->setOwnsValues(false);
return UnaliasedA;
}
// FlagClass aliases can have AliasArgs<>; add those to the unaliased arg.
if (const char *Val = getAliasArgs()) {
while (*Val != '\0') {
UnaliasedA->getValues().push_back(Val);
// Move past the '\0' to the next argument.
Val += strlen(Val) + 1;
}
}
if (UnaliasedOption.getKind() == JoinedClass && !getAliasArgs())
// A Flag alias for a Joined option must provide an argument.
UnaliasedA->getValues().push_back("");
return UnaliasedA;
}