diff options
author | Hamza Sood <hamza_sood@me.com> | 2018-09-09 12:06:35 +0000 |
---|---|---|
committer | Hamza Sood <hamza_sood@me.com> | 2018-09-09 12:06:35 +0000 |
commit | e978feba76c11c9882c59321a1901445555c1f89 (patch) | |
tree | 733d0bed0455f8eeff0ba1cf80cf1900b41ef274 /lib/Tooling/InterpolatingCompilationDatabase.cpp | |
parent | 22354ba448e057204e41c2e5f4bac7833a677c40 (diff) | |
download | clang-e978feba76c11c9882c59321a1901445555c1f89.tar.gz |
[Tooling] Improve handling of CL-style options
This patch fixes the handling of clang-cl options in InterpolatingCompilationDatabase.
They were previously ignored completely, which led to a lot of bugs:
Additional options were being added with the wrong syntax. E.g. a file was
specified as C++ by adding -x c++, which causes an error in CL mode.
The args were parsed and then rendered, which means that the aliasing information
was lost. E.g. /W4 was rendered to -Wall, which in CL mode means -Weverything.
CL options were ignored when checking things like -std=, so a lot of logic was
being bypassed.
Differential Revision: https://reviews.llvm.org/D51321
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@341760 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Tooling/InterpolatingCompilationDatabase.cpp')
-rw-r--r-- | lib/Tooling/InterpolatingCompilationDatabase.cpp | 153 |
1 files changed, 119 insertions, 34 deletions
diff --git a/lib/Tooling/InterpolatingCompilationDatabase.cpp b/lib/Tooling/InterpolatingCompilationDatabase.cpp index 914377cc6c..6e40a2dcee 100644 --- a/lib/Tooling/InterpolatingCompilationDatabase.cpp +++ b/lib/Tooling/InterpolatingCompilationDatabase.cpp @@ -48,6 +48,7 @@ #include "clang/Frontend/LangStandard.h" #include "clang/Tooling/CompilationDatabase.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Option/ArgList.h" @@ -127,47 +128,68 @@ struct TransferableCommand { Optional<types::ID> Type; // Standard specified by -std. LangStandard::Kind Std = LangStandard::lang_unspecified; + // Whether the command line is for the cl-compatible driver. + bool ClangCLMode; TransferableCommand(CompileCommand C) - : Cmd(std::move(C)), Type(guessType(Cmd.Filename)) { - std::vector<std::string> NewArgs = {Cmd.CommandLine.front()}; + : Cmd(std::move(C)), Type(guessType(Cmd.Filename)), + ClangCLMode(checkIsCLMode(Cmd.CommandLine)) { + std::vector<std::string> OldArgs = std::move(Cmd.CommandLine); + Cmd.CommandLine.clear(); + + // Wrap the old arguments in an InputArgList. + llvm::opt::InputArgList ArgList; + { + SmallVector<const char *, 16> TmpArgv; + for (const std::string &S : OldArgs) + TmpArgv.push_back(S.c_str()); + ArgList = {TmpArgv.begin(), TmpArgv.end()}; + } + // Parse the old args in order to strip out and record unwanted flags. + // We parse each argument individually so that we can retain the exact + // spelling of each argument; re-rendering is lossy for aliased flags. + // E.g. in CL mode, /W4 maps to -Wall. auto OptTable = clang::driver::createDriverOptTable(); - std::vector<const char *> Argv; - for (unsigned I = 1; I < Cmd.CommandLine.size(); ++I) - Argv.push_back(Cmd.CommandLine[I].c_str()); - unsigned MissingI, MissingC; - auto ArgList = OptTable->ParseArgs(Argv, MissingI, MissingC); - for (const auto *Arg : ArgList) { - const auto &option = Arg->getOption(); + Cmd.CommandLine.emplace_back(OldArgs.front()); + for (unsigned Pos = 1; Pos < OldArgs.size();) { + using namespace driver::options; + + const unsigned OldPos = Pos; + std::unique_ptr<llvm::opt::Arg> Arg(OptTable->ParseOneArg( + ArgList, Pos, + /* Include */ClangCLMode ? CoreOption | CLOption : 0, + /* Exclude */ClangCLMode ? 0 : CLOption)); + + if (!Arg) + continue; + + const llvm::opt::Option &Opt = Arg->getOption(); + // Strip input and output files. - if (option.matches(clang::driver::options::OPT_INPUT) || - option.matches(clang::driver::options::OPT_o)) { + if (Opt.matches(OPT_INPUT) || Opt.matches(OPT_o) || + (ClangCLMode && (Opt.matches(OPT__SLASH_Fa) || + Opt.matches(OPT__SLASH_Fe) || + Opt.matches(OPT__SLASH_Fi) || + Opt.matches(OPT__SLASH_Fo)))) continue; - } + // Strip -x, but record the overridden language. - if (option.matches(clang::driver::options::OPT_x)) { - for (const char *Value : Arg->getValues()) - Type = types::lookupTypeForTypeSpecifier(Value); + if (const auto GivenType = tryParseTypeArg(*Arg)) { + Type = *GivenType; continue; } - // Strip --std, but record the value. - if (option.matches(clang::driver::options::OPT_std_EQ)) { - for (const char *Value : Arg->getValues()) { - Std = llvm::StringSwitch<LangStandard::Kind>(Value) -#define LANGSTANDARD(id, name, lang, desc, features) \ - .Case(name, LangStandard::lang_##id) -#define LANGSTANDARD_ALIAS(id, alias) .Case(alias, LangStandard::lang_##id) -#include "clang/Frontend/LangStandards.def" - .Default(Std); - } + + // Strip -std, but record the value. + if (const auto GivenStd = tryParseStdArg(*Arg)) { + if (*GivenStd != LangStandard::lang_unspecified) + Std = *GivenStd; continue; } - llvm::opt::ArgStringList ArgStrs; - Arg->render(ArgList, ArgStrs); - NewArgs.insert(NewArgs.end(), ArgStrs.begin(), ArgStrs.end()); + + Cmd.CommandLine.insert(Cmd.CommandLine.end(), + &OldArgs[OldPos], &OldArgs[Pos]); } - Cmd.CommandLine = std::move(NewArgs); if (Std != LangStandard::lang_unspecified) // -std take precedence over -x Type = toType(LangStandard::getLangStandardForKind(Std).getLanguage()); @@ -188,21 +210,39 @@ struct TransferableCommand { TargetType = types::onlyPrecompileType(TargetType) // header? ? types::lookupHeaderTypeForSourceType(*Type) : *Type; - Result.CommandLine.push_back("-x"); - Result.CommandLine.push_back(types::getTypeName(TargetType)); + if (ClangCLMode) { + const StringRef Flag = toCLFlag(TargetType); + if (!Flag.empty()) + Result.CommandLine.push_back(Flag); + } else { + Result.CommandLine.push_back("-x"); + Result.CommandLine.push_back(types::getTypeName(TargetType)); + } } // --std flag may only be transferred if the language is the same. // We may consider "translating" these, e.g. c++11 -> c11. if (Std != LangStandard::lang_unspecified && foldType(TargetType) == Type) { - Result.CommandLine.push_back( - "-std=" + - std::string(LangStandard::getLangStandardForKind(Std).getName())); + Result.CommandLine.emplace_back(( + llvm::Twine(ClangCLMode ? "/std:" : "-std=") + + LangStandard::getLangStandardForKind(Std).getName()).str()); } Result.CommandLine.push_back(Filename); return Result; } private: + // Determine whether the given command line is intended for the CL driver. + static bool checkIsCLMode(ArrayRef<std::string> CmdLine) { + // First look for --driver-mode. + for (StringRef S : llvm::reverse(CmdLine)) { + if (S.consume_front("--driver-mode=")) + return S == "cl"; + } + + // Otherwise just check the clang executable file name. + return llvm::sys::path::stem(CmdLine.front()).endswith_lower("cl"); + } + // Map the language from the --std flag to that of the -x flag. static types::ID toType(InputKind::Language Lang) { switch (Lang) { @@ -218,6 +258,51 @@ private: return types::TY_INVALID; } } + + // Convert a file type to the matching CL-style type flag. + static StringRef toCLFlag(types::ID Type) { + switch (Type) { + case types::TY_C: + case types::TY_CHeader: + return "/TC"; + case types::TY_CXX: + case types::TY_CXXHeader: + return "/TP"; + default: + return StringRef(); + } + } + + // Try to interpret the argument as a type specifier, e.g. '-x'. + Optional<types::ID> tryParseTypeArg(const llvm::opt::Arg &Arg) { + const llvm::opt::Option &Opt = Arg.getOption(); + using namespace driver::options; + if (ClangCLMode) { + if (Opt.matches(OPT__SLASH_TC) || Opt.matches(OPT__SLASH_Tc)) + return types::TY_C; + if (Opt.matches(OPT__SLASH_TP) || Opt.matches(OPT__SLASH_Tp)) + return types::TY_CXX; + } else { + if (Opt.matches(driver::options::OPT_x)) + return types::lookupTypeForTypeSpecifier(Arg.getValue()); + } + return None; + } + + // Try to interpret the argument as '-std='. + Optional<LangStandard::Kind> tryParseStdArg(const llvm::opt::Arg &Arg) { + using namespace driver::options; + if (Arg.getOption().matches(ClangCLMode ? OPT__SLASH_std : OPT_std_EQ)) { + return llvm::StringSwitch<LangStandard::Kind>(Arg.getValue()) +#define LANGSTANDARD(id, name, lang, ...) .Case(name, LangStandard::lang_##id) +#define LANGSTANDARD_ALIAS(id, alias) .Case(alias, LangStandard::lang_##id) +#include "clang/Frontend/LangStandards.def" +#undef LANGSTANDARD_ALIAS +#undef LANGSTANDARD + .Default(LangStandard::lang_unspecified); + } + return None; + } }; // Given a filename, FileIndex picks the best matching file from the underlying |