xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp (revision 5ffd83dbcc34f10e07f6d3e968ae6365869615f4)
10b57cec5SDimitry Andric //===- NumberObjectConversionChecker.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 NumberObjectConversionChecker, which checks for a
100b57cec5SDimitry Andric // particular common mistake when dealing with numbers represented as objects
110b57cec5SDimitry Andric // passed around by pointers. Namely, the language allows to reinterpret the
120b57cec5SDimitry Andric // pointer as a number directly, often without throwing any warnings,
130b57cec5SDimitry Andric // but in most cases the result of such conversion is clearly unexpected,
140b57cec5SDimitry Andric // as pointer value, rather than number value represented by the pointee object,
150b57cec5SDimitry Andric // becomes the result of such operation.
160b57cec5SDimitry Andric //
170b57cec5SDimitry Andric // Currently the checker supports the Objective-C NSNumber class,
180b57cec5SDimitry Andric // and the OSBoolean class found in macOS low-level code; the latter
190b57cec5SDimitry Andric // can only hold boolean values.
200b57cec5SDimitry Andric //
210b57cec5SDimitry Andric // This checker has an option "Pedantic" (boolean), which enables detection of
220b57cec5SDimitry Andric // more conversion patterns (which are most likely more harmless, and therefore
230b57cec5SDimitry Andric // are more likely to produce false positives) - disabled by default,
240b57cec5SDimitry Andric // enabled with `-analyzer-config osx.NumberObjectConversion:Pedantic=true'.
250b57cec5SDimitry Andric //
260b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
270b57cec5SDimitry Andric 
280b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
290b57cec5SDimitry Andric #include "clang/ASTMatchers/ASTMatchFinder.h"
300b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
310b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
320b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
330b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
340b57cec5SDimitry Andric #include "clang/Lex/Lexer.h"
350b57cec5SDimitry Andric #include "llvm/ADT/APSInt.h"
360b57cec5SDimitry Andric 
370b57cec5SDimitry Andric using namespace clang;
380b57cec5SDimitry Andric using namespace ento;
390b57cec5SDimitry Andric using namespace ast_matchers;
400b57cec5SDimitry Andric 
410b57cec5SDimitry Andric namespace {
420b57cec5SDimitry Andric 
430b57cec5SDimitry Andric class NumberObjectConversionChecker : public Checker<check::ASTCodeBody> {
440b57cec5SDimitry Andric public:
450b57cec5SDimitry Andric   bool Pedantic;
460b57cec5SDimitry Andric 
470b57cec5SDimitry Andric   void checkASTCodeBody(const Decl *D, AnalysisManager &AM,
480b57cec5SDimitry Andric                         BugReporter &BR) const;
490b57cec5SDimitry Andric };
500b57cec5SDimitry Andric 
510b57cec5SDimitry Andric class Callback : public MatchFinder::MatchCallback {
520b57cec5SDimitry Andric   const NumberObjectConversionChecker *C;
530b57cec5SDimitry Andric   BugReporter &BR;
540b57cec5SDimitry Andric   AnalysisDeclContext *ADC;
550b57cec5SDimitry Andric 
560b57cec5SDimitry Andric public:
570b57cec5SDimitry Andric   Callback(const NumberObjectConversionChecker *C,
580b57cec5SDimitry Andric            BugReporter &BR, AnalysisDeclContext *ADC)
590b57cec5SDimitry Andric       : C(C), BR(BR), ADC(ADC) {}
60*5ffd83dbSDimitry Andric   void run(const MatchFinder::MatchResult &Result) override;
610b57cec5SDimitry Andric };
620b57cec5SDimitry Andric } // end of anonymous namespace
630b57cec5SDimitry Andric 
640b57cec5SDimitry Andric void Callback::run(const MatchFinder::MatchResult &Result) {
650b57cec5SDimitry Andric   bool IsPedanticMatch =
660b57cec5SDimitry Andric       (Result.Nodes.getNodeAs<Stmt>("pedantic") != nullptr);
670b57cec5SDimitry Andric   if (IsPedanticMatch && !C->Pedantic)
680b57cec5SDimitry Andric     return;
690b57cec5SDimitry Andric 
700b57cec5SDimitry Andric   ASTContext &ACtx = ADC->getASTContext();
710b57cec5SDimitry Andric 
720b57cec5SDimitry Andric   if (const Expr *CheckIfNull =
730b57cec5SDimitry Andric           Result.Nodes.getNodeAs<Expr>("check_if_null")) {
740b57cec5SDimitry Andric     // Unless the macro indicates that the intended type is clearly not
750b57cec5SDimitry Andric     // a pointer type, we should avoid warning on comparing pointers
760b57cec5SDimitry Andric     // to zero literals in non-pedantic mode.
770b57cec5SDimitry Andric     // FIXME: Introduce an AST matcher to implement the macro-related logic?
780b57cec5SDimitry Andric     bool MacroIndicatesWeShouldSkipTheCheck = false;
790b57cec5SDimitry Andric     SourceLocation Loc = CheckIfNull->getBeginLoc();
800b57cec5SDimitry Andric     if (Loc.isMacroID()) {
810b57cec5SDimitry Andric       StringRef MacroName = Lexer::getImmediateMacroName(
820b57cec5SDimitry Andric           Loc, ACtx.getSourceManager(), ACtx.getLangOpts());
830b57cec5SDimitry Andric       if (MacroName == "NULL" || MacroName == "nil")
840b57cec5SDimitry Andric         return;
850b57cec5SDimitry Andric       if (MacroName == "YES" || MacroName == "NO")
860b57cec5SDimitry Andric         MacroIndicatesWeShouldSkipTheCheck = true;
870b57cec5SDimitry Andric     }
880b57cec5SDimitry Andric     if (!MacroIndicatesWeShouldSkipTheCheck) {
890b57cec5SDimitry Andric       Expr::EvalResult EVResult;
900b57cec5SDimitry Andric       if (CheckIfNull->IgnoreParenCasts()->EvaluateAsInt(
910b57cec5SDimitry Andric               EVResult, ACtx, Expr::SE_AllowSideEffects)) {
920b57cec5SDimitry Andric         llvm::APSInt Result = EVResult.Val.getInt();
930b57cec5SDimitry Andric         if (Result == 0) {
940b57cec5SDimitry Andric           if (!C->Pedantic)
950b57cec5SDimitry Andric             return;
960b57cec5SDimitry Andric           IsPedanticMatch = true;
970b57cec5SDimitry Andric         }
980b57cec5SDimitry Andric       }
990b57cec5SDimitry Andric     }
1000b57cec5SDimitry Andric   }
1010b57cec5SDimitry Andric 
1020b57cec5SDimitry Andric   const Stmt *Conv = Result.Nodes.getNodeAs<Stmt>("conv");
1030b57cec5SDimitry Andric   assert(Conv);
1040b57cec5SDimitry Andric 
1050b57cec5SDimitry Andric   const Expr *ConvertedCObject = Result.Nodes.getNodeAs<Expr>("c_object");
1060b57cec5SDimitry Andric   const Expr *ConvertedCppObject = Result.Nodes.getNodeAs<Expr>("cpp_object");
1070b57cec5SDimitry Andric   const Expr *ConvertedObjCObject = Result.Nodes.getNodeAs<Expr>("objc_object");
1080b57cec5SDimitry Andric   bool IsCpp = (ConvertedCppObject != nullptr);
1090b57cec5SDimitry Andric   bool IsObjC = (ConvertedObjCObject != nullptr);
1100b57cec5SDimitry Andric   const Expr *Obj = IsObjC ? ConvertedObjCObject
1110b57cec5SDimitry Andric                   : IsCpp ? ConvertedCppObject
1120b57cec5SDimitry Andric                   : ConvertedCObject;
1130b57cec5SDimitry Andric   assert(Obj);
1140b57cec5SDimitry Andric 
1150b57cec5SDimitry Andric   bool IsComparison =
1160b57cec5SDimitry Andric       (Result.Nodes.getNodeAs<Stmt>("comparison") != nullptr);
1170b57cec5SDimitry Andric 
1180b57cec5SDimitry Andric   bool IsOSNumber =
1190b57cec5SDimitry Andric       (Result.Nodes.getNodeAs<Decl>("osnumber") != nullptr);
1200b57cec5SDimitry Andric 
1210b57cec5SDimitry Andric   bool IsInteger =
1220b57cec5SDimitry Andric       (Result.Nodes.getNodeAs<QualType>("int_type") != nullptr);
1230b57cec5SDimitry Andric   bool IsObjCBool =
1240b57cec5SDimitry Andric       (Result.Nodes.getNodeAs<QualType>("objc_bool_type") != nullptr);
1250b57cec5SDimitry Andric   bool IsCppBool =
1260b57cec5SDimitry Andric       (Result.Nodes.getNodeAs<QualType>("cpp_bool_type") != nullptr);
1270b57cec5SDimitry Andric 
1280b57cec5SDimitry Andric   llvm::SmallString<64> Msg;
1290b57cec5SDimitry Andric   llvm::raw_svector_ostream OS(Msg);
1300b57cec5SDimitry Andric 
1310b57cec5SDimitry Andric   // Remove ObjC ARC qualifiers.
1320b57cec5SDimitry Andric   QualType ObjT = Obj->getType().getUnqualifiedType();
1330b57cec5SDimitry Andric 
1340b57cec5SDimitry Andric   // Remove consts from pointers.
1350b57cec5SDimitry Andric   if (IsCpp) {
1360b57cec5SDimitry Andric     assert(ObjT.getCanonicalType()->isPointerType());
1370b57cec5SDimitry Andric     ObjT = ACtx.getPointerType(
1380b57cec5SDimitry Andric         ObjT->getPointeeType().getCanonicalType().getUnqualifiedType());
1390b57cec5SDimitry Andric   }
1400b57cec5SDimitry Andric 
1410b57cec5SDimitry Andric   if (IsComparison)
1420b57cec5SDimitry Andric     OS << "Comparing ";
1430b57cec5SDimitry Andric   else
1440b57cec5SDimitry Andric     OS << "Converting ";
1450b57cec5SDimitry Andric 
1460b57cec5SDimitry Andric   OS << "a pointer value of type '" << ObjT.getAsString() << "' to a ";
1470b57cec5SDimitry Andric 
1480b57cec5SDimitry Andric   std::string EuphemismForPlain = "primitive";
1490b57cec5SDimitry Andric   std::string SuggestedApi = IsObjC ? (IsInteger ? "" : "-boolValue")
1500b57cec5SDimitry Andric                            : IsCpp ? (IsOSNumber ? "" : "getValue()")
1510b57cec5SDimitry Andric                            : "CFNumberGetValue()";
1520b57cec5SDimitry Andric   if (SuggestedApi.empty()) {
1530b57cec5SDimitry Andric     // A generic message if we're not sure what API should be called.
1540b57cec5SDimitry Andric     // FIXME: Pattern-match the integer type to make a better guess?
1550b57cec5SDimitry Andric     SuggestedApi =
1560b57cec5SDimitry Andric         "a method on '" + ObjT.getAsString() + "' to get the scalar value";
1570b57cec5SDimitry Andric     // "scalar" is not quite correct or common, but some documentation uses it
1580b57cec5SDimitry Andric     // when describing object methods we suggest. For consistency, we use
1590b57cec5SDimitry Andric     // "scalar" in the whole sentence when we need to use this word in at least
1600b57cec5SDimitry Andric     // one place, otherwise we use "primitive".
1610b57cec5SDimitry Andric     EuphemismForPlain = "scalar";
1620b57cec5SDimitry Andric   }
1630b57cec5SDimitry Andric 
1640b57cec5SDimitry Andric   if (IsInteger)
1650b57cec5SDimitry Andric     OS << EuphemismForPlain << " integer value";
1660b57cec5SDimitry Andric   else if (IsObjCBool)
1670b57cec5SDimitry Andric     OS << EuphemismForPlain << " BOOL value";
1680b57cec5SDimitry Andric   else if (IsCppBool)
1690b57cec5SDimitry Andric     OS << EuphemismForPlain << " bool value";
1700b57cec5SDimitry Andric   else // Branch condition?
1710b57cec5SDimitry Andric     OS << EuphemismForPlain << " boolean value";
1720b57cec5SDimitry Andric 
1730b57cec5SDimitry Andric 
1740b57cec5SDimitry Andric   if (IsPedanticMatch)
1750b57cec5SDimitry Andric     OS << "; instead, either compare the pointer to "
1760b57cec5SDimitry Andric        << (IsObjC ? "nil" : IsCpp ? "nullptr" : "NULL") << " or ";
1770b57cec5SDimitry Andric   else
1780b57cec5SDimitry Andric     OS << "; did you mean to ";
1790b57cec5SDimitry Andric 
1800b57cec5SDimitry Andric   if (IsComparison)
1810b57cec5SDimitry Andric     OS << "compare the result of calling " << SuggestedApi;
1820b57cec5SDimitry Andric   else
1830b57cec5SDimitry Andric     OS << "call " << SuggestedApi;
1840b57cec5SDimitry Andric 
1850b57cec5SDimitry Andric   if (!IsPedanticMatch)
1860b57cec5SDimitry Andric     OS << "?";
1870b57cec5SDimitry Andric 
1880b57cec5SDimitry Andric   BR.EmitBasicReport(
1890b57cec5SDimitry Andric       ADC->getDecl(), C, "Suspicious number object conversion", "Logic error",
1900b57cec5SDimitry Andric       OS.str(),
1910b57cec5SDimitry Andric       PathDiagnosticLocation::createBegin(Obj, BR.getSourceManager(), ADC),
1920b57cec5SDimitry Andric       Conv->getSourceRange());
1930b57cec5SDimitry Andric }
1940b57cec5SDimitry Andric 
1950b57cec5SDimitry Andric void NumberObjectConversionChecker::checkASTCodeBody(const Decl *D,
1960b57cec5SDimitry Andric                                                      AnalysisManager &AM,
1970b57cec5SDimitry Andric                                                      BugReporter &BR) const {
1980b57cec5SDimitry Andric   // Currently this matches CoreFoundation opaque pointer typedefs.
1990b57cec5SDimitry Andric   auto CSuspiciousNumberObjectExprM =
2000b57cec5SDimitry Andric       expr(ignoringParenImpCasts(
2010b57cec5SDimitry Andric           expr(hasType(
2020b57cec5SDimitry Andric               typedefType(hasDeclaration(anyOf(
2030b57cec5SDimitry Andric                   typedefDecl(hasName("CFNumberRef")),
2040b57cec5SDimitry Andric                   typedefDecl(hasName("CFBooleanRef")))))))
2050b57cec5SDimitry Andric           .bind("c_object")));
2060b57cec5SDimitry Andric 
2070b57cec5SDimitry Andric   // Currently this matches XNU kernel number-object pointers.
2080b57cec5SDimitry Andric   auto CppSuspiciousNumberObjectExprM =
2090b57cec5SDimitry Andric       expr(ignoringParenImpCasts(
2100b57cec5SDimitry Andric           expr(hasType(hasCanonicalType(
2110b57cec5SDimitry Andric               pointerType(pointee(hasCanonicalType(
2120b57cec5SDimitry Andric                   recordType(hasDeclaration(
2130b57cec5SDimitry Andric                       anyOf(
2140b57cec5SDimitry Andric                         cxxRecordDecl(hasName("OSBoolean")),
2150b57cec5SDimitry Andric                         cxxRecordDecl(hasName("OSNumber"))
2160b57cec5SDimitry Andric                             .bind("osnumber"))))))))))
2170b57cec5SDimitry Andric           .bind("cpp_object")));
2180b57cec5SDimitry Andric 
2190b57cec5SDimitry Andric   // Currently this matches NeXTSTEP number objects.
2200b57cec5SDimitry Andric   auto ObjCSuspiciousNumberObjectExprM =
2210b57cec5SDimitry Andric       expr(ignoringParenImpCasts(
2220b57cec5SDimitry Andric           expr(hasType(hasCanonicalType(
2230b57cec5SDimitry Andric               objcObjectPointerType(pointee(
2240b57cec5SDimitry Andric                   qualType(hasCanonicalType(
2250b57cec5SDimitry Andric                       qualType(hasDeclaration(
2260b57cec5SDimitry Andric                           objcInterfaceDecl(hasName("NSNumber")))))))))))
2270b57cec5SDimitry Andric           .bind("objc_object")));
2280b57cec5SDimitry Andric 
2290b57cec5SDimitry Andric   auto SuspiciousNumberObjectExprM = anyOf(
2300b57cec5SDimitry Andric       CSuspiciousNumberObjectExprM,
2310b57cec5SDimitry Andric       CppSuspiciousNumberObjectExprM,
2320b57cec5SDimitry Andric       ObjCSuspiciousNumberObjectExprM);
2330b57cec5SDimitry Andric 
2340b57cec5SDimitry Andric   // Useful for predicates like "Unless we've seen the same object elsewhere".
2350b57cec5SDimitry Andric   auto AnotherSuspiciousNumberObjectExprM =
2360b57cec5SDimitry Andric       expr(anyOf(
2370b57cec5SDimitry Andric           equalsBoundNode("c_object"),
2380b57cec5SDimitry Andric           equalsBoundNode("objc_object"),
2390b57cec5SDimitry Andric           equalsBoundNode("cpp_object")));
2400b57cec5SDimitry Andric 
2410b57cec5SDimitry Andric   // The .bind here is in order to compose the error message more accurately.
2420b57cec5SDimitry Andric   auto ObjCSuspiciousScalarBooleanTypeM =
2430b57cec5SDimitry Andric       qualType(typedefType(hasDeclaration(
2440b57cec5SDimitry Andric                    typedefDecl(hasName("BOOL"))))).bind("objc_bool_type");
2450b57cec5SDimitry Andric 
2460b57cec5SDimitry Andric   // The .bind here is in order to compose the error message more accurately.
2470b57cec5SDimitry Andric   auto SuspiciousScalarBooleanTypeM =
2480b57cec5SDimitry Andric       qualType(anyOf(qualType(booleanType()).bind("cpp_bool_type"),
2490b57cec5SDimitry Andric                      ObjCSuspiciousScalarBooleanTypeM));
2500b57cec5SDimitry Andric 
2510b57cec5SDimitry Andric   // The .bind here is in order to compose the error message more accurately.
2520b57cec5SDimitry Andric   // Also avoid intptr_t and uintptr_t because they were specifically created
2530b57cec5SDimitry Andric   // for storing pointers.
2540b57cec5SDimitry Andric   auto SuspiciousScalarNumberTypeM =
2550b57cec5SDimitry Andric       qualType(hasCanonicalType(isInteger()),
2560b57cec5SDimitry Andric                unless(typedefType(hasDeclaration(
2570b57cec5SDimitry Andric                    typedefDecl(matchesName("^::u?intptr_t$"))))))
2580b57cec5SDimitry Andric       .bind("int_type");
2590b57cec5SDimitry Andric 
2600b57cec5SDimitry Andric   auto SuspiciousScalarTypeM =
2610b57cec5SDimitry Andric       qualType(anyOf(SuspiciousScalarBooleanTypeM,
2620b57cec5SDimitry Andric                      SuspiciousScalarNumberTypeM));
2630b57cec5SDimitry Andric 
2640b57cec5SDimitry Andric   auto SuspiciousScalarExprM =
2650b57cec5SDimitry Andric       expr(ignoringParenImpCasts(expr(hasType(SuspiciousScalarTypeM))));
2660b57cec5SDimitry Andric 
2670b57cec5SDimitry Andric   auto ConversionThroughAssignmentM =
2680b57cec5SDimitry Andric       binaryOperator(allOf(hasOperatorName("="),
2690b57cec5SDimitry Andric                            hasLHS(SuspiciousScalarExprM),
2700b57cec5SDimitry Andric                            hasRHS(SuspiciousNumberObjectExprM)));
2710b57cec5SDimitry Andric 
2720b57cec5SDimitry Andric   auto ConversionThroughBranchingM =
2730b57cec5SDimitry Andric       ifStmt(allOf(
2740b57cec5SDimitry Andric           hasCondition(SuspiciousNumberObjectExprM),
2750b57cec5SDimitry Andric           unless(hasConditionVariableStatement(declStmt())
2760b57cec5SDimitry Andric       ))).bind("pedantic");
2770b57cec5SDimitry Andric 
2780b57cec5SDimitry Andric   auto ConversionThroughCallM =
2790b57cec5SDimitry Andric       callExpr(hasAnyArgument(allOf(hasType(SuspiciousScalarTypeM),
2800b57cec5SDimitry Andric                                     ignoringParenImpCasts(
2810b57cec5SDimitry Andric                                         SuspiciousNumberObjectExprM))));
2820b57cec5SDimitry Andric 
2830b57cec5SDimitry Andric   // We bind "check_if_null" to modify the warning message
2840b57cec5SDimitry Andric   // in case it was intended to compare a pointer to 0 with a relatively-ok
2850b57cec5SDimitry Andric   // construct "x == 0" or "x != 0".
2860b57cec5SDimitry Andric   auto ConversionThroughEquivalenceM =
2870b57cec5SDimitry Andric       binaryOperator(allOf(anyOf(hasOperatorName("=="), hasOperatorName("!=")),
2880b57cec5SDimitry Andric                            hasEitherOperand(SuspiciousNumberObjectExprM),
2890b57cec5SDimitry Andric                            hasEitherOperand(SuspiciousScalarExprM
2900b57cec5SDimitry Andric                                             .bind("check_if_null"))))
2910b57cec5SDimitry Andric       .bind("comparison");
2920b57cec5SDimitry Andric 
2930b57cec5SDimitry Andric   auto ConversionThroughComparisonM =
2940b57cec5SDimitry Andric       binaryOperator(allOf(anyOf(hasOperatorName(">="), hasOperatorName(">"),
2950b57cec5SDimitry Andric                                  hasOperatorName("<="), hasOperatorName("<")),
2960b57cec5SDimitry Andric                            hasEitherOperand(SuspiciousNumberObjectExprM),
2970b57cec5SDimitry Andric                            hasEitherOperand(SuspiciousScalarExprM)))
2980b57cec5SDimitry Andric       .bind("comparison");
2990b57cec5SDimitry Andric 
3000b57cec5SDimitry Andric   auto ConversionThroughConditionalOperatorM =
3010b57cec5SDimitry Andric       conditionalOperator(allOf(
3020b57cec5SDimitry Andric           hasCondition(SuspiciousNumberObjectExprM),
3030b57cec5SDimitry Andric           unless(hasTrueExpression(
3040b57cec5SDimitry Andric               hasDescendant(AnotherSuspiciousNumberObjectExprM))),
3050b57cec5SDimitry Andric           unless(hasFalseExpression(
3060b57cec5SDimitry Andric               hasDescendant(AnotherSuspiciousNumberObjectExprM)))))
3070b57cec5SDimitry Andric       .bind("pedantic");
3080b57cec5SDimitry Andric 
3090b57cec5SDimitry Andric   auto ConversionThroughExclamationMarkM =
3100b57cec5SDimitry Andric       unaryOperator(allOf(hasOperatorName("!"),
3110b57cec5SDimitry Andric                           has(expr(SuspiciousNumberObjectExprM))))
3120b57cec5SDimitry Andric       .bind("pedantic");
3130b57cec5SDimitry Andric 
3140b57cec5SDimitry Andric   auto ConversionThroughExplicitBooleanCastM =
3150b57cec5SDimitry Andric       explicitCastExpr(allOf(hasType(SuspiciousScalarBooleanTypeM),
3160b57cec5SDimitry Andric                              has(expr(SuspiciousNumberObjectExprM))));
3170b57cec5SDimitry Andric 
3180b57cec5SDimitry Andric   auto ConversionThroughExplicitNumberCastM =
3190b57cec5SDimitry Andric       explicitCastExpr(allOf(hasType(SuspiciousScalarNumberTypeM),
3200b57cec5SDimitry Andric                              has(expr(SuspiciousNumberObjectExprM))));
3210b57cec5SDimitry Andric 
3220b57cec5SDimitry Andric   auto ConversionThroughInitializerM =
3230b57cec5SDimitry Andric       declStmt(hasSingleDecl(
3240b57cec5SDimitry Andric           varDecl(hasType(SuspiciousScalarTypeM),
3250b57cec5SDimitry Andric                   hasInitializer(SuspiciousNumberObjectExprM))));
3260b57cec5SDimitry Andric 
3270b57cec5SDimitry Andric   auto FinalM = stmt(anyOf(ConversionThroughAssignmentM,
3280b57cec5SDimitry Andric                            ConversionThroughBranchingM,
3290b57cec5SDimitry Andric                            ConversionThroughCallM,
3300b57cec5SDimitry Andric                            ConversionThroughComparisonM,
3310b57cec5SDimitry Andric                            ConversionThroughConditionalOperatorM,
3320b57cec5SDimitry Andric                            ConversionThroughEquivalenceM,
3330b57cec5SDimitry Andric                            ConversionThroughExclamationMarkM,
3340b57cec5SDimitry Andric                            ConversionThroughExplicitBooleanCastM,
3350b57cec5SDimitry Andric                            ConversionThroughExplicitNumberCastM,
3360b57cec5SDimitry Andric                            ConversionThroughInitializerM)).bind("conv");
3370b57cec5SDimitry Andric 
3380b57cec5SDimitry Andric   MatchFinder F;
3390b57cec5SDimitry Andric   Callback CB(this, BR, AM.getAnalysisDeclContext(D));
3400b57cec5SDimitry Andric 
341*5ffd83dbSDimitry Andric   F.addMatcher(traverse(TK_AsIs, stmt(forEachDescendant(FinalM))), &CB);
3420b57cec5SDimitry Andric   F.match(*D->getBody(), AM.getASTContext());
3430b57cec5SDimitry Andric }
3440b57cec5SDimitry Andric 
3450b57cec5SDimitry Andric void ento::registerNumberObjectConversionChecker(CheckerManager &Mgr) {
3460b57cec5SDimitry Andric   NumberObjectConversionChecker *Chk =
3470b57cec5SDimitry Andric       Mgr.registerChecker<NumberObjectConversionChecker>();
3480b57cec5SDimitry Andric   Chk->Pedantic =
3490b57cec5SDimitry Andric       Mgr.getAnalyzerOptions().getCheckerBooleanOption(Chk, "Pedantic");
3500b57cec5SDimitry Andric }
3510b57cec5SDimitry Andric 
352*5ffd83dbSDimitry Andric bool ento::shouldRegisterNumberObjectConversionChecker(const CheckerManager &mgr) {
3530b57cec5SDimitry Andric   return true;
3540b57cec5SDimitry Andric }
355