10b57cec5SDimitry Andric //===- ArgumentsAdjusters.cpp - Command line arguments adjuster -----------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file contains definitions of classes which implement ArgumentsAdjuster
100b57cec5SDimitry Andric // interface.
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric
140b57cec5SDimitry Andric #include "clang/Tooling/ArgumentsAdjusters.h"
150b57cec5SDimitry Andric #include "clang/Basic/LLVM.h"
16480093f4SDimitry Andric #include "llvm/ADT/STLExtras.h"
170b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h"
180b57cec5SDimitry Andric #include <cstddef>
19480093f4SDimitry Andric #include <vector>
200b57cec5SDimitry Andric
210b57cec5SDimitry Andric namespace clang {
220b57cec5SDimitry Andric namespace tooling {
230b57cec5SDimitry Andric
getDriverMode(const CommandLineArguments & Args)24e8d8bef9SDimitry Andric static StringRef getDriverMode(const CommandLineArguments &Args) {
25e8d8bef9SDimitry Andric for (const auto &Arg : Args) {
26e8d8bef9SDimitry Andric StringRef ArgRef = Arg;
27e8d8bef9SDimitry Andric if (ArgRef.consume_front("--driver-mode=")) {
28e8d8bef9SDimitry Andric return ArgRef;
29e8d8bef9SDimitry Andric }
30e8d8bef9SDimitry Andric }
31e8d8bef9SDimitry Andric return StringRef();
32e8d8bef9SDimitry Andric }
33e8d8bef9SDimitry Andric
34480093f4SDimitry Andric /// Add -fsyntax-only option and drop options that triggers output generation.
getClangSyntaxOnlyAdjuster()350b57cec5SDimitry Andric ArgumentsAdjuster getClangSyntaxOnlyAdjuster() {
360b57cec5SDimitry Andric return [](const CommandLineArguments &Args, StringRef /*unused*/) {
370b57cec5SDimitry Andric CommandLineArguments AdjustedArgs;
380b57cec5SDimitry Andric bool HasSyntaxOnly = false;
395ffd83dbSDimitry Andric constexpr llvm::StringRef OutputCommands[] = {
40480093f4SDimitry Andric // FIXME: Add other options that generate output.
41480093f4SDimitry Andric "-save-temps",
42480093f4SDimitry Andric "--save-temps",
43480093f4SDimitry Andric };
440b57cec5SDimitry Andric for (size_t i = 0, e = Args.size(); i < e; ++i) {
450b57cec5SDimitry Andric StringRef Arg = Args[i];
46480093f4SDimitry Andric // Skip output commands.
47480093f4SDimitry Andric if (llvm::any_of(OutputCommands, [&Arg](llvm::StringRef OutputCommand) {
48*5f757f3fSDimitry Andric return Arg.starts_with(OutputCommand);
49480093f4SDimitry Andric }))
50480093f4SDimitry Andric continue;
51480093f4SDimitry Andric
52*5f757f3fSDimitry Andric if (!Arg.starts_with("-fcolor-diagnostics") &&
53*5f757f3fSDimitry Andric !Arg.starts_with("-fdiagnostics-color"))
540b57cec5SDimitry Andric AdjustedArgs.push_back(Args[i]);
5547395794SDimitry Andric // If we strip a color option, make sure we strip any preceeding `-Xclang`
5647395794SDimitry Andric // option as well.
5747395794SDimitry Andric // FIXME: This should be added to most argument adjusters!
5847395794SDimitry Andric else if (!AdjustedArgs.empty() && AdjustedArgs.back() == "-Xclang")
5947395794SDimitry Andric AdjustedArgs.pop_back();
6047395794SDimitry Andric
610b57cec5SDimitry Andric if (Arg == "-fsyntax-only")
620b57cec5SDimitry Andric HasSyntaxOnly = true;
630b57cec5SDimitry Andric }
640b57cec5SDimitry Andric if (!HasSyntaxOnly)
65fe6060f1SDimitry Andric AdjustedArgs =
66fe6060f1SDimitry Andric getInsertArgumentAdjuster("-fsyntax-only")(AdjustedArgs, "");
670b57cec5SDimitry Andric return AdjustedArgs;
680b57cec5SDimitry Andric };
690b57cec5SDimitry Andric }
700b57cec5SDimitry Andric
getClangStripOutputAdjuster()710b57cec5SDimitry Andric ArgumentsAdjuster getClangStripOutputAdjuster() {
720b57cec5SDimitry Andric return [](const CommandLineArguments &Args, StringRef /*unused*/) {
730b57cec5SDimitry Andric CommandLineArguments AdjustedArgs;
740b57cec5SDimitry Andric for (size_t i = 0, e = Args.size(); i < e; ++i) {
750b57cec5SDimitry Andric StringRef Arg = Args[i];
76*5f757f3fSDimitry Andric if (!Arg.starts_with("-o"))
770b57cec5SDimitry Andric AdjustedArgs.push_back(Args[i]);
780b57cec5SDimitry Andric
790b57cec5SDimitry Andric if (Arg == "-o") {
800b57cec5SDimitry Andric // Output is specified as -o foo. Skip the next argument too.
810b57cec5SDimitry Andric ++i;
820b57cec5SDimitry Andric }
830b57cec5SDimitry Andric // Else, the output is specified as -ofoo. Just do nothing.
840b57cec5SDimitry Andric }
850b57cec5SDimitry Andric return AdjustedArgs;
860b57cec5SDimitry Andric };
870b57cec5SDimitry Andric }
880b57cec5SDimitry Andric
getClangStripDependencyFileAdjuster()890b57cec5SDimitry Andric ArgumentsAdjuster getClangStripDependencyFileAdjuster() {
900b57cec5SDimitry Andric return [](const CommandLineArguments &Args, StringRef /*unused*/) {
91e8d8bef9SDimitry Andric auto UsingClDriver = (getDriverMode(Args) == "cl");
92e8d8bef9SDimitry Andric
930b57cec5SDimitry Andric CommandLineArguments AdjustedArgs;
940b57cec5SDimitry Andric for (size_t i = 0, e = Args.size(); i < e; ++i) {
950b57cec5SDimitry Andric StringRef Arg = Args[i];
96e8d8bef9SDimitry Andric
97e8d8bef9SDimitry Andric // These flags take an argument: -MX foo. Skip the next argument also.
98e8d8bef9SDimitry Andric if (!UsingClDriver && (Arg == "-MF" || Arg == "-MT" || Arg == "-MQ")) {
99e8d8bef9SDimitry Andric ++i;
1000b57cec5SDimitry Andric continue;
1010b57cec5SDimitry Andric }
102e8d8bef9SDimitry Andric // When not using the cl driver mode, dependency file generation options
103e8d8bef9SDimitry Andric // begin with -M. These include -MM, -MF, -MG, -MP, -MT, -MQ, -MD, and
104e8d8bef9SDimitry Andric // -MMD.
105*5f757f3fSDimitry Andric if (!UsingClDriver && Arg.starts_with("-M"))
106e8d8bef9SDimitry Andric continue;
107e8d8bef9SDimitry Andric // Under MSVC's cl driver mode, dependency file generation is controlled
108e8d8bef9SDimitry Andric // using /showIncludes
109*5f757f3fSDimitry Andric if (Arg.starts_with("/showIncludes") || Arg.starts_with("-showIncludes"))
110e8d8bef9SDimitry Andric continue;
1110b57cec5SDimitry Andric
112e8d8bef9SDimitry Andric AdjustedArgs.push_back(Args[i]);
1130b57cec5SDimitry Andric }
1140b57cec5SDimitry Andric return AdjustedArgs;
1150b57cec5SDimitry Andric };
1160b57cec5SDimitry Andric }
1170b57cec5SDimitry Andric
getInsertArgumentAdjuster(const CommandLineArguments & Extra,ArgumentInsertPosition Pos)1180b57cec5SDimitry Andric ArgumentsAdjuster getInsertArgumentAdjuster(const CommandLineArguments &Extra,
1190b57cec5SDimitry Andric ArgumentInsertPosition Pos) {
1200b57cec5SDimitry Andric return [Extra, Pos](const CommandLineArguments &Args, StringRef /*unused*/) {
1210b57cec5SDimitry Andric CommandLineArguments Return(Args);
1220b57cec5SDimitry Andric
1230b57cec5SDimitry Andric CommandLineArguments::iterator I;
1240b57cec5SDimitry Andric if (Pos == ArgumentInsertPosition::END) {
125bdd1243dSDimitry Andric I = llvm::find(Return, "--");
1260b57cec5SDimitry Andric } else {
1270b57cec5SDimitry Andric I = Return.begin();
1280b57cec5SDimitry Andric ++I; // To leave the program name in place
1290b57cec5SDimitry Andric }
1300b57cec5SDimitry Andric
1310b57cec5SDimitry Andric Return.insert(I, Extra.begin(), Extra.end());
1320b57cec5SDimitry Andric return Return;
1330b57cec5SDimitry Andric };
1340b57cec5SDimitry Andric }
1350b57cec5SDimitry Andric
getInsertArgumentAdjuster(const char * Extra,ArgumentInsertPosition Pos)1360b57cec5SDimitry Andric ArgumentsAdjuster getInsertArgumentAdjuster(const char *Extra,
1370b57cec5SDimitry Andric ArgumentInsertPosition Pos) {
1380b57cec5SDimitry Andric return getInsertArgumentAdjuster(CommandLineArguments(1, Extra), Pos);
1390b57cec5SDimitry Andric }
1400b57cec5SDimitry Andric
combineAdjusters(ArgumentsAdjuster First,ArgumentsAdjuster Second)1410b57cec5SDimitry Andric ArgumentsAdjuster combineAdjusters(ArgumentsAdjuster First,
1420b57cec5SDimitry Andric ArgumentsAdjuster Second) {
1430b57cec5SDimitry Andric if (!First)
1440b57cec5SDimitry Andric return Second;
1450b57cec5SDimitry Andric if (!Second)
1460b57cec5SDimitry Andric return First;
1470b57cec5SDimitry Andric return [First, Second](const CommandLineArguments &Args, StringRef File) {
1480b57cec5SDimitry Andric return Second(First(Args, File), File);
1490b57cec5SDimitry Andric };
1500b57cec5SDimitry Andric }
1510b57cec5SDimitry Andric
getStripPluginsAdjuster()1520b57cec5SDimitry Andric ArgumentsAdjuster getStripPluginsAdjuster() {
1530b57cec5SDimitry Andric return [](const CommandLineArguments &Args, StringRef /*unused*/) {
1540b57cec5SDimitry Andric CommandLineArguments AdjustedArgs;
1550b57cec5SDimitry Andric for (size_t I = 0, E = Args.size(); I != E; I++) {
1560b57cec5SDimitry Andric // According to https://clang.llvm.org/docs/ClangPlugins.html
1570b57cec5SDimitry Andric // plugin arguments are in the form:
1580b57cec5SDimitry Andric // -Xclang {-load, -plugin, -plugin-arg-<plugin-name>, -add-plugin}
1590b57cec5SDimitry Andric // -Xclang <arbitrary-argument>
1600b57cec5SDimitry Andric if (I + 4 < E && Args[I] == "-Xclang" &&
1610b57cec5SDimitry Andric (Args[I + 1] == "-load" || Args[I + 1] == "-plugin" ||
162*5f757f3fSDimitry Andric llvm::StringRef(Args[I + 1]).starts_with("-plugin-arg-") ||
1630b57cec5SDimitry Andric Args[I + 1] == "-add-plugin") &&
1640b57cec5SDimitry Andric Args[I + 2] == "-Xclang") {
1650b57cec5SDimitry Andric I += 3;
1660b57cec5SDimitry Andric continue;
1670b57cec5SDimitry Andric }
1680b57cec5SDimitry Andric AdjustedArgs.push_back(Args[I]);
1690b57cec5SDimitry Andric }
1700b57cec5SDimitry Andric return AdjustedArgs;
1710b57cec5SDimitry Andric };
1720b57cec5SDimitry Andric }
1730b57cec5SDimitry Andric
1740b57cec5SDimitry Andric } // end namespace tooling
1750b57cec5SDimitry Andric } // end namespace clang
176