llvm-project/clang/test/Analysis/analyzer-note-analysis-entry-points.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

76 lines
2.7 KiB
C++

// RUN: %clang_analyze_cc1 -verify=common %s \
// RUN: -analyzer-checker=deadcode.DeadStores,debug.ExprInspection \
// RUN: -analyzer-note-analysis-entry-points
// RUN: %clang_analyze_cc1 -verify=common,textout %s \
// RUN: -analyzer-checker=deadcode.DeadStores,debug.ExprInspection \
// RUN: -analyzer-note-analysis-entry-points \
// RUN: -analyzer-output=text
// Test the actual source locations/ranges of entry point notes.
// RUN: %clang_analyze_cc1 %s \
// RUN: -analyzer-checker=deadcode.DeadStores,debug.ExprInspection \
// RUN: -analyzer-note-analysis-entry-points \
// RUN: -analyzer-output=text 2>&1 \
// RUN: | FileCheck --strict-whitespace %s
void clang_analyzer_warnIfReached();
void other() {
// common-warning@+1 {{REACHABLE}} textout-note@+1 {{REACHABLE}}
clang_analyzer_warnIfReached();
}
struct SomeOtherStruct {
// CHECK: note: [debug] analyzing from SomeOtherStruct::f()
// CHECK-NEXT: | void f() {
// CHECK-NEXT: | ^
// textout-note@+1 {{[debug] analyzing from SomeOtherStruct::f()}}
void f() {
other(); // textout-note {{Calling 'other'}}
}
};
// CHECK: note: [debug] analyzing from operator""_w(const char *)
// CHECK-NEXT: | unsigned operator ""_w(const char*) {
// CHECK-NEXT: | ^
// textout-note@+1 {{[debug] analyzing from operator""_w(const char *)}}
unsigned operator ""_w(const char*) {
// common-warning@+1 {{REACHABLE}} textout-note@+1 {{REACHABLE}}
clang_analyzer_warnIfReached();
return 404;
}
// textout-note@+1 {{[debug] analyzing from checkASTCodeBodyHasAnalysisEntryPoints()}}
void checkASTCodeBodyHasAnalysisEntryPoints() {
int z = 1;
z = 2;
// common-warning@-1 {{Value stored to 'z' is never read}}
// textout-note@-2 {{Value stored to 'z' is never read}}
}
void notInvokedLambdaScope() {
// CHECK: note: [debug] analyzing from notInvokedLambdaScope()::(lambda)::operator()()
// CHECK-NEXT: | auto notInvokedLambda = []() {
// CHECK-NEXT: | ^
// textout-note@+1 {{[debug] analyzing from notInvokedLambdaScope()::(lambda)::operator()()}}
auto notInvokedLambda = []() {
// common-warning@+1 {{REACHABLE}} textout-note@+1 {{REACHABLE}}
clang_analyzer_warnIfReached();
};
(void)notInvokedLambda; // Not invoking the lambda.
}
// CHECK: note: [debug] analyzing from invokedLambdaScope()
// CHECK-NEXT: | void invokedLambdaScope() {
// CHECK-NEXT: | ^
// textout-note@+1 {{[debug] analyzing from invokedLambdaScope()}}
void invokedLambdaScope() {
auto invokedLambda = []() {
// common-warning@+1 {{REACHABLE}} textout-note@+1 {{REACHABLE}}
clang_analyzer_warnIfReached();
};
invokedLambda(); // textout-note {{Calling 'operator()'}}
}