PointerSortingChecker.cpp 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. //== PointerSortingChecker.cpp --------------------------------- -*- 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. // This file defines PointerSortingChecker which checks for non-determinism
  10. // caused due to sorting containers with pointer-like elements.
  11. //
  12. //===----------------------------------------------------------------------===//
  13. #include "clang/ASTMatchers/ASTMatchFinder.h"
  14. #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
  15. #include "clang/StaticAnalyzer/Core/Checker.h"
  16. #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
  17. using namespace clang;
  18. using namespace ento;
  19. using namespace ast_matchers;
  20. namespace {
  21. // ID of a node at which the diagnostic would be emitted.
  22. constexpr llvm::StringLiteral WarnAtNode = "sort";
  23. class PointerSortingChecker : public Checker<check::ASTCodeBody> {
  24. public:
  25. void checkASTCodeBody(const Decl *D,
  26. AnalysisManager &AM,
  27. BugReporter &BR) const;
  28. };
  29. static void emitDiagnostics(const BoundNodes &Match, const Decl *D,
  30. BugReporter &BR, AnalysisManager &AM,
  31. const PointerSortingChecker *Checker) {
  32. auto *ADC = AM.getAnalysisDeclContext(D);
  33. const auto *MarkedStmt = Match.getNodeAs<CallExpr>(WarnAtNode);
  34. assert(MarkedStmt);
  35. auto Range = MarkedStmt->getSourceRange();
  36. auto Location = PathDiagnosticLocation::createBegin(MarkedStmt,
  37. BR.getSourceManager(),
  38. ADC);
  39. std::string Diagnostics;
  40. llvm::raw_string_ostream OS(Diagnostics);
  41. OS << "Sorting pointer-like elements "
  42. << "can result in non-deterministic ordering";
  43. BR.EmitBasicReport(ADC->getDecl(), Checker,
  44. "Sorting of pointer-like elements", "Non-determinism",
  45. OS.str(), Location, Range);
  46. }
  47. decltype(auto) callsName(const char *FunctionName) {
  48. return callee(functionDecl(hasName(FunctionName)));
  49. }
  50. // FIXME: Currently we simply check if std::sort is used with pointer-like
  51. // elements. This approach can have a big false positive rate. Using std::sort,
  52. // std::unique and then erase is common technique for deduplicating a container
  53. // (which in some cases might even be quicker than using, let's say std::set).
  54. // In case a container contains arbitrary memory addresses (e.g. multiple
  55. // things give different stuff but might give the same thing multiple times)
  56. // which we don't want to do things with more than once, we might use
  57. // sort-unique-erase and the sort call will emit a report.
  58. auto matchSortWithPointers() -> decltype(decl()) {
  59. // Match any of these function calls.
  60. auto SortFuncM = anyOf(
  61. callsName("std::is_sorted"),
  62. callsName("std::nth_element"),
  63. callsName("std::partial_sort"),
  64. callsName("std::partition"),
  65. callsName("std::sort"),
  66. callsName("std::stable_partition"),
  67. callsName("std::stable_sort")
  68. );
  69. // Match only if the container has pointer-type elements.
  70. auto IteratesPointerEltsM = hasArgument(0,
  71. hasType(cxxRecordDecl(has(
  72. fieldDecl(hasType(hasCanonicalType(
  73. pointsTo(hasCanonicalType(pointerType()))
  74. )))
  75. ))));
  76. auto PointerSortM = traverse(
  77. TK_AsIs,
  78. stmt(callExpr(allOf(SortFuncM, IteratesPointerEltsM))).bind(WarnAtNode));
  79. return decl(forEachDescendant(PointerSortM));
  80. }
  81. void PointerSortingChecker::checkASTCodeBody(const Decl *D,
  82. AnalysisManager &AM,
  83. BugReporter &BR) const {
  84. auto MatcherM = matchSortWithPointers();
  85. auto Matches = match(MatcherM, *D, AM.getASTContext());
  86. for (const auto &Match : Matches)
  87. emitDiagnostics(Match, D, BR, AM, this);
  88. }
  89. } // end of anonymous namespace
  90. void ento::registerPointerSortingChecker(CheckerManager &Mgr) {
  91. Mgr.registerChecker<PointerSortingChecker>();
  92. }
  93. bool ento::shouldRegisterPointerSortingChecker(const CheckerManager &mgr) {
  94. const LangOptions &LO = mgr.getLangOpts();
  95. return LO.CPlusPlus;
  96. }