diff --git a/src/lib/MrDocsCompilationDatabase.cpp b/src/lib/MrDocsCompilationDatabase.cpp index e565db784..cb9de647f 100644 --- a/src/lib/MrDocsCompilationDatabase.cpp +++ b/src/lib/MrDocsCompilationDatabase.cpp @@ -219,10 +219,17 @@ isValidMrDocsOption( driver::options::OPT_clang_ignored_gcc_optimization_f_Group, driver::options::OPT_clang_ignored_legacy_options_Group, driver::options::OPT_clang_ignored_m_Group, - driver::options::OPT_flang_ignored_w_Group -#if 0 + driver::options::OPT_flang_ignored_w_Group, + driver::options::OPT__SLASH_O, + driver::options::OPT__SLASH_EH, + driver::options::OPT__SLASH_GR, + driver::options::OPT__SLASH_GR_, + driver::options::OPT__SLASH_M_Group, + driver::options::OPT__SLASH_MP, + driver::options::OPT__SLASH_Zc, + // input file options - driver::options::OPT_INPUT, + //driver::options::OPT_INPUT, // output file options driver::options::OPT_o, @@ -237,11 +244,10 @@ isValidMrDocsOption( driver::options::OPT__SLASH_Fr, driver::options::OPT__SLASH_Fm, driver::options::OPT__SLASH_Fx, -#endif - // driver::options::OPT__SLASH_TP - // driver::options::OPT__SLASH_Tp - // driver::options::OPT__SLASH_TC - // driver::options::OPT__SLASH_Tc + driver::options::OPT__SLASH_TP, + driver::options::OPT__SLASH_Tp, + driver::options::OPT__SLASH_TC, + driver::options::OPT__SLASH_Tc )) { return false; @@ -288,7 +294,7 @@ adjustCommandLine( // Copy the compiler path // ------------------------------------------------------ std::string const& progName = cmdline.front(); - std::vector new_cmdline = {progName}; + std::vector new_cmdline = {std::string{"clang"}}; // ------------------------------------------------------ // Convert to InputArgList @@ -300,24 +306,12 @@ adjustCommandLine( cmdLineCStrs.data(), cmdLineCStrs.data() + cmdLineCStrs.size()); - // ------------------------------------------------------ - // Get driver mode - // ------------------------------------------------------ - // The driver mode distinguishes between clang/gcc and msvc - // command line option formats. The value is deduced from - // the `-drive-mode` option or from `progName`. - // Common values are "gcc", "g++", "cpp", "cl" and "flang". - StringRef const driver_mode = driver::getDriverMode(progName, cmdLineCStrs); - // Identify if we should use "msvc/clang-cl" or "clang/gcc" format - // for options. - bool const is_clang_cl = driver::IsClangCL(driver_mode); - // ------------------------------------------------------ // Supress all warnings // ------------------------------------------------------ // Add flags to ignore all warnings. Any options that // affect warnings will be discarded later. - new_cmdline.emplace_back(is_clang_cl ? "/w" : "-w"); + new_cmdline.emplace_back("-w"); new_cmdline.emplace_back("-fsyntax-only"); // ------------------------------------------------------ @@ -411,9 +405,10 @@ adjustCommandLine( isExplicitCCompileCommand || (!isExplicitCppCompileCommand && isImplicitCSourceFile); constexpr auto is_std_option = [](std::string_view const opt) { - return opt.starts_with("-std=") || opt.starts_with("--std=") || opt.starts_with("/std:"); + return opt.starts_with("-std=") || opt.starts_with("--std=") || // clang options + opt.starts_with("/std:") || opt.starts_with("-std:"); // clang-cl options }; - if (std::ranges::find_if(cmdline, is_std_option) == cmdline.end()) + if (std::ranges::none_of(cmdline, is_std_option)) { if (!isCCompileCommand) { @@ -449,7 +444,7 @@ adjustCommandLine( for (auto const& inc : it->second) { new_cmdline.emplace_back(std::format("-isystem{}", inc)); - } + } } } @@ -492,6 +487,18 @@ adjustCommandLine( new_cmdline.emplace_back(std::format("-I{}", inc)); } + // ------------------------------------------------------ + // Get driver mode + // ------------------------------------------------------ + // The driver mode distinguishes between clang/gcc and msvc + // command line option formats. The value is deduced from + // the `-drive-mode` option or from `progName`. + // Common values are "gcc", "g++", "cpp", "cl" and "flang". + StringRef const driver_mode = driver::getDriverMode(progName, cmdLineCStrs); + // Identify if we should use "msvc/clang-cl" or "clang/gcc" format + // for options. + bool const is_clang_cl = driver::IsClangCL(driver_mode); + // ------------------------------------------------------ // Adjust each argument in the command line // ------------------------------------------------------ @@ -507,18 +514,40 @@ adjustCommandLine( while (idx < cmdline.size()) { // Parse one argument as a Clang option - // ParseOneArg updates Index to the next argument to be parsed. - unsigned const idx0 = idx; std::unique_ptr arg = opts_table.ParseOneArg(args, idx, visibility); if (!isValidMrDocsOption(workingDir, arg)) { continue; } - new_cmdline.insert( - new_cmdline.end(), - cmdline.begin() + idx0, - cmdline.begin() + idx); + + // Translate clang-cl /std: into clang -std= + const llvm::opt::Option opt = + arg->getOption().getUnaliasedOption(); + if (opt.matches(clang::driver::options::OPT__SLASH_std)) + { + MRDOCS_ASSERT(arg->getNumValues() == 1); + + std::string_view v = arg->getValue(); + if (v == "c++latest" || v == "c++23preview") + { + new_cmdline.emplace_back("-std=c++23"); + } + else // c++14,c++17,c++20,c11,c17 + { + new_cmdline.emplace_back(std::format("-std={}", v)); + } + continue; + } + + // Append the translated arguments to the new command line + llvm::opt::ArgStringList output; + arg->render(args, output); + + for (auto const& v : output) + { + new_cmdline.emplace_back(v); + } } return new_cmdline; diff --git a/src/test/lib/MrDocsCompilationDatabase.cpp b/src/test/lib/MrDocsCompilationDatabase.cpp new file mode 100644 index 000000000..8758179a0 --- /dev/null +++ b/src/test/lib/MrDocsCompilationDatabase.cpp @@ -0,0 +1,184 @@ +// +// Licensed 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 +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include "lib/ConfigImpl.hpp" +#include "lib/MrDocsCompilationDatabase.hpp" +#include "lib/SingleFileDB.hpp" +#include +#include +#include + +namespace clang { +namespace mrdocs { + +namespace { +/** Compilation database for a single .cpp file. +*/ +class SingleFileTestDB + : public tooling::CompilationDatabase +{ + tooling::CompileCommand cc_; + +public: + explicit + SingleFileTestDB( + tooling::CompileCommand cc) + : cc_(std::move(cc)) + {} + + std::vector + getCompileCommands( + llvm::StringRef FilePath) const override + { + if (FilePath != cc_.Filename) + return {}; + return { cc_ }; + } + + std::vector + getAllFiles() const override + { + return { cc_.Filename }; + } + + std::vector + getAllCompileCommands() const override + { + return { cc_ }; + } +}; +} + +struct MrDocsCompilationDatabase_test +{ + Config::Settings fileSettings_; + ThreadPool threadPool_; + ReferenceDirectories dirs_; + + std::shared_ptr config_ = + ConfigImpl::load(fileSettings_, dirs_, threadPool_).value(); + + auto adjustCompileCommand(std::vector CommandLine) const + { + tooling::CompileCommand cc; + cc.Directory = "."; + cc.Filename = "test.cpp"; // file does not exist + cc.CommandLine = std::move(CommandLine); + cc.CommandLine.push_back(cc.Filename); + cc.Heuristic = "unit test"; + + // Create an adjusted MrDocsDatabase + std::unordered_map> defaultIncludePaths; + MrDocsCompilationDatabase compilations( + llvm::StringRef(cc.Directory), SingleFileTestDB(cc), config_, defaultIncludePaths); + return compilations.getCompileCommands(cc.Filename).front().CommandLine; + } + + static bool has(std::vector const& commandLine, std::string_view flag) + { + return std::find(commandLine.begin(), commandLine.end(), flag) != commandLine.end(); + } + static bool has(std::vector const& commandLine, std::initializer_list flags) + { + MRDOCS_ASSERT(flags.size() > 0); + return std::search(commandLine.begin(), commandLine.end(), flags.begin(), flags.end()) != commandLine.end(); + } + + void testClangCLStdFlag() + { + // /std:c++14 -> -std=c++14 + { + auto adjusted = adjustCompileCommand({ "clang-cl", "/std:c++14"}); + BOOST_TEST_GE(adjusted.size(), 2); + BOOST_TEST(has(adjusted, "-std=c++14")); + } + + // /std:c++17 -> -std=c++17 + { + auto adjusted = adjustCompileCommand({ "clang-cl", "/std:c++17"}); + BOOST_TEST_GE(adjusted.size(), 2); + BOOST_TEST(has(adjusted, "-std=c++17")); + } + + // /std:c++20 -> -std=c++20 + { + auto adjusted = adjustCompileCommand({ "clang-cl", "/std:c++20"}); + BOOST_TEST_GE(adjusted.size(), 2); + BOOST_TEST(has(adjusted, "-std=c++20")); + } + + // /std:c++23preview -> -std=c++23 + { + auto adjusted = adjustCompileCommand({ "clang-cl", "/std:c++23preview"}); + BOOST_TEST_GE(adjusted.size(), 2); + BOOST_TEST(has(adjusted, "-std=c++23")); + } + + // /std:c++latest -> -std=c++23 + { + auto adjusted = adjustCompileCommand({ "clang-cl", "/std:c++latest"}); + BOOST_TEST_GE(adjusted.size(), 2); + BOOST_TEST(has(adjusted, "-std=c++23")); + } + + // /std:c11 -> -std=c11 + { + auto adjusted = adjustCompileCommand({ "clang-cl", "/std:c11"}); + BOOST_TEST_GE(adjusted.size(), 2); + BOOST_TEST(has(adjusted, "-std=c11")); + } + + // /std:c17 -> -std=c17 + { + auto adjusted = adjustCompileCommand({ "clang-cl", "/std:c17"}); + BOOST_TEST_GE(adjusted.size(), 2); + BOOST_TEST(has(adjusted, "-std=c17")); + } + } + + void testClangCLIncludeFlag() + { + // /I -> -I + { + auto adjusted = adjustCompileCommand({ "clang-cl", "/I"}); + BOOST_TEST_GE(adjusted.size(), 2); + BOOST_TEST(has(adjusted, {"-I", ""})); + } + + // /external:I -> -isystem + { + auto adjusted = adjustCompileCommand({ "clang-cl", "/external:I"}); + BOOST_TEST_GE(adjusted.size(), 2); + BOOST_TEST(has(adjusted, {"-isystem", ""})); + } + } + + void testClangCL() + { + auto adjusted = adjustCompileCommand({ "clang-cl"}); + BOOST_TEST_GE(adjusted.size(), 1); + BOOST_TEST_EQ(adjusted[0], "clang"); + + testClangCLStdFlag(); + testClangCLIncludeFlag(); + } + + void run() + { + testClangCL(); + } +}; + +TEST_SUITE( + MrDocsCompilationDatabase_test, + "clang.mrdocs.MrDocsCompilationDatabase"); + +} // mrdocs +} // clang