[clang] 6503b01 - [clang-cl] Fix value of __FUNCTION__ in MSVC mode. (#84014)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Mar 19 07:55:53 PDT 2024
Author: Zahira Ammarguellat
Date: 2024-03-19T07:55:49-07:00
New Revision: 6503b015d4a8971dcb69a63de7d8d203e4be79b6
URL: https://github.com/llvm/llvm-project/commit/6503b015d4a8971dcb69a63de7d8d203e4be79b6
DIFF: https://github.com/llvm/llvm-project/commit/6503b015d4a8971dcb69a63de7d8d203e4be79b6.diff
LOG: [clang-cl] Fix value of __FUNCTION__ in MSVC mode. (#84014)
Predefined macro FUNCTION in clang is not returning the same string than
MS for templated functions.
See https://godbolt.org/z/q3EKn5zq4
For the same test case MSVC is returning:
function: TestClass::TestClass
function: TestStruct::TestStruct
function: TestEnum::TestEnum
The initial work for this was in the reverted patch
(https://github.com/llvm/llvm-project/pull/66120). This patch solves the
issues raised in the reverted patch.
Added:
Modified:
clang/docs/ReleaseNotes.rst
clang/include/clang/AST/Expr.h
clang/lib/AST/DeclPrinter.cpp
clang/lib/AST/Expr.cpp
clang/lib/AST/TypePrinter.cpp
clang/lib/Sema/SemaExpr.cpp
clang/test/Analysis/eval-predefined-exprs.cpp
clang/test/SemaCXX/source_location.cpp
clang/unittests/AST/DeclPrinterTest.cpp
clang/unittests/AST/TypePrinterTest.cpp
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 5372a575071c0c..5ddad61e27c909 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -284,6 +284,9 @@ Bug Fixes in This Version
for variables created through copy initialization having side-effects in C++17 and later.
Fixes (#GH64356) (#GH79518).
+- Fix value of predefined macro ``__FUNCTION__`` in MSVC compatibility mode.
+ Fixes (#GH66114).
+
- Clang now emits errors for explicit specializations/instatiations of lambda call
operator.
Fixes (#GH83267).
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index 446bec4081e869..f9313f87be3800 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -2045,7 +2045,8 @@ class PredefinedExpr final
}
static std::string ComputeName(PredefinedIdentKind IK,
- const Decl *CurrentDecl);
+ const Decl *CurrentDecl,
+ bool ForceElaboratedPrinting = false);
SourceLocation getBeginLoc() const { return getLocation(); }
SourceLocation getEndLoc() const { return getLocation(); }
diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
index b701581b2474a9..ad45da6b8f5659 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -679,6 +679,16 @@ static void printExplicitSpecifier(ExplicitSpecifier ES, llvm::raw_ostream &Out,
Out << Proto;
}
+static void MaybePrintTagKeywordIfSupressingScopes(PrintingPolicy &Policy,
+ QualType T,
+ llvm::raw_ostream &Out) {
+ StringRef prefix = T->isClassType() ? "class "
+ : T->isStructureType() ? "struct "
+ : T->isUnionType() ? "union "
+ : "";
+ Out << prefix;
+}
+
void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
if (!D->getDescribedFunctionTemplate() &&
!D->isFunctionTemplateSpecialization())
@@ -855,6 +865,10 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
Out << Proto << " -> ";
Proto.clear();
}
+ if (!Policy.SuppressTagKeyword && Policy.SuppressScope &&
+ !Policy.SuppressUnwrittenScope)
+ MaybePrintTagKeywordIfSupressingScopes(Policy, AFT->getReturnType(),
+ Out);
AFT->getReturnType().print(Out, Policy, Proto);
Proto.clear();
}
@@ -1022,7 +1036,13 @@ void DeclPrinter::VisitVarDecl(VarDecl *D) {
? D->getIdentifier()->deuglifiedName()
: D->getName();
- printDeclType(T, Name);
+ if (!Policy.SuppressTagKeyword && Policy.SuppressScope &&
+ !Policy.SuppressUnwrittenScope) {
+ MaybePrintTagKeywordIfSupressingScopes(Policy, T, Out);
+ printDeclType(T, Name);
+ } else {
+ printDeclType(T, Name);
+ }
// Print the attributes that should be placed right before the end of the
// decl.
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index 131dace77f9c25..6221ebd5c9b4e9 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -676,7 +676,8 @@ StringRef PredefinedExpr::getIdentKindName(PredefinedIdentKind IK) {
// FIXME: Maybe this should use DeclPrinter with a special "print predefined
// expr" policy instead.
std::string PredefinedExpr::ComputeName(PredefinedIdentKind IK,
- const Decl *CurrentDecl) {
+ const Decl *CurrentDecl,
+ bool ForceElaboratedPrinting) {
ASTContext &Context = CurrentDecl->getASTContext();
if (IK == PredefinedIdentKind::FuncDName) {
@@ -724,10 +725,21 @@ std::string PredefinedExpr::ComputeName(PredefinedIdentKind IK,
return std::string(Out.str());
}
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CurrentDecl)) {
- if (IK != PredefinedIdentKind::PrettyFunction &&
+ const auto &LO = Context.getLangOpts();
+ bool IsFuncOrFunctionInNonMSVCCompatEnv =
+ ((IK == PredefinedIdentKind::Func ||
+ IK == PredefinedIdentKind ::Function) &&
+ !LO.MSVCCompat);
+ bool IsLFunctionInMSVCCommpatEnv =
+ IK == PredefinedIdentKind::LFunction && LO.MSVCCompat;
+ bool IsFuncOrFunctionOrLFunctionOrFuncDName =
+ IK != PredefinedIdentKind::PrettyFunction &&
IK != PredefinedIdentKind::PrettyFunctionNoVirtual &&
IK != PredefinedIdentKind::FuncSig &&
- IK != PredefinedIdentKind::LFuncSig)
+ IK != PredefinedIdentKind::LFuncSig;
+ if ((ForceElaboratedPrinting &&
+ (IsFuncOrFunctionInNonMSVCCompatEnv || IsLFunctionInMSVCCommpatEnv)) ||
+ (!ForceElaboratedPrinting && IsFuncOrFunctionOrLFunctionOrFuncDName))
return FD->getNameAsString();
SmallString<256> Name;
@@ -755,6 +767,8 @@ std::string PredefinedExpr::ComputeName(PredefinedIdentKind IK,
PrintingPolicy Policy(Context.getLangOpts());
PrettyCallbacks PrettyCB(Context.getLangOpts());
Policy.Callbacks = &PrettyCB;
+ if (IK == PredefinedIdentKind::Function && ForceElaboratedPrinting)
+ Policy.SuppressTagKeyword = !LO.MSVCCompat;
std::string Proto;
llvm::raw_string_ostream POut(Proto);
@@ -782,6 +796,12 @@ std::string PredefinedExpr::ComputeName(PredefinedIdentKind IK,
FD->printQualifiedName(POut, Policy);
+ if (IK == PredefinedIdentKind::Function) {
+ POut.flush();
+ Out << Proto;
+ return std::string(Name);
+ }
+
POut << "(";
if (FT) {
for (unsigned i = 0, e = Decl->getNumParams(); i != e; ++i) {
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index 7dcc4348f8e036..2a3e32bc6995db 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -1635,6 +1635,17 @@ void TypePrinter::printElaboratedBefore(const ElaboratedType *T,
if (T->getKeyword() != ElaboratedTypeKeyword::None)
OS << " ";
NestedNameSpecifier *Qualifier = T->getQualifier();
+ if (!Policy.SuppressTagKeyword && Policy.SuppressScope &&
+ !Policy.SuppressUnwrittenScope) {
+ bool OldTagKeyword = Policy.SuppressTagKeyword;
+ bool OldSupressScope = Policy.SuppressScope;
+ Policy.SuppressTagKeyword = true;
+ Policy.SuppressScope = false;
+ printBefore(T->getNamedType(), OS);
+ Policy.SuppressTagKeyword = OldTagKeyword;
+ Policy.SuppressScope = OldSupressScope;
+ return;
+ }
if (Qualifier)
Qualifier->print(OS, Policy);
}
@@ -2260,10 +2271,15 @@ printTo(raw_ostream &OS, ArrayRef<TA> Args, const PrintingPolicy &Policy,
} else {
if (!FirstArg)
OS << Comma;
- // Tries to print the argument with location info if exists.
- printArgument(Arg, Policy, ArgOS,
- TemplateParameterList::shouldIncludeTypeForArgument(
- Policy, TPL, ParmIndex));
+ if (!Policy.SuppressTagKeyword &&
+ Argument.getKind() == TemplateArgument::Type &&
+ isa<TagType>(Argument.getAsType()))
+ OS << Argument.getAsType().getAsString();
+ else
+ // Tries to print the argument with location info if exists.
+ printArgument(Arg, Policy, ArgOS,
+ TemplateParameterList::shouldIncludeTypeForArgument(
+ Policy, TPL, ParmIndex));
}
StringRef ArgString = ArgOS.str();
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 8725b09f8546cf..db1bb70c2d69f6 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -3740,7 +3740,10 @@ ExprResult Sema::BuildPredefinedExpr(SourceLocation Loc,
else {
// Pre-defined identifiers are of type char[x], where x is the length of
// the string.
- auto Str = PredefinedExpr::ComputeName(IK, currentDecl);
+ bool ForceElaboratedPrinting =
+ IK == PredefinedIdentKind::Function && getLangOpts().MSVCCompat;
+ auto Str =
+ PredefinedExpr::ComputeName(IK, currentDecl, ForceElaboratedPrinting);
unsigned Length = Str.length();
llvm::APInt LengthI(32, Length + 1);
diff --git a/clang/test/Analysis/eval-predefined-exprs.cpp b/clang/test/Analysis/eval-predefined-exprs.cpp
index 1eec4476a065f3..b26091869cd03f 100644
--- a/clang/test/Analysis/eval-predefined-exprs.cpp
+++ b/clang/test/Analysis/eval-predefined-exprs.cpp
@@ -52,12 +52,13 @@ void foo() {
struct A {
A() {
- clang_analyzer_dump(__func__);
- clang_analyzer_dump(__FUNCTION__);
- clang_analyzer_dump(__PRETTY_FUNCTION__);
- // expected-warning at -3 {{&Element{"A",0 S64b,char}}}
- // expected-warning at -3 {{&Element{"A",0 S64b,char}}}
- // expected-warning at -3 {{&Element{"A::A()",0 S64b,char}}}
+ clang_analyzer_dump(__func__); // expected-warning {{&Element{"A",0 S64b,char}}}
+#ifdef ANALYZER_MS
+ clang_analyzer_dump(__FUNCTION__); // expected-warning {{&Element{"A::A",0 S64b,char}}}
+#else
+ clang_analyzer_dump(__FUNCTION__); // expected-warning {{&Element{"A",0 S64b,char}}}
+#endif
+ clang_analyzer_dump(__PRETTY_FUNCTION__); // expected-warning {{&Element{"A::A()",0 S64b,char}}}
#ifdef ANALYZER_MS
clang_analyzer_dump(__FUNCDNAME__);
@@ -71,12 +72,13 @@ struct A {
#endif
}
~A() {
- clang_analyzer_dump(__func__);
- clang_analyzer_dump(__FUNCTION__);
- clang_analyzer_dump(__PRETTY_FUNCTION__);
- // expected-warning at -3 {{&Element{"~A",0 S64b,char}}}
- // expected-warning at -3 {{&Element{"~A",0 S64b,char}}}
- // expected-warning at -3 {{&Element{"A::~A()",0 S64b,char}}}
+ clang_analyzer_dump(__func__); // expected-warning {{&Element{"~A",0 S64b,char}}}
+#ifdef ANALYZER_MS
+ clang_analyzer_dump(__FUNCTION__); // expected-warning {{&Element{"A::~A",0 S64b,char}}}
+#else
+ clang_analyzer_dump(__FUNCTION__); // expected-warning {{&Element{"~A",0 S64b,char}}}
+#endif
+ clang_analyzer_dump(__PRETTY_FUNCTION__); // expected-warning {{&Element{"A::~A()",0 S64b,char}}}
#ifdef ANALYZER_MS
clang_analyzer_dump(__FUNCDNAME__);
diff --git a/clang/test/SemaCXX/source_location.cpp b/clang/test/SemaCXX/source_location.cpp
index b151fc45fdad62..63157cfacdd98b 100644
--- a/clang/test/SemaCXX/source_location.cpp
+++ b/clang/test/SemaCXX/source_location.cpp
@@ -1,14 +1,14 @@
// RUN: %clang_cc1 -std=c++1z -fcxx-exceptions -fexceptions -verify %s
// RUN: %clang_cc1 -std=c++2a -fcxx-exceptions -DUSE_CONSTEVAL -fexceptions -verify %s
// RUN: %clang_cc1 -std=c++2b -fcxx-exceptions -DUSE_CONSTEVAL -DPAREN_INIT -fexceptions -verify %s
-// RUN: %clang_cc1 -std=c++1z -fcxx-exceptions -fms-extensions -DMS -fexceptions -verify %s
-// RUN: %clang_cc1 -std=c++2a -fcxx-exceptions -fms-extensions -DMS -DUSE_CONSTEVAL -fexceptions -verify %s
+// RUN: %clang_cc1 -std=c++1z -fcxx-exceptions -fms-extensions -DMS -fexceptions -fms-compatibility -verify %s
+// RUN: %clang_cc1 -std=c++2a -fcxx-exceptions -fms-extensions -DMS -DUSE_CONSTEVAL -fexceptions -fms-compatibility -verify %s
//
// RUN: %clang_cc1 -std=c++1z -fcxx-exceptions -fexceptions -fexperimental-new-constant-interpreter -DNEW_INTERP -verify %s
// RUN: %clang_cc1 -std=c++2a -fcxx-exceptions -DUSE_CONSTEVAL -fexceptions -fexperimental-new-constant-interpreter -DNEW_INTERP -verify %s
// RUN: %clang_cc1 -std=c++2b -fcxx-exceptions -DUSE_CONSTEVAL -DPAREN_INIT -fexceptions -fexperimental-new-constant-interpreter -DNEW_INTERP -verify %s
-// RUN: %clang_cc1 -std=c++1z -fcxx-exceptions -fms-extensions -DMS -fexceptions -fexperimental-new-constant-interpreter -DNEW_INTERP -verify %s
-// RUN: %clang_cc1 -std=c++2a -fcxx-exceptions -fms-extensions -DMS -DUSE_CONSTEVAL -fexceptions -fexperimental-new-constant-interpreter -DNEW_INTERP -verify %s
+// RUN: %clang_cc1 -std=c++1z -fcxx-exceptions -fms-extensions -DMS -fexceptions -fexperimental-new-constant-interpreter -DNEW_INTERP -fms-compatibility -verify %s
+// RUN: %clang_cc1 -std=c++2a -fcxx-exceptions -fms-extensions -DMS -DUSE_CONSTEVAL -fexceptions -fexperimental-new-constant-interpreter -DNEW_INTERP -verify -fms-compatibility %s
// expected-no-diagnostics
#define assert(...) ((__VA_ARGS__) ? ((void)0) : throw 42)
@@ -463,8 +463,70 @@ void ctor_tests() {
constexpr SL global_sl = SL::current();
static_assert(is_equal(global_sl.function(), ""));
+template <class T>
+class TestBI {
+public:
+ TestBI() {
+#ifdef MS
+ static_assert(is_equal(__FUNCTION__, "test_func::TestBI<int>::TestBI"));
+#else
+ static_assert(is_equal(__FUNCTION__, "TestBI"));
+#endif
+ static_assert(is_equal(__func__, "TestBI"));
+ }
+};
+
+template <class T>
+class TestClass {
+public:
+ TestClass() {
+#ifdef MS
+ static_assert(is_equal(__FUNCTION__, "test_func::TestClass<class test_func::C>::TestClass"));
+#else
+ static_assert(is_equal(__FUNCTION__, "TestClass"));
+#endif
+ static_assert(is_equal(__func__, "TestClass"));
+ }
+};
+
+template <class T>
+class TestStruct {
+public:
+ TestStruct() {
+#ifdef MS
+ static_assert(is_equal(__FUNCTION__, "test_func::TestStruct<struct test_func::S>::TestStruct"));
+#else
+ static_assert(is_equal(__FUNCTION__, "TestStruct"));
+#endif
+ static_assert(is_equal(__func__, "TestStruct"));
+ }
+};
+
+template <class T>
+class TestEnum {
+public:
+ TestEnum() {
+#ifdef MS
+ static_assert(is_equal(__FUNCTION__, "test_func::TestEnum<enum test_func::E>::TestEnum"));
+#else
+ static_assert(is_equal(__FUNCTION__, "TestEnum"));
+#endif
+ static_assert(is_equal(__func__, "TestEnum"));
+ }
+};
+
+class C {};
+struct S {};
+enum E {};
+
+TestBI<int> t1;
+TestClass<test_func::C> t2;
+TestStruct<test_func::S> t3;
+TestEnum<test_func::E> t4;
+
} // namespace test_func
+
//===----------------------------------------------------------------------===//
// __builtin_FUNCSIG()
//===----------------------------------------------------------------------===//
diff --git a/clang/unittests/AST/DeclPrinterTest.cpp b/clang/unittests/AST/DeclPrinterTest.cpp
index 0e09ab2a7bba88..e024c41e03b484 100644
--- a/clang/unittests/AST/DeclPrinterTest.cpp
+++ b/clang/unittests/AST/DeclPrinterTest.cpp
@@ -358,6 +358,59 @@ TEST(DeclPrinter, TestCXXRecordDecl11) {
"class A : virtual public Z, private Y {}"));
}
+TEST(DeclPrinter, TestCXXRecordDecl12) {
+ ASSERT_TRUE(
+ PrintedDeclCXX98Matches("struct S { int x; };"
+ "namespace NS { class C {};}"
+ "void foo() {using namespace NS; C c;}",
+ "foo",
+ "void foo() {\nusing namespace NS;\nclass "
+ "NS::C c;\n}\n",
+ [](PrintingPolicy &Policy) {
+ Policy.SuppressTagKeyword = false;
+ Policy.SuppressScope = true;
+ Policy.TerseOutput = false;
+ }));
+}
+
+TEST(DeclPrinter, TestCXXRecordDecl13) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "struct S { int x; };"
+ "S s1;"
+ "S foo() {return s1;}",
+ "foo", "struct S foo() {\nreturn s1;\n}\n", [](PrintingPolicy &Policy) {
+ Policy.SuppressTagKeyword = false;
+ Policy.SuppressScope = true;
+ Policy.TerseOutput = false;
+ }));
+}
+
+TEST(DeclPrinter, TestCXXRecordDecl14) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "struct S { int x; };"
+ "S foo(S s1) {return s1;}",
+ "foo", "struct S foo(struct S s1) {\nreturn s1;\n}\n",
+ [](PrintingPolicy &Policy) {
+ Policy.SuppressTagKeyword = false;
+ Policy.SuppressScope = true;
+ Policy.TerseOutput = false;
+ }));
+}
+TEST(DeclPrinter, TestCXXRecordDecl15) {
+ ASSERT_TRUE(PrintedDeclCXX98Matches(
+ "struct S { int x; };"
+ "namespace NS { class C {};}"
+ "S foo(S s1, NS::C c1) {using namespace NS; C c; return s1;}",
+ "foo",
+ "struct S foo(struct S s1, class NS::C c1) {\nusing namespace NS;\nclass "
+ "NS::C c;\nreturn s1;\n}\n",
+ [](PrintingPolicy &Policy) {
+ Policy.SuppressTagKeyword = false;
+ Policy.SuppressScope = true;
+ Policy.TerseOutput = false;
+ }));
+}
+
TEST(DeclPrinter, TestFunctionDecl1) {
ASSERT_TRUE(PrintedDeclCXX98Matches(
"void A();",
diff --git a/clang/unittests/AST/TypePrinterTest.cpp b/clang/unittests/AST/TypePrinterTest.cpp
index f0a6eb7e9fd8c9..494085a2ebca6b 100644
--- a/clang/unittests/AST/TypePrinterTest.cpp
+++ b/clang/unittests/AST/TypePrinterTest.cpp
@@ -155,6 +155,22 @@ TEST(TypePrinter, TemplateIdWithNTTP) {
}));
}
+TEST(TypePrinter, TemplateArgumentsSubstitution) {
+ constexpr char Code[] = R"cpp(
+ template <typename Y> class X {};
+ typedef X<int> A;
+ int foo() {
+ return sizeof(A);
+ }
+ )cpp";
+ auto Matcher = typedefNameDecl(hasName("A"), hasType(qualType().bind("id")));
+ ASSERT_TRUE(PrintedTypeMatches(Code, {}, Matcher, "X<int>",
+ [](PrintingPolicy &Policy) {
+ Policy.SuppressTagKeyword = false;
+ Policy.SuppressScope = true;
+ }));
+}
+
TEST(TypePrinter, TemplateArgumentsSubstitution_Expressions) {
/// Tests clang::isSubstitutedDefaultArgument on TemplateArguments
/// that are of kind TemplateArgument::Expression
More information about the cfe-commits
mailing list