Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -71,20 +71,20 @@ void ContainerDataPointerCheck::registerMatchers(MatchFinder *Finder) {

const auto Zero = integerLiteral(equals(0));

const auto SubscriptOperator = callee(cxxMethodDecl(hasName("operator[]")));

Finder->addMatcher(
const auto AddressOfMatcher =
unaryOperator(
unless(isExpansionInSystemHeader()), hasOperatorName("&"),
hasUnaryOperand(expr(
anyOf(cxxOperatorCallExpr(SubscriptOperator, argumentCountIs(2),
hasArgument(0, ContainerExpr),
hasArgument(1, Zero)),
cxxMemberCallExpr(SubscriptOperator, on(ContainerExpr),
argumentCountIs(1), hasArgument(0, Zero)),
arraySubscriptExpr(hasLHS(ContainerExpr), hasRHS(Zero))))))
.bind(AddressOfName),
this);
hasUnaryOperand(ignoringParenImpCasts(expr(anyOf(
cxxOperatorCallExpr(
hasOverloadedOperatorName("[]"), argumentCountIs(2),
hasArgument(0, ContainerExpr), hasArgument(1, Zero)),
cxxMemberCallExpr(callee(cxxMethodDecl(hasName("operator[]"))),
on(ContainerExpr), argumentCountIs(1),
hasArgument(0, Zero)),
arraySubscriptExpr(hasLHS(ContainerExpr), hasRHS(Zero)))))))
.bind(AddressOfName);

Finder->addMatcher(traverse(TK_AsIs, AddressOfMatcher), this);
Copy link
Contributor

Choose a reason for hiding this comment

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

You moved TK_AsIs from functions-level to addMatcher which essentially is the same.
What I suggest is not introducing TK_AsIs in the first place anywhere

Copy link
Contributor Author

@zeyi2 zeyi2 Nov 5, 2025

Choose a reason for hiding this comment

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

Hi, I tried to implement the new check without TK_AsIs and I encountered some difficulties:

The former template case m is a simple "dependent type + subscript" inside a template body. The old matcher can recognize containers by inspecting the primary template declaration. The new test case u is an "overloaded operator* followed by operator[]", it requires knowing that the dependent result type T actually has a data() method to safely emit (*p).data().

With TK_IgnoreUnlessSpelledInSource, many nodes remain dependent in uninstantiated template bodies, which makes predicates fail to check whether the type really has .data().

To fix this issue, I switched to a syntax‑driven approach that doesn’t rely on type constraints in the matcher (only matches address-of subscript zero) and do all container detection in check(). But still, resolving "type has a public data()" on a dependent specialization is not reliably possible, so now the fix for m (return v.data();) cannot be emitted conservatively.

I think it will be hard to proceed without TK_AsIs, I may need to reimplement a dependency-aware walk from a dependent specialization back to its primary template in check(), which may introduce more problems.

Would you be open to a compromise where we enable TK_AsIs only for this check? IMHO it is a more practical and low-risk option.


Please forgive any shortcomings in my English. If anything isn’t clear, please let me know. I appreciate all feedback.

}

void ContainerDataPointerCheck::check(const MatchFinder::MatchResult &Result) {
Expand All @@ -101,14 +101,20 @@ void ContainerDataPointerCheck::check(const MatchFinder::MatchResult &Result) {
else if (ACE)
CE = ACE;

SourceRange SrcRange = CE->getSourceRange();
const Expr *PrintedCE = CE->IgnoreParenImpCasts();

const SourceRange SrcRange = PrintedCE->getSourceRange();

std::string ReplacementText{
Lexer::getSourceText(CharSourceRange::getTokenRange(SrcRange),
*Result.SourceManager, getLangOpts())};

if (!isa<DeclRefExpr, ArraySubscriptExpr, CXXOperatorCallExpr, CallExpr,
MemberExpr>(CE))
const auto *OpCall = dyn_cast<CXXOperatorCallExpr>(PrintedCE);
const bool NeedsParens =
OpCall ? (OpCall->getOperator() != OO_Subscript)
: !isa<DeclRefExpr, MemberExpr, ArraySubscriptExpr, CallExpr>(
PrintedCE);
if (NeedsParens)
ReplacementText = "(" + ReplacementText + ")";

if (CE->getType()->isPointerType())
Expand Down
6 changes: 5 additions & 1 deletion clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ Changes in existing checks

- Improved :doc:`misc-const-correctness
<clang-tidy/checks/misc/const-correctness>` check to avoid false
positives when pointers is transferred to non-const references
positives when pointers is transferred to non-const references
and avoid false positives of function pointer and fix false
positives on return of non-const pointer.

Expand Down Expand Up @@ -429,6 +429,10 @@ Changes in existing checks
comparisons to ``npos``. Internal changes may cause new rare false positives
in non-standard containers.

- Improved :doc:`readability-container-data-pointer
<clang-tidy/checks/readability/container-data-pointer>` check by correctly
adding parentheses when the container expression is a dereference.

- Improved :doc:`readability-container-size-empty
<clang-tidy/checks/readability/container-size-empty>` check by correctly
generating fix-it hints when size method is called from implicit ``this``,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ template <typename T>
struct enable_if<true, T> {
typedef T type;
};

template <typename T>
struct unique_ptr {
T &operator*() const;
T *operator->() const;
};
}

template <typename T>
Expand Down Expand Up @@ -144,3 +150,18 @@ int *r() {
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: 'data' should be used for accessing the data pointer instead of taking the address of the 0-th element [readability-container-data-pointer]
// CHECK-FIXES: return holder.v.data();
}

void s(std::unique_ptr<std::vector<unsigned char>> p) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we add test with templated function and unique_ptr<T>?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hi, thanks for reviewing. I've added a new test as requested :)

f(&(*p)[0]);
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 'data' should be used for accessing the data pointer instead of taking the address of the 0-th element [readability-container-data-pointer]
// CHECK-FIXES: f((*p).data());
}

template <typename T>
void u(std::unique_ptr<T> p) {
f(&(*p)[0]);
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 'data' should be used for accessing the data pointer instead of taking the address of the 0-th element [readability-container-data-pointer]
// CHECK-FIXES: f((*p).data());
}

template void u<std::vector<int>>(std::unique_ptr<std::vector<int>>);