PointerIterationChecker.cpp revision 353358
1228072Sbapt//== PointerIterationChecker.cpp ------------------------------- -*- C++ -*--=//
2228072Sbapt//
3228072Sbapt// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4228072Sbapt// See https://llvm.org/LICENSE.txt for license information.
5228072Sbapt// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6228072Sbapt//
7228072Sbapt//===----------------------------------------------------------------------===//
8228072Sbapt//
9228072Sbapt// This file defines PointerIterationChecker which checks for non-determinism
10228072Sbapt// caused due to iteration of unordered containers of pointer elements.
11228072Sbapt//
12228072Sbapt//===----------------------------------------------------------------------===//
13228072Sbapt
14228072Sbapt#include "clang/ASTMatchers/ASTMatchFinder.h"
15228072Sbapt#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
16228072Sbapt#include "clang/StaticAnalyzer/Core/Checker.h"
17228072Sbapt#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
18228072Sbapt
19228072Sbaptusing namespace clang;
20228072Sbaptusing namespace ento;
21228072Sbaptusing namespace ast_matchers;
22228072Sbapt
23228072Sbaptnamespace {
24228072Sbapt
25228072Sbapt// ID of a node at which the diagnostic would be emitted.
26228072Sbaptconstexpr llvm::StringLiteral WarnAtNode = "iter";
27228072Sbapt
28228072Sbaptclass PointerIterationChecker : public Checker<check::ASTCodeBody> {
29228072Sbaptpublic:
30228072Sbapt  void checkASTCodeBody(const Decl *D,
31228072Sbapt                        AnalysisManager &AM,
32228072Sbapt                        BugReporter &BR) const;
33228072Sbapt};
34228072Sbapt
35228072Sbaptstatic void emitDiagnostics(const BoundNodes &Match, const Decl *D,
36228072Sbapt                            BugReporter &BR, AnalysisManager &AM,
37228072Sbapt                            const PointerIterationChecker *Checker) {
38228072Sbapt  auto *ADC = AM.getAnalysisDeclContext(D);
39250874Sjkim
40228072Sbapt  const auto *MarkedStmt = Match.getNodeAs<Stmt>(WarnAtNode);
41228072Sbapt  assert(MarkedStmt);
42228072Sbapt
43228072Sbapt  auto Range = MarkedStmt->getSourceRange();
44228072Sbapt  auto Location = PathDiagnosticLocation::createBegin(MarkedStmt,
45228072Sbapt                                                      BR.getSourceManager(),
46228072Sbapt                                                      ADC);
47228072Sbapt  std::string Diagnostics;
48228072Sbapt  llvm::raw_string_ostream OS(Diagnostics);
49228072Sbapt  OS << "Iteration of pointer-like elements "
50228072Sbapt     << "can result in non-deterministic ordering";
51228072Sbapt
52228072Sbapt  BR.EmitBasicReport(ADC->getDecl(), Checker,
53228072Sbapt                     "Iteration of pointer-like elements", "Non-determinism",
54228072Sbapt                     OS.str(), Location, Range);
55228072Sbapt}
56228072Sbapt
57228072Sbapt// Assumption: Iteration of ordered containers of pointers is deterministic.
58228072Sbapt
59228072Sbapt// TODO: Currently, we only check for std::unordered_set. Other unordered
60228072Sbapt// containers like std::unordered_map also need to be handled.
61228072Sbapt
62228072Sbapt// TODO: Currently, we do not check what the for loop does with the iterated
63228072Sbapt// pointer values. Not all iterations may cause non-determinism. For example,
64228072Sbapt// counting or summing up the elements should not be non-deterministic.
65228072Sbapt
66228072Sbaptauto matchUnorderedIterWithPointers() -> decltype(decl()) {
67228072Sbapt
68228072Sbapt  auto UnorderedContainerM = declRefExpr(to(varDecl(hasType(
69228072Sbapt                               recordDecl(hasName("std::unordered_set")
70228072Sbapt                             )))));
71228072Sbapt
72228072Sbapt  auto PointerTypeM = varDecl(hasType(hasCanonicalType(pointerType())));
73228072Sbapt
74228072Sbapt  auto PointerIterM = stmt(cxxForRangeStmt(
75228072Sbapt                             hasLoopVariable(PointerTypeM),
76228072Sbapt                             hasRangeInit(UnorderedContainerM)
77228072Sbapt                      )).bind(WarnAtNode);
78228072Sbapt
79228072Sbapt  return decl(forEachDescendant(PointerIterM));
80228072Sbapt}
81228072Sbapt
82228072Sbaptvoid PointerIterationChecker::checkASTCodeBody(const Decl *D,
83228072Sbapt                                             AnalysisManager &AM,
84228072Sbapt                                             BugReporter &BR) const {
85228072Sbapt  auto MatcherM = matchUnorderedIterWithPointers();
86228072Sbapt
87228072Sbapt  auto Matches = match(MatcherM, *D, AM.getASTContext());
88228072Sbapt  for (const auto &Match : Matches)
89228072Sbapt    emitDiagnostics(Match, D, BR, AM, this);
90228072Sbapt}
91228072Sbapt
92228072Sbapt} // end of anonymous namespace
93228072Sbapt
94228072Sbaptvoid ento::registerPointerIterationChecker(CheckerManager &Mgr) {
95228072Sbapt  Mgr.registerChecker<PointerIterationChecker>();
96228072Sbapt}
97228072Sbapt
98228072Sbaptbool ento::shouldRegisterPointerIterationChecker(const LangOptions &LO) {
99228072Sbapt  return LO.CPlusPlus;
100228072Sbapt}
101228072Sbapt