Skip to content

Commit faf3644

Browse files
author
einvbri
committed
[clang-tidy] [analyzer] Nondeterministic pointer usage improvements
RFC: This PR is not ready to merge yet, preliminary comments are welcome. Here are some questions to consider... Q) Is the name and file location ok in the directory structure? I initially chose bugprone and NondeterministicPointerUsage as a starting point for review. Q) I wrote an explanation for the check based on internal discussion since I felt like this was missing. Please check and comment. Q) There are more ways to iterate over an unordered container than captured here. Do those need to be detected as well? Q) I squashed PointerIteration and PointerSorting. I think that works in this case, but interested to hear your comments about that. Q) I ended up expanding upon the C++ simulation header, but had thoughts about breaking that up into multiple smaller files. Maybe there's an opportunity to refactor some C++ simulation files across multiple checkers as a seperate PR first, or maybe even as part of this one? This change moves the alpha.nondeterministic.PointerSorting and alpha.nondeterministic.PointerIteration static analyzer checkers to a single clang-tidy check. Those checkers were implemented as clang-tidy checks wrapped in the static analyzer framework. The documentation was updated to describe what the checks can and cannot do, and testing was completed on a broad set of open source projects.
1 parent 83fe851 commit faf3644

File tree

13 files changed

+1657
-303
lines changed

13 files changed

+1657
-303
lines changed

clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
#include "MultipleStatementMacroCheck.h"
4949
#include "NoEscapeCheck.h"
5050
#include "NonZeroEnumToBoolConversionCheck.h"
51+
#include "NondeterministicPointerUsageCheck.h"
5152
#include "NotNullTerminatedResultCheck.h"
5253
#include "OptionalValueConversionCheck.h"
5354
#include "ParentVirtualCallCheck.h"
@@ -179,6 +180,8 @@ class BugproneModule : public ClangTidyModule {
179180
CheckFactories.registerCheck<cppcoreguidelines::NarrowingConversionsCheck>(
180181
"bugprone-narrowing-conversions");
181182
CheckFactories.registerCheck<NoEscapeCheck>("bugprone-no-escape");
183+
CheckFactories.registerCheck<NondeterministicPointerUsageCheck>(
184+
"bugprone-nondeterministic-pointer-usage");
182185
CheckFactories.registerCheck<NonZeroEnumToBoolConversionCheck>(
183186
"bugprone-non-zero-enum-to-bool-conversion");
184187
CheckFactories.registerCheck<NotNullTerminatedResultCheck>(

clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ add_clang_library(clangTidyBugproneModule
4444
MultipleNewInOneExpressionCheck.cpp
4545
MultipleStatementMacroCheck.cpp
4646
NoEscapeCheck.cpp
47+
NondeterministicPointerUsageCheck.cpp
4748
NonZeroEnumToBoolConversionCheck.cpp
4849
NotNullTerminatedResultCheck.cpp
4950
OptionalValueConversionCheck.cpp
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//===--- NondetermnisticPointerUsageCheck.cpp - clang-tidy ------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "NondeterministicPointerUsageCheck.h"
10+
#include "clang/AST/ASTContext.h"
11+
#include "clang/Lex/Lexer.h"
12+
13+
using namespace clang::ast_matchers;
14+
15+
namespace clang::tidy::bugprone {
16+
17+
void NondeterministicPointerUsageCheck::registerMatchers(MatchFinder *Finder) {
18+
19+
auto LoopVariable = varDecl(hasType(hasCanonicalType(pointerType())));
20+
21+
auto RangeInit = declRefExpr(to(varDecl(hasType(recordDecl(
22+
anyOf(hasName("std::unordered_set"), hasName("std::unordered_map"),
23+
hasName("std::unordered_multiset"),
24+
hasName("std::unordered_multimap")))))));
25+
26+
Finder->addMatcher(
27+
stmt(cxxForRangeStmt(hasRangeInit(RangeInit.bind("rangeinit")),
28+
hasLoopVariable(LoopVariable.bind("loopVar"))))
29+
.bind("cxxForRangeStmt"),
30+
this);
31+
32+
auto SortFuncM = anyOf(callee(functionDecl(hasName("std::is_sorted"))),
33+
callee(functionDecl(hasName("std::nth_element"))),
34+
callee(functionDecl(hasName("std::sort"))),
35+
callee(functionDecl(hasName("std::partial_sort"))),
36+
callee(functionDecl(hasName("std::partition"))),
37+
callee(functionDecl(hasName("std::stable_partition"))),
38+
callee(functionDecl(hasName("std::stable_sort"))));
39+
40+
auto IteratesPointerEltsM = hasArgument(
41+
0,
42+
cxxMemberCallExpr(on(hasType(cxxRecordDecl(has(fieldDecl(hasType(
43+
hasCanonicalType(pointsTo(hasCanonicalType(pointerType())))))))))));
44+
45+
Finder->addMatcher(stmt(callExpr(allOf(SortFuncM, IteratesPointerEltsM)))
46+
.bind("sortsemantic"),
47+
this);
48+
}
49+
50+
void NondeterministicPointerUsageCheck::check(
51+
const MatchFinder::MatchResult &Result) {
52+
const auto *ForRangePointers =
53+
Result.Nodes.getNodeAs<Stmt>("cxxForRangeStmt");
54+
const auto *SortPointers = Result.Nodes.getNodeAs<Stmt>("sortsemantic");
55+
56+
if ((ForRangePointers) && !(ForRangePointers->getBeginLoc().isMacroID())) {
57+
const auto *Node = dyn_cast<CXXForRangeStmt>(ForRangePointers);
58+
diag(Node->getRParenLoc(), "Iteration of pointers is nondeterministic");
59+
}
60+
61+
if ((SortPointers) && !(SortPointers->getBeginLoc().isMacroID())) {
62+
const auto *Node = dyn_cast<Stmt>(SortPointers);
63+
diag(Node->getBeginLoc(), "Sorting pointers is nondeterministic");
64+
}
65+
}
66+
67+
} // namespace clang::tidy::bugprone
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//===--- NondeterministicPointerUsageCheck.h - clang-tidy ---*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE__NONDETERMINISTICPOINTERUSAGECHECK_H
10+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE__NONDETERMINISTICPOINTERUSAGECHECK_H
11+
12+
#include "../ClangTidyCheck.h"
13+
14+
namespace clang::tidy::bugprone {
15+
16+
/// Finds temporaries that look like RAII objects.
17+
///
18+
/// For the user-facing documentation see:
19+
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/nondeterministic-pointer-usage.html
20+
class NondeterministicPointerUsageCheck : public ClangTidyCheck {
21+
public:
22+
NondeterministicPointerUsageCheck(StringRef Name, ClangTidyContext *Context)
23+
: ClangTidyCheck(Name, Context) {}
24+
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
25+
return LangOpts.CPlusPlus;
26+
}
27+
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
28+
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
29+
std::optional<TraversalKind> getCheckTraversalKind() const override {
30+
return TK_IgnoreUnlessSpelledInSource;
31+
}
32+
};
33+
34+
} // namespace clang::tidy::bugprone
35+
36+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE__NONDETERMINISTICPOINTERUSAGECHECK_H
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
.. title:: clang-tidy - bugprone-nondeterministic-pointer-usage
2+
3+
nondeterministic-pointer-usage
4+
==============================
5+
6+
Finds nondeterministic usages of pointers in unordered containers.
7+
8+
One canonical example is iteration across a container of pointers.
9+
10+
.. code-block:: c++
11+
12+
{
13+
for (auto i : UnorderedPtrSet)
14+
f(i);
15+
}
16+
17+
Another such example is sorting a container of pointers.
18+
19+
.. code-block:: c++
20+
21+
{
22+
std::sort(VectorOfPtr.begin(), VectorOfPtr.end());
23+
}
24+
25+
Iteration of a containers of pointers may present the order of different
26+
pointers differently across different runs of a program. In some cases this
27+
may be acceptable behavior, in others this may be unexpected behavior. This
28+
check is advisory for this reason.
29+
30+
This check only detects range-based for loops over unordered sets. Other
31+
similar usages will not be found and are false negatives.

0 commit comments

Comments
 (0)