From 99eab31a80d93eacea2e8687e12cf34a381fa68c Mon Sep 17 00:00:00 2001 From: Matheus Izvekov Date: Wed, 29 Jan 2025 21:58:55 -0300 Subject: [PATCH] [clang] StmtPrinter: Handle DeclRefExpr to a Decomposition (#125001) A DeclRefExpr could never refer to a Decomposition in valid C++ code, but somehow the Analyzer creates these entities and then it tries to print them. There is no sensible answer here, so we print 'decomposition' followed by the names of all of its bindings, separated by dashes. (cherry picked from commit 00c096e604ad3a5244af06602556f8de867e36c4) --- clang/lib/AST/StmtPrinter.cpp | 8 +- clang/test/Analysis/anonymous-decls.cpp | 89 +++++++++++++++++++++ clang/test/Analysis/anonymous-parameter.cpp | 30 ------- 3 files changed, 96 insertions(+), 31 deletions(-) create mode 100644 clang/test/Analysis/anonymous-decls.cpp delete mode 100644 clang/test/Analysis/anonymous-parameter.cpp diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 9efc88436f928..9299d4cb4aba8 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -1291,8 +1291,14 @@ void StmtPrinter::VisitDeclRefExpr(DeclRefExpr *Node) { << PD->getFunctionScopeIndex(); break; } + case Decl::Decomposition: + OS << "decomposition"; + for (const auto &I : cast(VD)->bindings()) + OS << '-' << I->getName(); + break; default: - llvm_unreachable("Unhandled anonymous declaration kind"); + OS << "unhandled-anonymous-" << VD->getDeclKindName(); + break; } } if (Node->hasExplicitTemplateArgs()) { diff --git a/clang/test/Analysis/anonymous-decls.cpp b/clang/test/Analysis/anonymous-decls.cpp new file mode 100644 index 0000000000000..211184523aa51 --- /dev/null +++ b/clang/test/Analysis/anonymous-decls.cpp @@ -0,0 +1,89 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -triple x86_64-apple-darwin12 -std=c++20 %s 2>&1 | FileCheck %s + +struct A { + static A a; + char b; + friend bool operator==(A, A) = default; +}; +bool _ = A() == A::a; + +// FIXME: steps 1 and 5 show anonymous function parameters are +// not handled correctly. + +// CHECK-LABEL: bool operator==(A, A) noexcept = default +// CHECK-NEXT: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1] +// CHECK-NEXT: 1: function-parameter-0-0 +// CHECK-NEXT: 2: [B1.1].b +// CHECK-NEXT: 3: [B1.2] (ImplicitCastExpr, LValueToRValue, char) +// CHECK-NEXT: 4: [B1.3] (ImplicitCastExpr, IntegralCast, int) +// CHECK-NEXT: 5: function-parameter-0-1 +// CHECK-NEXT: 6: [B1.5].b +// CHECK-NEXT: 7: [B1.6] (ImplicitCastExpr, LValueToRValue, char) +// CHECK-NEXT: 8: [B1.7] (ImplicitCastExpr, IntegralCast, int) +// CHECK-NEXT: 9: [B1.4] == [B1.8] +// CHECK-NEXT: 10: return [B1.9]; +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 + +namespace std { +template struct iterator_traits; +template struct pair; +template struct iterator_traits<_Tp *> { + typedef _Tp &reference; +}; +template struct tuple_element; +template struct tuple_size; +template struct tuple_size> { + static const int value = 2; +}; +template struct tuple_element<0, pair<_T1, _T2>> { + using type = _T1; +}; +template struct tuple_element<1, pair<_T1, _T2>> { + using type = _T2; +}; +template +tuple_element<_Ip, pair<_T1, _T2>>::type get(pair<_T1, _T2> &); +struct __wrap_iter { + iterator_traits *>::reference operator*(); + void operator++(); +}; +bool operator!=(__wrap_iter, __wrap_iter); +struct vector { + __wrap_iter begin(); + __wrap_iter end(); +}; +} // namespace std +int main() { + std::vector v; + for (auto &[a, b] : v) + ; +} + +// FIXME: On steps 8 and 14, a decomposition is referred by name, which they never have. + +// CHECK-LABEL: int main() +// CHECK: [B3] +// CHECK-NEXT: 1: operator* +// CHECK-NEXT: 2: [B3.1] (ImplicitCastExpr, FunctionToPointerDecay, iterator_traits *>::reference (*)(void)) +// CHECK-NEXT: 3: __begin1 +// CHECK-NEXT: 4: * [B3.3] (OperatorCall) +// CHECK-NEXT: 5: auto &; +// CHECK-NEXT: 6: get<0UL> +// CHECK-NEXT: 7: [B3.6] (ImplicitCastExpr, FunctionToPointerDecay, typename tuple_element<0L, pair >::type (*)(pair &)) +// CHECK-NEXT: 8: decomposition-a-b +// CHECK-NEXT: 9: [B3.7]([B3.8]) +// CHECK-NEXT: 10: [B3.9] +// CHECK-NEXT: 11: std::tuple_element<0, std::pair>::type a = get<0UL>(decomposition-a-b); +// CHECK-NEXT: 12: get<1UL> +// CHECK-NEXT: 13: [B3.12] (ImplicitCastExpr, FunctionToPointerDecay, typename tuple_element<1L, pair >::type (*)(pair &)) +// CHECK-NEXT: 14: decomposition-a-b +// CHECK-NEXT: 15: [B3.13]([B3.14]) +// CHECK-NEXT: 16: [B3.15] +// CHECK-NEXT: 17: std::tuple_element<1, std::pair>::type b = get<1UL>(decomposition-a-b); +// CHECK-NEXT: Preds (1): B1 +// CHECK-NEXT: Succs (1): B2 diff --git a/clang/test/Analysis/anonymous-parameter.cpp b/clang/test/Analysis/anonymous-parameter.cpp deleted file mode 100644 index ad2a00b3329cb..0000000000000 --- a/clang/test/Analysis/anonymous-parameter.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -triple x86_64-apple-darwin12 -std=c++20 %s 2>&1 | FileCheck %s - -struct A { - static A a; - char b; - friend bool operator==(A, A) = default; -}; -bool _ = A() == A::a; - -// FIXME: steps 1 and 5 show anonymous function parameters are -// not handled correctly. - -// CHECK-LABEL: bool operator==(A, A) noexcept = default -// CHECK-NEXT: [B2 (ENTRY)] -// CHECK-NEXT: Succs (1): B1 -// CHECK: [B1] -// CHECK-NEXT: 1: function-parameter-0-0 -// CHECK-NEXT: 2: [B1.1].b -// CHECK-NEXT: 3: [B1.2] (ImplicitCastExpr, LValueToRValue, char) -// CHECK-NEXT: 4: [B1.3] (ImplicitCastExpr, IntegralCast, int) -// CHECK-NEXT: 5: function-parameter-0-1 -// CHECK-NEXT: 6: [B1.5].b -// CHECK-NEXT: 7: [B1.6] (ImplicitCastExpr, LValueToRValue, char) -// CHECK-NEXT: 8: [B1.7] (ImplicitCastExpr, IntegralCast, int) -// CHECK-NEXT: 9: [B1.4] == [B1.8] -// CHECK-NEXT: 10: return [B1.9]; -// CHECK-NEXT: Preds (1): B2 -// CHECK-NEXT: Succs (1): B0 -// CHECK: [B0 (EXIT)] -// CHECK-NEXT: Preds (1): B1