[clang] [clang-tools-extra] [clangd] Support clang-cl flags /std:c++23preview and /std:clatest (PR #178080)
Nico Weber via cfe-commits
cfe-commits at lists.llvm.org
Mon Jan 26 15:56:28 PST 2026
https://github.com/nico updated https://github.com/llvm/llvm-project/pull/178080
>From 143f7d1e3b9e104f89c9b42f9bac6c74c3c59eb8 Mon Sep 17 00:00:00 2001
From: Nico Weber <thakis at chromium.org>
Date: Mon, 26 Jan 2026 18:26:45 -0500
Subject: [PATCH 1/2] [clangd] Support clang-cl flags /std:c++23preview and
/std:clatest
Related to https://github.com/clangd/clangd/issues/1850
---
.../clangd/unittests/CompileCommandsTests.cpp | 28 +++++++++---
clang/lib/Driver/ToolChains/Clang.cpp | 10 +++++
.../InterpolatingCompilationDatabase.cpp | 43 ++++++++++++++-----
3 files changed, 65 insertions(+), 16 deletions(-)
diff --git a/clang-tools-extra/clangd/unittests/CompileCommandsTests.cpp b/clang-tools-extra/clangd/unittests/CompileCommandsTests.cpp
index 4d59a6151c167..d7b7fd0f9df87 100644
--- a/clang-tools-extra/clangd/unittests/CompileCommandsTests.cpp
+++ b/clang-tools-extra/clangd/unittests/CompileCommandsTests.cpp
@@ -535,13 +535,29 @@ TEST(CommandMangler, StdLatestFlag) {
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "), HasSubstr("/std:c++latest"));
}
-TEST(CommandMangler, StdLatestFlag_Inference) {
+TEST(CommandMangler, ClangClStdFlags_Inference) {
const auto Mangler = CommandMangler::forTests();
- tooling::CompileCommand Cmd;
- Cmd.CommandLine = {"clang-cl", "/std:c++latest", "--", "/Users/foo.cc"};
- Mangler(Cmd, "/Users/foo.hpp");
- // Check that the /std:c++latest flag is not dropped during inference
- EXPECT_THAT(llvm::join(Cmd.CommandLine, " "), HasSubstr("/std:c++latest"));
+
+ {
+ tooling::CompileCommand Cmd;
+ Cmd.CommandLine = {"clang-cl", "/std:c++23preview", "--", "/Users/foo.cc"};
+ Mangler(Cmd, "/Users/foo.hpp");
+ EXPECT_THAT(llvm::join(Cmd.CommandLine, " "), HasSubstr("/std:c++23preview"));
+ }
+
+ {
+ tooling::CompileCommand Cmd;
+ Cmd.CommandLine = {"clang-cl", "/std:clatest", "--", "/Users/foo.c"};
+ Mangler(Cmd, "/Users/foo.h");
+ EXPECT_THAT(llvm::join(Cmd.CommandLine, " "), HasSubstr("/std:clatest"));
+ }
+
+ {
+ tooling::CompileCommand Cmd;
+ Cmd.CommandLine = {"clang-cl", "/std:c++latest", "--", "/Users/foo.cc"};
+ Mangler(Cmd, "/Users/foo.hpp");
+ EXPECT_THAT(llvm::join(Cmd.CommandLine, " "), HasSubstr("/std:c++latest"));
+ }
}
} // namespace
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index ab671d032644b..343e78973bce7 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -7211,6 +7211,11 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
LanguageStandard = llvm::StringSwitch<StringRef>(StdArg->getValue())
.Case("c11", "-std=c11")
.Case("c17", "-std=c17")
+ // If you add cases below for spellings that are
+ // not in LangStandards.def, update
+ // TransferableCommand::tryParseStdArg() in
+ // lib/Tooling/InterpolatingCompilationDatabase.cpp
+ // to match.
// TODO: add c23 when MSVC supports it.
.Case("clatest", "-std=c23")
.Default("");
@@ -7228,6 +7233,11 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
.Case("c++14", "-std=c++14")
.Case("c++17", "-std=c++17")
.Case("c++20", "-std=c++20")
+ // If you add cases below for spellings that are
+ // not in LangStandards.def, update
+ // TransferableCommand::tryParseStdArg() in
+ // lib/Tooling/InterpolatingCompilationDatabase.cpp
+ // to match.
// TODO add c++23 and c++26 when MSVC supports it.
.Case("c++23preview", "-std=c++23")
.Case("c++latest", "-std=c++26")
diff --git a/clang/lib/Tooling/InterpolatingCompilationDatabase.cpp b/clang/lib/Tooling/InterpolatingCompilationDatabase.cpp
index e9b72388ae4df..261aa648f6226 100644
--- a/clang/lib/Tooling/InterpolatingCompilationDatabase.cpp
+++ b/clang/lib/Tooling/InterpolatingCompilationDatabase.cpp
@@ -123,9 +123,18 @@ static types::ID foldType(types::ID Lang) {
}
}
+// Return the language standard that's activated by the /std:clatest
+// flag in clang-CL mode.
+static LangStandard::Kind latestLangStandardC() {
+ // FIXME: Have a single source of truth for the mapping from
+ // clatest --> c23 that's shared by the driver code
+ // (clang/lib/Driver/ToolChains/Clang.cpp) and this file.
+ return LangStandard::lang_c23;
+}
+
// Return the language standard that's activated by the /std:c++latest
// flag in clang-CL mode.
-static LangStandard::Kind latestLangStandard() {
+static LangStandard::Kind latestLangStandardCXX() {
// FIXME: Have a single source of truth for the mapping from
// c++latest --> c++26 that's shared by the driver code
// (clang/lib/Driver/ToolChains/Clang.cpp) and this file.
@@ -243,20 +252,29 @@ struct TransferableCommand {
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) {
const char *Spelling =
LangStandard::getLangStandardForKind(Std).getName();
- // In clang-cl mode, the latest standard is spelled 'c++latest' rather
- // than e.g. 'c++26', and the driver does not accept the latter, so emit
+
+ // In clang-cl mode, some standards have different spellings, so emit
// the spelling that the driver does accept.
- if (ClangCLMode && Std == latestLangStandard()) {
- Spelling = "c++latest";
- }
+ // Keep in sync with OPT__SLASH_std handling in Clang::ConstructJob().
+ if (ClangCLMode) {
+ if (Std == LangStandard::lang_cxx23)
+ Spelling = "c++23preview";
+ else if (Std == latestLangStandardC())
+ Spelling = "clatest";
+ else if (Std == latestLangStandardCXX())
+ Spelling = "c++latest";
+ }
+
Result.CommandLine.emplace_back(
(llvm::Twine(ClangCLMode ? "/std:" : "-std=") + Spelling).str());
}
+
Result.CommandLine.push_back("--");
Result.CommandLine.push_back(std::string(Filename));
return Result;
@@ -313,10 +331,15 @@ struct TransferableCommand {
std::optional<LangStandard::Kind> tryParseStdArg(const llvm::opt::Arg &Arg) {
using namespace options;
if (Arg.getOption().matches(ClangCLMode ? OPT__SLASH_std : OPT_std_EQ)) {
- // "c++latest" is not a recognized LangStandard, but it's accepted by
- // the clang driver in CL mode.
- if (ClangCLMode && StringRef(Arg.getValue()) == "c++latest") {
- return latestLangStandard();
+ if (ClangCLMode) {
+ // Handle clang-cl spellings not in LangStandards.def.
+ // Keep in sync with OPT__SLASH_std handling in Clang::ConstructJob().
+ if (StringRef(Arg.getValue()) == "c++23preview")
+ return LangStandard::lang_cxx23;
+ if (StringRef(Arg.getValue()) == "clatest")
+ return latestLangStandardC();
+ if (StringRef(Arg.getValue()) == "c++latest")
+ return latestLangStandardCXX();
}
return LangStandard::getLangKind(Arg.getValue());
}
>From 68ebca5aa6510c2f7c2ff45f25e831e7b996e721 Mon Sep 17 00:00:00 2001
From: Nico Weber <thakis at chromium.org>
Date: Mon, 26 Jan 2026 18:56:16 -0500
Subject: [PATCH 2/2] clang-format
---
clang-tools-extra/clangd/unittests/CompileCommandsTests.cpp | 3 ++-
clang/lib/Tooling/InterpolatingCompilationDatabase.cpp | 2 +-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/clang-tools-extra/clangd/unittests/CompileCommandsTests.cpp b/clang-tools-extra/clangd/unittests/CompileCommandsTests.cpp
index d7b7fd0f9df87..79ae334791c96 100644
--- a/clang-tools-extra/clangd/unittests/CompileCommandsTests.cpp
+++ b/clang-tools-extra/clangd/unittests/CompileCommandsTests.cpp
@@ -542,7 +542,8 @@ TEST(CommandMangler, ClangClStdFlags_Inference) {
tooling::CompileCommand Cmd;
Cmd.CommandLine = {"clang-cl", "/std:c++23preview", "--", "/Users/foo.cc"};
Mangler(Cmd, "/Users/foo.hpp");
- EXPECT_THAT(llvm::join(Cmd.CommandLine, " "), HasSubstr("/std:c++23preview"));
+ EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
+ HasSubstr("/std:c++23preview"));
}
{
diff --git a/clang/lib/Tooling/InterpolatingCompilationDatabase.cpp b/clang/lib/Tooling/InterpolatingCompilationDatabase.cpp
index 261aa648f6226..f8306a6ad6e90 100644
--- a/clang/lib/Tooling/InterpolatingCompilationDatabase.cpp
+++ b/clang/lib/Tooling/InterpolatingCompilationDatabase.cpp
@@ -269,7 +269,7 @@ struct TransferableCommand {
Spelling = "clatest";
else if (Std == latestLangStandardCXX())
Spelling = "c++latest";
- }
+ }
Result.CommandLine.emplace_back(
(llvm::Twine(ClangCLMode ? "/std:" : "-std=") + Spelling).str());
More information about the cfe-commits
mailing list