xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerIterationChecker.cpp (revision 5ffd83dbcc34f10e07f6d3e968ae6365869615f4)
10b57cec5SDimitry Andric //== PointerIterationChecker.cpp ------------------------------- -*- C++ -*--=//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file defines PointerIterationChecker which checks for non-determinism
100b57cec5SDimitry Andric // caused due to iteration of unordered containers of pointer elements.
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric 
140b57cec5SDimitry Andric #include "clang/ASTMatchers/ASTMatchFinder.h"
150b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
160b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
170b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
180b57cec5SDimitry Andric 
190b57cec5SDimitry Andric using namespace clang;
200b57cec5SDimitry Andric using namespace ento;
210b57cec5SDimitry Andric using namespace ast_matchers;
220b57cec5SDimitry Andric 
230b57cec5SDimitry Andric namespace {
240b57cec5SDimitry Andric 
250b57cec5SDimitry Andric // ID of a node at which the diagnostic would be emitted.
260b57cec5SDimitry Andric constexpr llvm::StringLiteral WarnAtNode = "iter";
270b57cec5SDimitry Andric 
280b57cec5SDimitry Andric class PointerIterationChecker : public Checker<check::ASTCodeBody> {
290b57cec5SDimitry Andric public:
300b57cec5SDimitry Andric   void checkASTCodeBody(const Decl *D,
310b57cec5SDimitry Andric                         AnalysisManager &AM,
320b57cec5SDimitry Andric                         BugReporter &BR) const;
330b57cec5SDimitry Andric };
340b57cec5SDimitry Andric 
350b57cec5SDimitry Andric static void emitDiagnostics(const BoundNodes &Match, const Decl *D,
360b57cec5SDimitry Andric                             BugReporter &BR, AnalysisManager &AM,
370b57cec5SDimitry Andric                             const PointerIterationChecker *Checker) {
380b57cec5SDimitry Andric   auto *ADC = AM.getAnalysisDeclContext(D);
390b57cec5SDimitry Andric 
400b57cec5SDimitry Andric   const auto *MarkedStmt = Match.getNodeAs<Stmt>(WarnAtNode);
410b57cec5SDimitry Andric   assert(MarkedStmt);
420b57cec5SDimitry Andric 
430b57cec5SDimitry Andric   auto Range = MarkedStmt->getSourceRange();
440b57cec5SDimitry Andric   auto Location = PathDiagnosticLocation::createBegin(MarkedStmt,
450b57cec5SDimitry Andric                                                       BR.getSourceManager(),
460b57cec5SDimitry Andric                                                       ADC);
470b57cec5SDimitry Andric   std::string Diagnostics;
480b57cec5SDimitry Andric   llvm::raw_string_ostream OS(Diagnostics);
490b57cec5SDimitry Andric   OS << "Iteration of pointer-like elements "
500b57cec5SDimitry Andric      << "can result in non-deterministic ordering";
510b57cec5SDimitry Andric 
520b57cec5SDimitry Andric   BR.EmitBasicReport(ADC->getDecl(), Checker,
530b57cec5SDimitry Andric                      "Iteration of pointer-like elements", "Non-determinism",
540b57cec5SDimitry Andric                      OS.str(), Location, Range);
550b57cec5SDimitry Andric }
560b57cec5SDimitry Andric 
570b57cec5SDimitry Andric // Assumption: Iteration of ordered containers of pointers is deterministic.
580b57cec5SDimitry Andric 
590b57cec5SDimitry Andric // TODO: Currently, we only check for std::unordered_set. Other unordered
600b57cec5SDimitry Andric // containers like std::unordered_map also need to be handled.
610b57cec5SDimitry Andric 
620b57cec5SDimitry Andric // TODO: Currently, we do not check what the for loop does with the iterated
630b57cec5SDimitry Andric // pointer values. Not all iterations may cause non-determinism. For example,
640b57cec5SDimitry Andric // counting or summing up the elements should not be non-deterministic.
650b57cec5SDimitry Andric 
660b57cec5SDimitry Andric auto matchUnorderedIterWithPointers() -> decltype(decl()) {
670b57cec5SDimitry Andric 
680b57cec5SDimitry Andric   auto UnorderedContainerM = declRefExpr(to(varDecl(hasType(
690b57cec5SDimitry Andric                                recordDecl(hasName("std::unordered_set")
700b57cec5SDimitry Andric                              )))));
710b57cec5SDimitry Andric 
720b57cec5SDimitry Andric   auto PointerTypeM = varDecl(hasType(hasCanonicalType(pointerType())));
730b57cec5SDimitry Andric 
740b57cec5SDimitry Andric   auto PointerIterM = stmt(cxxForRangeStmt(
750b57cec5SDimitry Andric                              hasLoopVariable(PointerTypeM),
760b57cec5SDimitry Andric                              hasRangeInit(UnorderedContainerM)
770b57cec5SDimitry Andric                       )).bind(WarnAtNode);
780b57cec5SDimitry Andric 
790b57cec5SDimitry Andric   return decl(forEachDescendant(PointerIterM));
800b57cec5SDimitry Andric }
810b57cec5SDimitry Andric 
820b57cec5SDimitry Andric void PointerIterationChecker::checkASTCodeBody(const Decl *D,
830b57cec5SDimitry Andric                                              AnalysisManager &AM,
840b57cec5SDimitry Andric                                              BugReporter &BR) const {
850b57cec5SDimitry Andric   auto MatcherM = matchUnorderedIterWithPointers();
860b57cec5SDimitry Andric 
870b57cec5SDimitry Andric   auto Matches = match(MatcherM, *D, AM.getASTContext());
880b57cec5SDimitry Andric   for (const auto &Match : Matches)
890b57cec5SDimitry Andric     emitDiagnostics(Match, D, BR, AM, this);
900b57cec5SDimitry Andric }
910b57cec5SDimitry Andric 
920b57cec5SDimitry Andric } // end of anonymous namespace
930b57cec5SDimitry Andric 
940b57cec5SDimitry Andric void ento::registerPointerIterationChecker(CheckerManager &Mgr) {
950b57cec5SDimitry Andric   Mgr.registerChecker<PointerIterationChecker>();
960b57cec5SDimitry Andric }
970b57cec5SDimitry Andric 
98*5ffd83dbSDimitry Andric bool ento::shouldRegisterPointerIterationChecker(const CheckerManager &mgr) {
99*5ffd83dbSDimitry Andric   const LangOptions &LO = mgr.getLangOpts();
1000b57cec5SDimitry Andric   return LO.CPlusPlus;
1010b57cec5SDimitry Andric }
102