llvm-project/clang/test/Analysis/bug_hash_test.cpp
Michael Buch 4ef6419164
[clang][TypePrinter] Unify printing of anonymous/unnamed tag types (#169445)
In https://github.com/llvm/llvm-project/pull/168534 we made the
`TypePrinter` re-use `printNestedNameSpecifier` for printing scopes.
However, the way that the names of anonymous/unnamed types get printed
by the two are slightly inconsistent with each other.

`printNestedNameSpecifier` calls all `TagType`s without an identifer
`(anonymous)`. On the other hand, `TypePrinter` prints them slightly
more accurate (it differentiates anonymous vs. unnamed decls) and allows
for some additional customization points. E.g., with `MSVCFormatting`,
it will print `` `unnamed struct'`` instead of `(unnamed struct)`.
`printNestedNameSpecifier` already accounts for `MSVCFormatting` for
namespaces, but doesn't for `TagType`s. This inconsistency means that if
an unnamed tag is printed as part of a scope then it's displayed as
`(anonymous struct)`, but if it's the entity whose scope is being
printed, then it shows as `(unnamed struct)`.

This patch moves the printing of anonymous/unnamed tags into
`TagDecl::printName`. All the callsites that previously printed
anonymous tag decls now call `printName` to handle it. To preserve the
behaviour of not printing the kind name (i.e., `struct`/`class`/`enum`)
when printing the inner type of an elaborated type (i.e., avoiding
`struct (unnamed struct)`), this patch adds a
`PrintingPolicy::SuppressTagKeywordInAnonNames` that is appropriately
set when we want to suppress the tag keyword inside the anonymous name.
I had to make sure we set this bit to `false` when printing
nested-name-specifiers because we always want the tag keyword there
(e.g., `foo::(anonymous struct)::bar`) and for a `clangd` special case
which is described in a comment in the source.

**Test changes**

Mostly we now more accurately print the kind name of anonymous entities.
So there's a lot of `anonymous` -> `unnamed` changes. There are a
handful of `clangd` tests where the name of the entity is now `(unnamed
struct)` instead of just `(unnamed)`. That should be consistent with how
we choose to omit the tag keyword elsewhere. Since we're just printing
the name of the entity here, we include the kind tag.
2026-01-06 10:57:59 +00:00

123 lines
3.8 KiB
C++

// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,debug.ExprInspection %s -verify
constexpr int clang_analyzer_hashDump(int) { return 5; }
void function(int) {
clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$void function(int)$27$clang_analyzer_hashDump(5);$Category}}
}
namespace {
void variadicParam(int, ...) {
clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$void (anonymous namespace)::variadicParam(int, ...)$27$clang_analyzer_hashDump(5);$Category}}
}
} // namespace
constexpr int f() {
return clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$int f()$34$returnclang_analyzer_hashDump(5);$Category}}
}
namespace AA {
class X {
X() {
clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$AA::X::X()$29$clang_analyzer_hashDump(5);$Category}}
}
static void static_method() {
clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$void AA::X::static_method()$29$clang_analyzer_hashDump(5);$Category}}
variadicParam(5);
}
void method() && {
struct Y {
inline void method() const & {
clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$void AA::X::method()::Y::method() const &$33$clang_analyzer_hashDump(5);$Category}}
}
};
Y y;
y.method();
clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$void AA::X::method() &&$29$clang_analyzer_hashDump(5);$Category}}
}
void OutOfLine();
X &operator=(int) {
clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$X & AA::X::operator=(int)$29$clang_analyzer_hashDump(5);$Category}}
return *this;
}
operator int() {
clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$AA::X::operator int()$29$clang_analyzer_hashDump(5);$Category}}
return 0;
}
explicit operator float() {
clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$AA::X::operator float()$29$clang_analyzer_hashDump(5);$Category}}
return 0;
}
};
} // namespace AA
void AA::X::OutOfLine() {
clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$void AA::X::OutOfLine()$27$clang_analyzer_hashDump(5);$Category}}
}
void testLambda() {
[]() {
clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$void testLambda()::(lambda)::operator()() const$29$clang_analyzer_hashDump(5);$Category}}
}();
}
template <typename T>
void f(T) {
clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$void f(T)$27$clang_analyzer_hashDump(5);$Category}}
}
template <typename T>
struct TX {
void f(T) {
clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$void TX::f(T)$29$clang_analyzer_hashDump(5);$Category}}
}
};
template <>
void f<long>(long) {
clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$void f(long)$27$clang_analyzer_hashDump(5);$Category}}
}
template <>
struct TX<long> {
void f(long) {
clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$void TX<long>::f(long)$29$clang_analyzer_hashDump(5);$Category}}
}
};
template <typename T>
struct TTX {
template<typename S>
void f(T, S) {
clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$void TTX::f(T, S)$29$clang_analyzer_hashDump(5);$Category}}
}
};
void g() {
// TX<int> and TX<double> is instantiated from the same code with the same
// source locations. The same error happining in both of the instantiations
// should share the common hash. This means we should not include the
// template argument for these types in the function signature.
// Note that, we still want the hash to be different for explicit
// specializations.
TX<int> x;
TX<double> y;
TX<long> xl;
x.f(1);
xl.f(1);
f(5);
f(3.0);
y.f(2);
TTX<int> z;
z.f<int>(5, 5);
f(5l);
}