Author: lianjinfeng2003
Date: 2026-07-07T10:16:21+02:00
New Revision: 70646dd3eda3e64fc94b44d7403f946cd7455b92

URL: 
https://github.com/llvm/llvm-project/commit/70646dd3eda3e64fc94b44d7403f946cd7455b92
DIFF: 
https://github.com/llvm/llvm-project/commit/70646dd3eda3e64fc94b44d7403f946cd7455b92.diff

LOG: [clang][AST] Qualify DeclRefExpr printing (#206041)

I updated DeclRefExpr printing so fully-qualified printing consistently
uses declaration ownership rather than only the written qualifier. This
also preserves existing behavior for local names and cleaned parameter
names.
The added tests cover both the direct expression printer behavior and
the original type-printing scenario that exposed the missing
qualification.

Fixes #205925

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.md
    clang/lib/AST/StmtPrinter.cpp
    clang/unittests/AST/StmtPrinterTest.cpp
    clang/unittests/AST/TypePrinterTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 9683dec78cec1..a37e1be5862b3 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -165,6 +165,10 @@ latest release, please see the [Clang Web 
Site](https://clang.llvm.org) or the
   fields were missing from the JSON output.
 - Colons that appear at the end of a ParamCommentCommand name are not 
serialized
   as part of the name.
+- AST pretty-printing now respects `PrintingPolicy::FullyQualifiedName` when
+  printing `DeclRefExpr` names, including expression-form non-type template
+  arguments in printed types. Previously, these references could be printed
+  unqualified. (#GH206041)
 
 ### Clang Frontend Potentially Breaking Changes
 

diff  --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index 6c3294573e9d4..e4dc41cf2c043 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -1330,42 +1330,49 @@ void StmtPrinter::VisitDeclRefExpr(DeclRefExpr *Node) {
     TPOD->printAsExpr(OS, Policy);
     return;
   }
-  Node->getQualifier().print(OS, Policy);
-  if (Node->hasTemplateKeyword())
-    OS << "template ";
-
   bool ForceAnonymous =
       Policy.PrintAsCanonical && VD->getKind() == Decl::NonTypeTemplateParm;
-  DeclarationNameInfo NameInfo = Node->getNameInfo();
-  if (IdentifierInfo *ID = NameInfo.getName().getAsIdentifierInfo();
-      !ForceAnonymous &&
-      (ID || NameInfo.getName().getNameKind() != DeclarationName::Identifier)) 
{
-    if (Policy.CleanUglifiedParameters &&
-        isa<ParmVarDecl, NonTypeTemplateParmDecl>(VD) && ID)
-      OS << ID->deuglifiedName();
-    else
-      NameInfo.printName(OS, Policy);
+  bool CleanUglifiedParameter = Policy.CleanUglifiedParameters &&
+                                isa<ParmVarDecl, NonTypeTemplateParmDecl>(VD);
+
+  if (Policy.FullyQualifiedName && !ForceAnonymous && !CleanUglifiedParameter) 
{
+    VD->printQualifiedName(OS, Policy);
   } else {
-    switch (VD->getKind()) {
-    case Decl::NonTypeTemplateParm: {
-      auto *TD = cast<NonTypeTemplateParmDecl>(VD);
-      OS << "value-parameter-" << TD->getDepth() << '-' << TD->getIndex() << 
"";
-      break;
-    }
-    case Decl::ParmVar: {
-      auto *PD = cast<ParmVarDecl>(VD);
-      OS << "function-parameter-" << PD->getFunctionScopeDepth() << '-'
-         << PD->getFunctionScopeIndex();
-      break;
-    }
-    case Decl::Decomposition:
-      OS << "decomposition";
-      for (const auto &I : cast<DecompositionDecl>(VD)->bindings())
-        OS << '-' << I->getName();
-      break;
-    default:
-      OS << "unhandled-anonymous-" << VD->getDeclKindName();
-      break;
+    Node->getQualifier().print(OS, Policy);
+    if (Node->hasTemplateKeyword())
+      OS << "template ";
+
+    DeclarationNameInfo NameInfo = Node->getNameInfo();
+    if (IdentifierInfo *ID = NameInfo.getName().getAsIdentifierInfo();
+        !ForceAnonymous && (ID || NameInfo.getName().getNameKind() !=
+                                      DeclarationName::Identifier)) {
+      if (CleanUglifiedParameter && ID)
+        OS << ID->deuglifiedName();
+      else
+        NameInfo.printName(OS, Policy);
+    } else {
+      switch (VD->getKind()) {
+      case Decl::NonTypeTemplateParm: {
+        auto *TD = cast<NonTypeTemplateParmDecl>(VD);
+        OS << "value-parameter-" << TD->getDepth() << '-' << TD->getIndex()
+           << "";
+        break;
+      }
+      case Decl::ParmVar: {
+        auto *PD = cast<ParmVarDecl>(VD);
+        OS << "function-parameter-" << PD->getFunctionScopeDepth() << '-'
+           << PD->getFunctionScopeIndex();
+        break;
+      }
+      case Decl::Decomposition:
+        OS << "decomposition";
+        for (const auto &I : cast<DecompositionDecl>(VD)->bindings())
+          OS << '-' << I->getName();
+        break;
+      default:
+        OS << "unhandled-anonymous-" << VD->getDeclKindName();
+        break;
+      }
     }
   }
   if (Node->hasExplicitTemplateArgs()) {

diff  --git a/clang/unittests/AST/StmtPrinterTest.cpp 
b/clang/unittests/AST/StmtPrinterTest.cpp
index 24ad5f30d9480..2b75253fa6698 100644
--- a/clang/unittests/AST/StmtPrinterTest.cpp
+++ b/clang/unittests/AST/StmtPrinterTest.cpp
@@ -297,6 +297,42 @@ TEST(StmtPrinter, TerseOutputWithLambdas) {
       [](PrintingPolicy &PP) { PP.TerseOutput = true; }));
 }
 
+TEST(StmtPrinter, FullyQualifiedDeclRefExpr) {
+  auto FullyQualified = [](PrintingPolicy &Policy) {
+    Policy.FullyQualifiedName = true;
+  };
+
+  ASSERT_TRUE(PrintedStmtCXXMatches(
+      StdVer::CXX17,
+      R"cpp(
+        namespace ns { int value; }
+        using namespace ns;
+        void A() { (void)value; }
+      )cpp",
+      declRefExpr(to(varDecl(hasName("::ns::value")))).bind("id"), "ns::value",
+      FullyQualified));
+
+  ASSERT_TRUE(PrintedStmtCXXMatches(
+      StdVer::CXX17,
+      R"cpp(
+        namespace ns { template <class T> void func(); }
+        using namespace ns;
+        void A() { func<int>(); }
+      )cpp",
+      declRefExpr(to(functionDecl(hasName("::ns::func")))).bind("id"),
+      "ns::func<int>", FullyQualified));
+
+  ASSERT_TRUE(PrintedStmtCXXMatches(
+      StdVer::CXX17, "void A(int param) { (void)param; }",
+      declRefExpr(to(parmVarDecl(hasName("param")))).bind("id"), "param",
+      FullyQualified));
+
+  ASSERT_TRUE(PrintedStmtCXXMatches(
+      StdVer::CXX17, "void A() { int local = 0; (void)local; }",
+      declRefExpr(to(varDecl(hasName("local")))).bind("id"), "local",
+      FullyQualified));
+}
+
 TEST(StmtPrinter, ParamsUglified) {
   llvm::StringLiteral Code = R"cpp(
     template <typename _T, int _I, template <typename> class _C>
@@ -307,6 +343,10 @@ TEST(StmtPrinter, ParamsUglified) {
   auto Clean = [](PrintingPolicy &Policy) {
     Policy.CleanUglifiedParameters = true;
   };
+  auto CleanFullyQualified = [](PrintingPolicy &Policy) {
+    Policy.CleanUglifiedParameters = true;
+    Policy.FullyQualifiedName = true;
+  };
 
   ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX14, Code,
                                     returnStmt().bind("id"),
@@ -314,4 +354,7 @@ TEST(StmtPrinter, ParamsUglified) {
   ASSERT_TRUE(
       PrintedStmtCXXMatches(StdVer::CXX14, Code, returnStmt().bind("id"),
                             "return typename C<T>::_F(I, j);\n", Clean));
+  ASSERT_TRUE(PrintedStmtCXXMatches(
+      StdVer::CXX14, Code, returnStmt().bind("id"),
+      "return typename _C<T>::_F(I, j);\n", CleanFullyQualified));
 }

diff  --git a/clang/unittests/AST/TypePrinterTest.cpp 
b/clang/unittests/AST/TypePrinterTest.cpp
index 5023b5e093ec3..79f75909c57b5 100644
--- a/clang/unittests/AST/TypePrinterTest.cpp
+++ b/clang/unittests/AST/TypePrinterTest.cpp
@@ -121,6 +121,30 @@ TEST(TypePrinter, TemplateSpecializationFullyQualified) {
       [](PrintingPolicy &Policy) { Policy.FullyQualifiedName = true; }));
 }
 
+TEST(TypePrinter, TemplateArgumentExpressionFullyQualified) {
+  llvm::StringLiteral Code = R"cpp(
+    namespace ns {
+      template <class T> inline constexpr bool pred_v = sizeof(T) > 0;
+      template <bool, class T> struct ei {};
+      template <class T> struct ei<true, T> { using type = T; };
+      template <bool B, class T> using enable_if_t = typename ei<B, T>::type;
+      struct Ret {};
+      template <class T> enable_if_t<pred_v<T>, Ret> f() { return {}; }
+    }
+    inline auto *ns_f_int = &ns::f<int>;
+  )cpp";
+
+  auto Matcher = functionDecl(hasName("::ns::f"), isTemplateInstantiation(),
+                              returns(qualType().bind("id")));
+  ASSERT_TRUE(PrintedTypeMatches(
+      Code, {"-std=c++17"}, Matcher, "enable_if_t<pred_v<int>, Ret>",
+      [](PrintingPolicy &Policy) { Policy.FullyQualifiedName = false; }));
+  ASSERT_TRUE(PrintedTypeMatches(
+      Code, {"-std=c++17"}, Matcher,
+      "ns::enable_if_t<ns::pred_v<int>, ns::Ret>",
+      [](PrintingPolicy &Policy) { Policy.FullyQualifiedName = true; }));
+}
+
 TEST(TypePrinter, TemplateIdWithNTTP) {
   constexpr char Code[] = R"cpp(
     template <int N>


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to