Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,10 @@ Changes in existing checks

- ``for`` loops are supported.

- Improved :doc:`misc-const-correctness
<clang-tidy/checks/misc/const-correctness>` check to avoid false
positives when pointers is tranferred to non-const references.
Copy link
Contributor

@localspook localspook Sep 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
positives when pointers is tranferred to non-const references.
positives when pointers to non-const are bound to references.

Since the false positive happens whether the reference itself is const or not.

Copy link
Contributor Author

@flovent flovent Sep 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the second function in link:

void const_ref() {
  int* p1 {nullptr};
  int* const& r1 = p1;
}

It's not fixed by this patch actually, because it's a reference points to const (int* const), and it's not legal to write a const reference version:

   int*  p1 {nullptr};
   const int* & r1 = p1; // error: binding reference of type 'const int *' to value of type 'int *' not permitted due to incompatible qualifiers

And canResolveToExprPointee don't handle NoOp right now, for the AST :

VarDecl <col:4, col:22> col:17 r1 'int *const &' cinit
  `-ImplicitCastExpr <col:22> 'int *const' lvalue <NoOp>
    `-DeclRefExpr <col:22> 'int *' lvalue Var 0x38dc2e98 'p1' 'int *'

So it might need more work to erase this FP.


- Improved :doc:`misc-header-include-cycle
<clang-tidy/checks/misc/header-include-cycle>` check performance.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,18 @@ void ignore_const_alias() {
p_local0 = &a[1];
}

void takeNonConstRef(int *&r);

void ignoreNonConstRefOps() {
// init with non-const ref
int* p0 {nullptr};
int*& r1 = p0;

// non-const ref param
int* p1 {nullptr};
takeNonConstRef(p1);

// cast
int* p2 {nullptr};
int*& r2 = (int*&)p2;
}
25 changes: 13 additions & 12 deletions clang/lib/Analysis/ExprMutationAnalyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -755,22 +755,23 @@ ExprMutationAnalyzer::Analyzer::findPointeeMemberMutation(const Expr *Exp) {

const Stmt *
ExprMutationAnalyzer::Analyzer::findPointeeToNonConst(const Expr *Exp) {
const auto NonConstPointerOrDependentType =
type(anyOf(nonConstPointerType(), isDependentType()));
const auto NonConstPointerOrNonConstRefOrDependentType = type(
anyOf(nonConstPointerType(), nonConstReferenceType(), isDependentType()));

// assign
const auto InitToNonConst =
varDecl(hasType(NonConstPointerOrDependentType),
varDecl(hasType(NonConstPointerOrNonConstRefOrDependentType),
hasInitializer(expr(canResolveToExprPointee(Exp)).bind("stmt")));
const auto AssignToNonConst =
binaryOperation(hasOperatorName("="),
hasLHS(expr(hasType(NonConstPointerOrDependentType))),
hasRHS(canResolveToExprPointee(Exp)));
const auto AssignToNonConst = binaryOperation(
hasOperatorName("="),
hasLHS(expr(hasType(NonConstPointerOrNonConstRefOrDependentType))),
hasRHS(canResolveToExprPointee(Exp)));
// arguments like
const auto ArgOfInstantiationDependent = allOf(
hasAnyArgument(canResolveToExprPointee(Exp)), isInstantiationDependent());
const auto ArgOfNonConstParameter = forEachArgumentWithParamType(
canResolveToExprPointee(Exp), NonConstPointerOrDependentType);
const auto ArgOfNonConstParameter =
forEachArgumentWithParamType(canResolveToExprPointee(Exp),
NonConstPointerOrNonConstRefOrDependentType);
const auto CallLikeMatcher =
anyOf(ArgOfNonConstParameter, ArgOfInstantiationDependent);
const auto PassAsNonConstArg =
Expand All @@ -779,9 +780,9 @@ ExprMutationAnalyzer::Analyzer::findPointeeToNonConst(const Expr *Exp) {
parenListExpr(has(canResolveToExprPointee(Exp))),
initListExpr(hasAnyInit(canResolveToExprPointee(Exp)))));
// cast
const auto CastToNonConst =
explicitCastExpr(hasSourceExpression(canResolveToExprPointee(Exp)),
hasDestinationType(NonConstPointerOrDependentType));
const auto CastToNonConst = explicitCastExpr(
hasSourceExpression(canResolveToExprPointee(Exp)),
hasDestinationType(NonConstPointerOrNonConstRefOrDependentType));

// capture
// FIXME: false positive if the pointee does not change in lambda
Expand Down
23 changes: 23 additions & 0 deletions clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1749,6 +1749,13 @@ TEST(ExprMutationAnalyzerTest, PointeeMutatedByInitToNonConst) {
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
}
{
const std::string Code = "void f() { int* x = nullptr; int*& b = x; }";
auto AST = buildASTFromCodeWithArgs(Code, {});
auto Results =
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
}
}

TEST(ExprMutationAnalyzerTest, PointeeMutatedByAssignToNonConst) {
Expand Down Expand Up @@ -1786,6 +1793,14 @@ TEST(ExprMutationAnalyzerTest, PointeeMutatedByPassAsArgument) {
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
}
{
const std::string Code =
"void b(int *&); void f() { int* x = nullptr; b(x); }";
auto AST = buildASTFromCodeWithArgs(Code, {});
auto Results =
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
}
}

TEST(ExprMutationAnalyzerTest, PointeeMutatedByPassAsArgumentInConstruct) {
Expand Down Expand Up @@ -1884,6 +1899,14 @@ TEST(ExprMutationAnalyzerTest, PointeeMutatedByExplicitCastToNonConst) {
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
}
{
const std::string Code =
"void f() { int* x = nullptr; static_cast<int*&>(x); }";
auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-everything"});
auto Results =
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
}
}

TEST(ExprMutationAnalyzerTest, PointeeMutatedByConstCastToNonConst) {
Expand Down