diff --git a/src/lib/AST/ClangHelpers.cpp b/src/lib/AST/ClangHelpers.cpp index 8de956239..5be852238 100644 --- a/src/lib/AST/ClangHelpers.cpp +++ b/src/lib/AST/ClangHelpers.cpp @@ -13,8 +13,10 @@ #include "lib/AST/ClangHelpers.hpp" #include #include +#include #include #include +#include #include namespace clang::mrdocs { @@ -454,4 +456,32 @@ isDocumented(Decl const* D) return getDocumentation(D) != nullptr; } +bool +isClangCL(tooling::CompileCommand const& cc) +{ + auto const& cmdline = cc.CommandLine; + + // ------------------------------------------------------ + // Convert to InputArgList + // ------------------------------------------------------ + // InputArgList is the input format for llvm functions + auto cmdLineCStrsView = std::views::transform(cmdline, &std::string::c_str); + std::vector const cmdLineCStrs(cmdLineCStrsView.begin(), cmdLineCStrsView.end()); + llvm::opt::InputArgList const args( + 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". + std::string const& progName = cmdline.front(); + StringRef const driver_mode = driver::getDriverMode(progName, cmdLineCStrs); + + return driver::IsClangCL(driver_mode); +} + } // clang::mrdocs diff --git a/src/lib/AST/ClangHelpers.hpp b/src/lib/AST/ClangHelpers.hpp index 2cd2cbf0e..d04360b9d 100644 --- a/src/lib/AST/ClangHelpers.hpp +++ b/src/lib/AST/ClangHelpers.hpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -1081,6 +1082,13 @@ namespace detail { report::trace("{}", MRDOCS_SYMBOL_TRACE_UNIQUE_NAME) #endif +/** Determine whether the driver mode is ClangCL. + + @param cc The compilation command to insepct. + */ +bool +isClangCL(tooling::CompileCommand const& cc); + } // clang::mrdocs #endif diff --git a/src/lib/CorpusImpl.cpp b/src/lib/CorpusImpl.cpp index 258c4c8e6..ccf7000ad 100644 --- a/src/lib/CorpusImpl.cpp +++ b/src/lib/CorpusImpl.cpp @@ -715,7 +715,7 @@ mrdocs::Expected> CorpusImpl:: build( std::shared_ptr const& config, - tooling::CompilationDatabase const& compilations) + MrDocsCompilationDatabase const& compilations) { using clock_type = std::chrono::steady_clock; auto start_time = clock_type::now(); @@ -735,6 +735,10 @@ build( // InfoSet in the execution context. InfoExecutionContext context(*config); + // Identify if we should use "msvc/clang-cl" or "clang/gcc" format + // for options. + bool const is_clang_cl = compilations.isClangCL(); + // ------------------------------------------ // "Process file" task // ------------------------------------------ @@ -795,7 +799,7 @@ build( FSConcrete->addVirtualFile(shimPath, shimContent); Tool.appendArgumentsAdjuster( tooling::combineAdjusters( - tooling::getInsertArgumentAdjuster("-include"), + tooling::getInsertArgumentAdjuster(is_clang_cl ? "/FI" : "-include"), tooling::getInsertArgumentAdjuster(shimPath.data()))); } diff --git a/src/lib/CorpusImpl.hpp b/src/lib/CorpusImpl.hpp index c8d70d0cf..0a3f56dae 100644 --- a/src/lib/CorpusImpl.hpp +++ b/src/lib/CorpusImpl.hpp @@ -12,9 +12,10 @@ #ifndef MRDOCS_LIB_CORPUSIMPL_HPP #define MRDOCS_LIB_CORPUSIMPL_HPP -#include "lib/AST/ParseRef.hpp" #include "ConfigImpl.hpp" +#include "lib/AST/ParseRef.hpp" #include "lib/Metadata/InfoSet.hpp" +#include "lib/MrDocsCompilationDatabase.hpp" #include "lib/Support/Debug.hpp" #include #include @@ -22,11 +23,11 @@ #include #include #include +#include #include #include #include #include -#include namespace clang::mrdocs { @@ -161,7 +162,7 @@ class CorpusImpl final : public Corpus not need to call this function directly. @param config A shared pointer to the configuration. - @param compilations A compilations database for the input files. + @param compilations A MrDocs compilations database for the input files. */ // MRDOCS_DECL [[nodiscard]] @@ -169,7 +170,7 @@ class CorpusImpl final : public Corpus mrdocs::Expected> build( std::shared_ptr const& config, - tooling::CompilationDatabase const& compilations); + MrDocsCompilationDatabase const& compilations); void qualifiedName( diff --git a/src/lib/MrDocsCompilationDatabase.cpp b/src/lib/MrDocsCompilationDatabase.cpp index e565db784..cbe2b0c56 100644 --- a/src/lib/MrDocsCompilationDatabase.cpp +++ b/src/lib/MrDocsCompilationDatabase.cpp @@ -11,6 +11,7 @@ #include "MrDocsCompilationDatabase.hpp" #include "lib/ConfigImpl.hpp" +#include "lib/AST/ClangHelpers.hpp" #include "lib/Support/Debug.hpp" #include "lib/Support/ExecuteAndWaitWithLogging.hpp" #include "lib/Support/Path.hpp" @@ -275,6 +276,7 @@ std::vector adjustCommandLine( StringRef const workingDir, std::vector const& cmdline, + bool is_clang_cl, std::shared_ptr const& config, std::unordered_map> const& implicitIncludeDirectories, std::string_view filename) @@ -300,24 +302,14 @@ 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); + auto const systemIncludeFlag = is_clang_cl ? "-external:I" : "-isystem"; // ------------------------------------------------------ // 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,17 +403,18 @@ 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) { - new_cmdline.emplace_back("-std=c++23"); + new_cmdline.emplace_back(is_clang_cl ? "-std:c++23preview" : "-std=c++23"); } else { - new_cmdline.emplace_back("-std=c23"); + new_cmdline.emplace_back(is_clang_cl ? "-std:c17" : "-std=c23"); } } @@ -448,8 +441,9 @@ adjustCommandLine( it != implicitIncludeDirectories.end()) { for (auto const& inc : it->second) { - new_cmdline.emplace_back(std::format("-isystem{}", inc)); - } + new_cmdline.emplace_back(systemIncludeFlag); + new_cmdline.emplace_back(inc); + } } } @@ -463,11 +457,11 @@ adjustCommandLine( // implicit include paths and add the standard library // and system includes manually. That gives MrDocs // access to libc++ in a portable way. - new_cmdline.emplace_back("-nostdinc++"); - new_cmdline.emplace_back("-nostdlib++"); + new_cmdline.emplace_back(is_clang_cl ? "-X" : "-nostdinc++"); for (auto const& inc : (*config)->stdlibIncludes) { - new_cmdline.emplace_back(std::format("-isystem{}", inc)); + new_cmdline.emplace_back(systemIncludeFlag); + new_cmdline.emplace_back(inc); } } @@ -476,7 +470,8 @@ adjustCommandLine( new_cmdline.emplace_back("-nostdinc"); for (auto const& inc : (*config)->libcIncludes) { - new_cmdline.emplace_back(std::format("-isystem{}", inc)); + new_cmdline.emplace_back(systemIncludeFlag); + new_cmdline.emplace_back(inc); } } @@ -485,7 +480,8 @@ adjustCommandLine( // ------------------------------------------------------ for (auto const& inc : (*config)->systemIncludes) { - new_cmdline.emplace_back(std::format("-isystem{}", inc)); + new_cmdline.emplace_back(systemIncludeFlag); + new_cmdline.emplace_back(inc); } for (auto const& inc : (*config)->includes) { @@ -556,6 +552,9 @@ MrDocsCompilationDatabase( using tooling::CompileCommand; std::vector allCommands = inner.getAllCompileCommands(); + if (allCommands.empty()) return; + + isClangCL_ = mrdocs::isClangCL(allCommands.front()); AllCommands_.reserve(allCommands.size()); SmallPathString temp; for (CompileCommand const& cmd0 : allCommands) @@ -567,6 +566,7 @@ MrDocsCompilationDatabase( cmd.CommandLine = adjustCommandLine( workingDir, cmd0.CommandLine, + isClangCL_, config, implicitIncludeDirectories, cmd0.Filename); diff --git a/src/lib/MrDocsCompilationDatabase.hpp b/src/lib/MrDocsCompilationDatabase.hpp index 490955ad2..8dc7d2acd 100644 --- a/src/lib/MrDocsCompilationDatabase.hpp +++ b/src/lib/MrDocsCompilationDatabase.hpp @@ -38,6 +38,7 @@ class MrDocsCompilationDatabase { std::vector AllCommands_; llvm::StringMap IndexByFile_; + bool isClangCL_{}; public: /** @@ -80,6 +81,13 @@ class MrDocsCompilationDatabase */ std::vector getAllCompileCommands() const override; + + /** Whether the driver mode for the compilation database is ClangCL */ + bool + isClangCL() const noexcept + { + return isClangCL_; + } }; } // mrdocs diff --git a/src/lib/SingleFileDB.hpp b/src/lib/SingleFileDB.hpp index 16af01ed5..393e7dea8 100644 --- a/src/lib/SingleFileDB.hpp +++ b/src/lib/SingleFileDB.hpp @@ -11,7 +11,6 @@ #ifndef MRDOCS_LIB_SINGLEFILEDB_HPP #define MRDOCS_LIB_SINGLEFILEDB_HPP -#include #include #include #include @@ -20,55 +19,37 @@ namespace clang { namespace mrdocs { -/** Compilation database for a single .cpp file. +/** Compilation database for a single file. */ class SingleFileDB : public tooling::CompilationDatabase { - std::vector cc_; + tooling::CompileCommand cc_; public: - explicit - SingleFileDB( - llvm::StringRef pathName) - { - auto fileName = files::getFileName(pathName); - auto parentDir = files::getParentDir(pathName); - - std::vector cmds; - cmds.emplace_back("clang"); - cmds.emplace_back("-fsyntax-only"); - cmds.emplace_back("-std=c++23"); - cmds.emplace_back("-pedantic-errors"); - cmds.emplace_back("-Werror"); - cmds.emplace_back(fileName); - cc_.emplace_back( - parentDir, - fileName, - std::move(cmds), - parentDir); - cc_.back().Heuristic = "unit test"; - } + explicit SingleFileDB(tooling::CompileCommand cc) + : cc_(std::move(cc)) + {} std::vector getCompileCommands( llvm::StringRef FilePath) const override { - if (FilePath != cc_.front().Filename) + if (FilePath != cc_.Filename) return {}; - return { cc_.front() }; + return { cc_ }; } std::vector getAllFiles() const override { - return { cc_.front().Filename }; + return { cc_.Filename }; } std::vector getAllCompileCommands() const override { - return { cc_.front() }; + return { cc_ }; } }; diff --git a/src/test/TestRunner.cpp b/src/test/TestRunner.cpp index 31ea0e38d..3a451b707 100644 --- a/src/test/TestRunner.cpp +++ b/src/test/TestRunner.cpp @@ -55,6 +55,7 @@ writeFile( return {}; } +namespace{ void replaceCRLFWithLF(std::string &str) { @@ -65,6 +66,23 @@ replaceCRLFWithLF(std::string &str) } } +SingleFileDB makeSingleFileDB(llvm::StringRef pathName, std::vector cmds) +{ + auto fileName = files::getFileName(pathName); + auto parentDir = files::getParentDir(pathName); + + cmds.push_back(std::string{fileName}); + + tooling::CompileCommand cc( + parentDir, + fileName, + std::move(cmds), + parentDir); + cc.Heuristic = "unit test"; + return SingleFileDB(std::move(cc)); +} +} + void TestRunner:: handleFile( @@ -73,7 +91,6 @@ handleFile( { report::debug("Handling {}", filePath); - namespace fs = llvm::sys::fs; namespace path = llvm::sys::path; MRDOCS_ASSERT(path::extension(filePath).compare_insensitive(".cpp") == 0); @@ -108,16 +125,47 @@ handleFile( std::shared_ptr config = ConfigImpl::load(fileSettings, dirs_, threadPool_).value(); + auto parentDir = files::getParentDir(filePath); + std::unordered_map> defaultIncludePaths; + + // Test normally + { + auto const db = makeSingleFileDB(filePath, + {"clang", "-std=c++23"}); + + // Create an adjusted MrDocsDatabase + MrDocsCompilationDatabase compilations( + llvm::StringRef(parentDir), db, config, defaultIncludePaths); + handleCompilationDatabase(filePath, compilations, config); + } + +#if defined(_MSC_VER) + // Test again in clang-cl mode + { + auto const db = makeSingleFileDB(filePath, + {"clang-cl", "/std:c++23preview"}); + + // Create an adjusted MrDocsDatabase + MrDocsCompilationDatabase compilations( + llvm::StringRef(parentDir), db, config, defaultIncludePaths); + handleCompilationDatabase(filePath, compilations, config); + } +#endif +} + +void +TestRunner:: +handleCompilationDatabase( + llvm::StringRef filePath, + MrDocsCompilationDatabase const& compilations, + std::shared_ptr const& config) +{ + namespace path = llvm::sys::path; + // Path with the expected results SmallPathString expectedPath = filePath; path::replace_extension(expectedPath, gen_->fileExtension()); - // Create an adjusted MrDocsDatabase - auto parentDir = files::getParentDir(filePath); - std::unordered_map> defaultIncludePaths; - MrDocsCompilationDatabase compilations( - llvm::StringRef(parentDir), SingleFileDB(filePath), config, defaultIncludePaths); - report::debug("Building Corpus", filePath); auto corpus = CorpusImpl::build(config, compilations); if (!corpus) diff --git a/src/test/TestRunner.hpp b/src/test/TestRunner.hpp index 2642a2262..0f7d9afd5 100644 --- a/src/test/TestRunner.hpp +++ b/src/test/TestRunner.hpp @@ -12,8 +12,10 @@ #define MRDOCS_TEST_TESTRUNNER_HPP #include "lib/ConfigImpl.hpp" +#include "lib/MrDocsCompilationDatabase.hpp" #include #include +#include #include #include #include @@ -75,6 +77,12 @@ class TestRunner TestRunner(std::string_view generator); + void + handleCompilationDatabase( + llvm::StringRef filePath, + MrDocsCompilationDatabase const& compilations, + std::shared_ptr const& config); + /** Check a single file, or a directory recursively. This function checks the specified path diff --git a/src/test/lib/MrDocsCompilationDatabase.cpp b/src/test/lib/MrDocsCompilationDatabase.cpp new file mode 100644 index 000000000..26731347f --- /dev/null +++ b/src/test/lib/MrDocsCompilationDatabase.cpp @@ -0,0 +1,308 @@ +// +// 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 +#include + +namespace clang { +namespace mrdocs { + +namespace { +struct TestConfigImpl final : Config +{ + Settings settings_; + + ThreadPool& + threadPool() const noexcept override + { + static ThreadPool dummyThreadPool_; + return dummyThreadPool_; + } + + dom::Object const& object() const override + { + static dom::Object dummyObject; + return dummyObject; + + } + + Settings const& settings() const noexcept override + { + return settings_; + } +}; +} + +struct MrDocsCompilationDatabase_test +{ + auto adjustCompileCommand( + std::vector commandLine, + std::shared_ptr config = std::make_shared()) 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( + "", SingleFileDB(std::move(cc)), config, defaultIncludePaths); + return compilations.getAllCompileCommands().front().CommandLine; + } + + static bool has(std::vector const& commandLine, std::string_view flag) + { + return mrdocs::contains(commandLine, flag); + } + 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 testClang_stdCxx() + { + std::string const programName = "clang"; + + { + auto adjusted = adjustCompileCommand({ programName }); + BOOST_TEST(has(adjusted, "-std=c++23")); + } + { + auto adjusted = adjustCompileCommand({ programName, "-std=c++11" }); + BOOST_TEST(has(adjusted, "-std=c++11")); + BOOST_TEST_NOT(has(adjusted, "-std=c++23")); + } + { + auto adjusted = adjustCompileCommand({ programName, "--std=c++11" }); + BOOST_TEST(has(adjusted, "--std=c++11")); + BOOST_TEST_NOT(has(adjusted, "-std=c++23")); + } + } + + void testClang_stdC() + { + std::string const programName = "clang"; + + { + auto adjusted = adjustCompileCommand({ programName, "-x", "c" }); + BOOST_TEST(has(adjusted, "-std=c23")); + } + { + auto adjusted = adjustCompileCommand({ programName, "-x", "c", "-std=c11" }); + BOOST_TEST(has(adjusted, "-std=c11")); + BOOST_TEST_NOT(has(adjusted, "-std=c23")); + } + { + auto adjusted = adjustCompileCommand({ programName, "-x", "c", "--std=c11" }); + BOOST_TEST(has(adjusted, "--std=c11")); + BOOST_TEST_NOT(has(adjusted, "-std=c23")); + } + } + + void testClang_defines() + { + std::string const programName = "clang"; + + { + std::shared_ptr config = std::make_shared(); + config->settings_.defines = { "FOO", "BAR=1" }; + + auto adjusted = adjustCompileCommand({ programName, "-DBAZ=2" }, config); + BOOST_TEST(has(adjusted, "-D__MRDOCS__")); + BOOST_TEST(has(adjusted, "-DFOO")); + BOOST_TEST(has(adjusted, "-DBAR=1")); + BOOST_TEST(has(adjusted, "-DBAZ=2")); + } + } + + void testClang_stdlib() + { + std::string const programName = "clang"; + + { + std::shared_ptr const config = std::make_shared(); + config->settings_.useSystemStdlib = false; + config->settings_.stdlibIncludes.push_back("stdlib-path"); + + auto adjusted = adjustCompileCommand({ programName }, config); + BOOST_TEST(has(adjusted, "-nostdinc++")); + BOOST_TEST(has(adjusted, { "-isystem", "stdlib-path" })); + } + } + + void testClang_libc() + { + std::string const programName = "clang"; + + { + std::shared_ptr config = std::make_shared(); + config->settings_.useSystemLibc = false; + config->settings_.libcIncludes.push_back("libc-path"); + + auto adjusted = adjustCompileCommand({ programName }, config); + BOOST_TEST(has(adjusted, "-nostdinc")); + BOOST_TEST(has(adjusted, { "-isystem", "libc-path" })); + } + } + + void testClang_systemIncludes() + { + std::string const programName = "clang"; + + { + std::shared_ptr config = std::make_shared(); + config->settings_.systemIncludes.push_back("system-path"); + + auto adjusted = adjustCompileCommand({ programName }, config); + BOOST_TEST(has(adjusted, { "-isystem", "system-path" })); + } + } + + void testClang() + { + testClang_stdCxx(); + testClang_stdC(); + testClang_defines(); + testClang_stdlib(); + testClang_libc(); + testClang_systemIncludes(); + } + + void testClangCL_stdCxx() + { + std::string const programName = "clang-cl"; + + { + auto adjusted = adjustCompileCommand({ programName }); + BOOST_TEST(has(adjusted, "-std:c++23preview")); + } + { + auto adjusted = adjustCompileCommand({ programName, "-std:c++11" }); + BOOST_TEST(has(adjusted, "-std:c++11")); + BOOST_TEST_NOT(has(adjusted, "-std:c++latest")); + } + { + auto adjusted = adjustCompileCommand({ programName, "/std:c++11" }); + BOOST_TEST(has(adjusted, "/std:c++11")); + BOOST_TEST_NOT(has(adjusted, "-std:c++latest")); + } + } + + void testClangCL_stdC() + { + std::string const programName = "clang-cl"; + + { + auto adjusted = adjustCompileCommand({ programName, "-x", "c" }); + BOOST_TEST(has(adjusted, "-std:c17")); + } + { + auto adjusted = adjustCompileCommand({ programName, "-x", "c", "-std:c11" }); + BOOST_TEST(has(adjusted, "-std:c11")); + BOOST_TEST_NOT(has(adjusted, "-std:clatest")); + } + { + auto adjusted = adjustCompileCommand({ programName, "-x", "c", "/std:c11" }); + BOOST_TEST(has(adjusted, "/std:c11")); + BOOST_TEST_NOT(has(adjusted, "-std:clatest")); + } + } + + void testClangCL_defines() + { + std::string const programName = "clang-cl"; + + { + std::shared_ptr config = std::make_shared(); + config->settings_.defines = { "FOO", "BAR=1" }; + + auto adjusted = adjustCompileCommand({ programName, "-DBAZ=2" }, config); + BOOST_TEST(has(adjusted, "-D__MRDOCS__")); + BOOST_TEST(has(adjusted, "-DFOO")); + BOOST_TEST(has(adjusted, "-DBAR=1")); + BOOST_TEST(has(adjusted, "-DBAZ=2")); + } + } + + void testClangCL_stdlib() + { + std::string const programName = "clang-cl"; + + { + std::shared_ptr config = std::make_shared(); + config->settings_.useSystemStdlib = false; + config->settings_.stdlibIncludes.push_back("stdlib-path"); + + auto adjusted = adjustCompileCommand({ programName }, config); + BOOST_TEST(has(adjusted, "-X")); + BOOST_TEST(has(adjusted, { "-external:I", "stdlib-path" })); + } + } + + void testClangCL_libc() + { + std::string const programName = "clang-cl"; + + { + std::shared_ptr config = std::make_shared(); + config->settings_.useSystemLibc = false; + config->settings_.libcIncludes.push_back("libc-path"); + + auto adjusted = adjustCompileCommand({ programName }, config); + BOOST_TEST(has(adjusted, "-nostdinc")); + BOOST_TEST(has(adjusted, { "-external:I", "libc-path" })); + } + } + + void testClangCL_systemIncludes() + { + std::string const programName = "clang-cl"; + + { + std::shared_ptr config = std::make_shared(); + config->settings_.systemIncludes.push_back("system-path"); + + auto adjusted = adjustCompileCommand({ programName }, config); + BOOST_TEST(has(adjusted, { "-external:I", "system-path" })); + } + } + + void testClangCL() + { + testClangCL_stdCxx(); + testClangCL_stdC(); + testClangCL_defines(); + testClangCL_stdlib(); + testClangCL_libc(); + testClangCL_systemIncludes(); + } + + void run() + { + testClang(); + testClangCL(); + } +}; + +TEST_SUITE( + MrDocsCompilationDatabase_test, + "clang.mrdocs.MrDocsCompilationDatabase"); + +} // mrdocs +} // clang