llvm-project/clang/lib/Driver/OptTable.cpp
Daniel Dunbar b2a7c062aa Driver: Fix a parsing bug where some options were matched
incorrectly. I'm blanking on the smartest way to write this search,
but we should just do the right thing when we move to TableGen.
 - <rdar://problem/6761194> [driver] -Wextra-tokens isn't parsed
   correctly

llvm-svn: 68525
2009-04-07 18:21:47 +00:00

265 lines
8.0 KiB
C++

//===--- Options.cpp - Option info table --------------------------------*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "clang/Driver/Options.h"
#include "clang/Driver/Arg.h"
#include "clang/Driver/ArgList.h"
#include "clang/Driver/Option.h"
#include <algorithm>
#include <cassert>
using namespace clang::driver;
using namespace clang::driver::options;
struct Info {
const char *Name;
const char *Flags;
const char *HelpText;
const char *MetaVar;
Option::OptionClass Kind;
unsigned GroupID;
unsigned AliasID;
unsigned Param;
};
// Ordering on Info. The ordering is *almost* lexicographic, with two
// exceptions. First, '\0' comes at the end of the alphabet instead of
// the beginning (thus options preceed any other options which prefix
// them). Second, for options with the same name, the less permissive
// version should come first; a Flag option should preceed a Joined
// option, for example.
static int StrCmpOptionName(const char *A, const char *B) {
char a = *A, b = *B;
while (a == b) {
if (a == '\0')
return 0;
a = *++A;
b = *++B;
}
if (a == '\0') // A is a prefix of B.
return 1;
if (b == '\0') // B is a prefix of A.
return -1;
// Otherwise lexicographic.
return (a < b) ? -1 : 1;
}
static inline bool operator<(const Info &A, const Info &B) {
if (&A == &B)
return false;
if (int N = StrCmpOptionName(A.Name, B.Name))
return N == -1;
// Names are the same, check that classes are in order; exactly one
// should be joined, and it should succeed the other.
assert(((A.Kind == Option::JoinedClass) ^ (B.Kind == Option::JoinedClass)) &&
"Unexpected classes for options with same name.");
return B.Kind == Option::JoinedClass;
}
//
static Info OptionInfos[] = {
// The InputOption info
{ "<input>", "d", 0, 0, Option::InputClass, OPT_INVALID, OPT_INVALID, 0 },
// The UnknownOption info
{ "<unknown>", "", 0, 0, Option::UnknownClass, OPT_INVALID, OPT_INVALID, 0 },
#define OPTION(NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, \
HELPTEXT, METAVAR) \
{ NAME, FLAGS, HELPTEXT, METAVAR, \
Option::KIND##Class, OPT_##GROUP, OPT_##ALIAS, PARAM },
#include "clang/Driver/Options.def"
};
static const unsigned numOptions = sizeof(OptionInfos) / sizeof(OptionInfos[0]);
static Info &getInfo(unsigned id) {
assert(id > 0 && id - 1 < numOptions && "Invalid Option ID.");
return OptionInfos[id - 1];
}
OptTable::OptTable() : Options(new Option*[numOptions]()) {
// Find start of normal options.
FirstSearchableOption = 0;
for (unsigned i = OPT_UNKNOWN + 1; i < LastOption; ++i) {
if (getInfo(i).Kind != Option::GroupClass) {
FirstSearchableOption = i;
break;
}
}
assert(FirstSearchableOption != 0 && "No searchable options?");
#ifndef NDEBUG
// Check that everything after the first searchable option is a
// regular option class.
for (unsigned i = FirstSearchableOption; i < LastOption; ++i) {
Option::OptionClass Kind = getInfo(i).Kind;
assert((Kind != Option::InputClass && Kind != Option::UnknownClass &&
Kind != Option::GroupClass) &&
"Special options should be defined first!");
}
// Check that options are in order.
for (unsigned i = FirstSearchableOption + 1; i < LastOption; ++i) {
if (!(getInfo(i - 1) < getInfo(i))) {
getOption((options::ID) (i - 1))->dump();
getOption((options::ID) i)->dump();
assert(0 && "Options are not in order!");
}
}
#endif
}
OptTable::~OptTable() {
for (unsigned i = 0; i < numOptions; ++i)
delete Options[i];
delete[] Options;
}
unsigned OptTable::getNumOptions() const {
return numOptions;
}
const char *OptTable::getOptionName(options::ID id) const {
return getInfo(id).Name;
}
unsigned OptTable::getOptionKind(options::ID id) const {
return getInfo(id).Kind;
}
const char *OptTable::getOptionHelpText(options::ID id) const {
return getInfo(id).HelpText;
}
const char *OptTable::getOptionMetaVar(options::ID id) const {
return getInfo(id).MetaVar;
}
const Option *OptTable::getOption(options::ID id) const {
if (id == OPT_INVALID)
return 0;
assert((unsigned) (id - 1) < numOptions && "Invalid ID.");
Option *&Entry = Options[id - 1];
if (!Entry)
Entry = constructOption(id);
return Entry;
}
Option *OptTable::constructOption(options::ID id) const {
Info &info = getInfo(id);
const OptionGroup *Group =
cast_or_null<OptionGroup>(getOption((options::ID) info.GroupID));
const Option *Alias = getOption((options::ID) info.AliasID);
Option *Opt = 0;
switch (info.Kind) {
case Option::InputClass:
Opt = new InputOption(); break;
case Option::UnknownClass:
Opt = new UnknownOption(); break;
case Option::GroupClass:
Opt = new OptionGroup(id, info.Name, Group); break;
case Option::FlagClass:
Opt = new FlagOption(id, info.Name, Group, Alias); break;
case Option::JoinedClass:
Opt = new JoinedOption(id, info.Name, Group, Alias); break;
case Option::SeparateClass:
Opt = new SeparateOption(id, info.Name, Group, Alias); break;
case Option::CommaJoinedClass:
Opt = new CommaJoinedOption(id, info.Name, Group, Alias); break;
case Option::MultiArgClass:
Opt = new MultiArgOption(id, info.Name, Group, Alias, info.Param); break;
case Option::JoinedOrSeparateClass:
Opt = new JoinedOrSeparateOption(id, info.Name, Group, Alias); break;
case Option::JoinedAndSeparateClass:
Opt = new JoinedAndSeparateOption(id, info.Name, Group, Alias); break;
}
for (const char *s = info.Flags; *s; ++s) {
switch (*s) {
default: assert(0 && "Invalid option flag.");
case 'J':
assert(info.Kind == Option::SeparateClass && "Invalid option.");
Opt->setForceJoinedRender(true); break;
case 'S':
assert(info.Kind == Option::JoinedClass && "Invalid option.");
Opt->setForceSeparateRender(true); break;
case 'd': Opt->setDriverOption(true); break;
case 'i': Opt->setNoOptAsInput(true); break;
case 'l': Opt->setLinkerInput(true); break;
case 'u': Opt->setUnsupported(true); break;
}
}
return Opt;
}
// Support lower_bound between info and an option name.
static inline bool operator<(struct Info &I, const char *Name) {
return StrCmpOptionName(I.Name, Name) == -1;
}
static inline bool operator<(const char *Name, struct Info &I) {
return StrCmpOptionName(Name, I.Name) == -1;
}
Arg *OptTable::ParseOneArg(const InputArgList &Args, unsigned &Index) const {
unsigned Prev = Index;
const char *Str = Args.getArgString(Index);
// Anything that doesn't start with '-' is an input, as is '-' itself.
if (Str[0] != '-' || Str[1] == '\0')
return new PositionalArg(getOption(OPT_INPUT), Index++);
struct Info *Start = OptionInfos + FirstSearchableOption - 1;
struct Info *End = OptionInfos + LastOption - 1;
// Search for the first next option which could be a prefix.
Start = std::lower_bound(Start, End, Str);
// Options are stored in sorted order, with '\0' at the end of the
// alphabet. Since the only options which can accept a string must
// prefix it, we iteratively search for the next option which could
// be a prefix.
//
// FIXME: This is searching much more than necessary, but I am
// blanking on the simplest way to make it fast. We can solve this
// problem when we move to TableGen.
for (; Start != End; ++Start) {
// Scan for first option which is a proper prefix.
for (; Start != End; ++Start)
if (memcmp(Str, Start->Name, strlen(Start->Name)) == 0)
break;
if (Start == End)
break;
// See if this option matches.
options::ID id = (options::ID) (Start - OptionInfos + 1);
if (Arg *A = getOption(id)->accept(Args, Index))
return A;
// Otherwise, see if this argument was missing values.
if (Prev != Index)
return 0;
}
return new PositionalArg(getOption(OPT_UNKNOWN), Index++);
}