1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-19 11:02:59 +02:00
llvm-mirror/lib/Option/Option.cpp
Nico Weber 1254b69520 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
2019-07-09 00:34:08 +00:00

291 lines
9.1 KiB
C++

//===- Option.cpp - Abstract Driver Options -------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Option/Option.h"
#include "llvm/Option/OptTable.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include <cassert>
#include <cstring>
using namespace llvm;
using namespace llvm::opt;
Option::Option(const OptTable::Info *info, const OptTable *owner)
: Info(info), Owner(owner) {
// Multi-level aliases are not supported. This just simplifies option
// tracking, it is not an inherent limitation.
assert((!Info || !getAlias().isValid() || !getAlias().getAlias().isValid()) &&
"Multi-level aliases are not supported.");
if (Info && getAliasArgs()) {
assert(getAlias().isValid() && "Only alias options can have alias args.");
assert(getKind() == FlagClass && "Only Flag aliases can have alias args.");
assert(getAlias().getKind() != FlagClass &&
"Cannot provide alias args to a flag option.");
}
}
void Option::print(raw_ostream &O) const {
O << "<";
switch (getKind()) {
#define P(N) case N: O << #N; break
P(GroupClass);
P(InputClass);
P(UnknownClass);
P(FlagClass);
P(JoinedClass);
P(ValuesClass);
P(SeparateClass);
P(CommaJoinedClass);
P(MultiArgClass);
P(JoinedOrSeparateClass);
P(JoinedAndSeparateClass);
P(RemainingArgsClass);
P(RemainingArgsJoinedClass);
#undef P
}
if (Info->Prefixes) {
O << " Prefixes:[";
for (const char *const *Pre = Info->Prefixes; *Pre != nullptr; ++Pre) {
O << '"' << *Pre << (*(Pre + 1) == nullptr ? "\"" : "\", ");
}
O << ']';
}
O << " Name:\"" << getName() << '"';
const Option Group = getGroup();
if (Group.isValid()) {
O << " Group:";
Group.print(O);
}
const Option Alias = getAlias();
if (Alias.isValid()) {
O << " Alias:";
Alias.print(O);
}
if (getKind() == MultiArgClass)
O << " NumArgs:" << getNumArgs();
O << ">\n";
}
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
LLVM_DUMP_METHOD void Option::dump() const { print(dbgs()); }
#endif
bool Option::matches(OptSpecifier Opt) const {
// Aliases are never considered in matching, look through them.
const Option Alias = getAlias();
if (Alias.isValid())
return Alias.matches(Opt);
// Check exact match.
if (getID() == Opt.getID())
return true;
const Option Group = getGroup();
if (Group.isValid())
return Group.matches(Opt);
return false;
}
Arg *Option::acceptInternal(const ArgList &Args, unsigned &Index,
unsigned ArgSize) const {
StringRef Spelling = StringRef(Args.getArgString(Index), ArgSize);
switch (getKind()) {
case FlagClass: {
if (ArgSize != strlen(Args.getArgString(Index)))
return nullptr;
return new Arg(*this, Spelling, Index++);
}
case JoinedClass: {
const char *Value = Args.getArgString(Index) + ArgSize;
return new Arg(*this, Spelling, Index++, Value);
}
case CommaJoinedClass: {
// Always matches.
const char *Str = Args.getArgString(Index) + ArgSize;
Arg *A = new Arg(*this, Spelling, Index++);
// Parse out the comma separated values.
const char *Prev = Str;
for (;; ++Str) {
char c = *Str;
if (!c || c == ',') {
if (Prev != Str) {
char *Value = new char[Str - Prev + 1];
memcpy(Value, Prev, Str - Prev);
Value[Str - Prev] = '\0';
A->getValues().push_back(Value);
}
if (!c)
break;
Prev = Str + 1;
}
}
A->setOwnsValues(true);
return A;
}
case SeparateClass:
// Matches iff this is an exact match.
// FIXME: Avoid strlen.
if (ArgSize != strlen(Args.getArgString(Index)))
return nullptr;
Index += 2;
if (Index > Args.getNumInputArgStrings() ||
Args.getArgString(Index - 1) == nullptr)
return nullptr;
return new Arg(*this, Spelling, Index - 2, Args.getArgString(Index - 1));
case MultiArgClass: {
// Matches iff this is an exact match.
// FIXME: Avoid strlen.
if (ArgSize != strlen(Args.getArgString(Index)))
return nullptr;
Index += 1 + getNumArgs();
if (Index > Args.getNumInputArgStrings())
return nullptr;
Arg *A = new Arg(*this, Spelling, Index - 1 - getNumArgs(),
Args.getArgString(Index - getNumArgs()));
for (unsigned i = 1; i != getNumArgs(); ++i)
A->getValues().push_back(Args.getArgString(Index - getNumArgs() + i));
return A;
}
case JoinedOrSeparateClass: {
// If this is not an exact match, it is a joined arg.
// FIXME: Avoid strlen.
if (ArgSize != strlen(Args.getArgString(Index))) {
const char *Value = Args.getArgString(Index) + ArgSize;
return new Arg(*this, Spelling, Index++, Value);
}
// Otherwise it must be separate.
Index += 2;
if (Index > Args.getNumInputArgStrings() ||
Args.getArgString(Index - 1) == nullptr)
return nullptr;
return new Arg(*this, Spelling, Index - 2, Args.getArgString(Index - 1));
}
case JoinedAndSeparateClass:
// Always matches.
Index += 2;
if (Index > Args.getNumInputArgStrings() ||
Args.getArgString(Index - 1) == nullptr)
return nullptr;
return new Arg(*this, Spelling, Index - 2,
Args.getArgString(Index - 2) + ArgSize,
Args.getArgString(Index - 1));
case RemainingArgsClass: {
// Matches iff this is an exact match.
// FIXME: Avoid strlen.
if (ArgSize != strlen(Args.getArgString(Index)))
return nullptr;
Arg *A = new Arg(*this, Spelling, Index++);
while (Index < Args.getNumInputArgStrings() &&
Args.getArgString(Index) != nullptr)
A->getValues().push_back(Args.getArgString(Index++));
return A;
}
case RemainingArgsJoinedClass: {
Arg *A = new Arg(*this, Spelling, Index);
if (ArgSize != strlen(Args.getArgString(Index))) {
// An inexact match means there is a joined arg.
A->getValues().push_back(Args.getArgString(Index) + ArgSize);
}
Index++;
while (Index < Args.getNumInputArgStrings() &&
Args.getArgString(Index) != nullptr)
A->getValues().push_back(Args.getArgString(Index++));
return A;
}
default:
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;
}