1940c770dSArtem Dergachev //===- NumberObjectConversionChecker.cpp -------------------------*- C++ -*-==//
2940c770dSArtem Dergachev //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6940c770dSArtem Dergachev //
7940c770dSArtem Dergachev //===----------------------------------------------------------------------===//
8940c770dSArtem Dergachev //
9940c770dSArtem Dergachev // This file defines NumberObjectConversionChecker, which checks for a
10940c770dSArtem Dergachev // particular common mistake when dealing with numbers represented as objects
11940c770dSArtem Dergachev // passed around by pointers. Namely, the language allows to reinterpret the
12940c770dSArtem Dergachev // pointer as a number directly, often without throwing any warnings,
13940c770dSArtem Dergachev // but in most cases the result of such conversion is clearly unexpected,
14940c770dSArtem Dergachev // as pointer value, rather than number value represented by the pointee object,
15940c770dSArtem Dergachev // becomes the result of such operation.
16940c770dSArtem Dergachev //
17940c770dSArtem Dergachev // Currently the checker supports the Objective-C NSNumber class,
18940c770dSArtem Dergachev // and the OSBoolean class found in macOS low-level code; the latter
19940c770dSArtem Dergachev // can only hold boolean values.
20940c770dSArtem Dergachev //
21940c770dSArtem Dergachev // This checker has an option "Pedantic" (boolean), which enables detection of
22940c770dSArtem Dergachev // more conversion patterns (which are most likely more harmless, and therefore
23940c770dSArtem Dergachev // are more likely to produce false positives) - disabled by default,
24940c770dSArtem Dergachev // enabled with `-analyzer-config osx.NumberObjectConversion:Pedantic=true'.
25940c770dSArtem Dergachev //
26940c770dSArtem Dergachev //===----------------------------------------------------------------------===//
27940c770dSArtem Dergachev 
2876a21502SKristof Umann #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
29940c770dSArtem Dergachev #include "clang/ASTMatchers/ASTMatchFinder.h"
30940c770dSArtem Dergachev #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
31940c770dSArtem Dergachev #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
32940c770dSArtem Dergachev #include "clang/StaticAnalyzer/Core/Checker.h"
33940c770dSArtem Dergachev #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
34940c770dSArtem Dergachev #include "clang/Lex/Lexer.h"
35940c770dSArtem Dergachev #include "llvm/ADT/APSInt.h"
36940c770dSArtem Dergachev 
37940c770dSArtem Dergachev using namespace clang;
38940c770dSArtem Dergachev using namespace ento;
39940c770dSArtem Dergachev using namespace ast_matchers;
40940c770dSArtem Dergachev 
41940c770dSArtem Dergachev namespace {
42940c770dSArtem Dergachev 
43940c770dSArtem Dergachev class NumberObjectConversionChecker : public Checker<check::ASTCodeBody> {
44940c770dSArtem Dergachev public:
45940c770dSArtem Dergachev   bool Pedantic;
46940c770dSArtem Dergachev 
47940c770dSArtem Dergachev   void checkASTCodeBody(const Decl *D, AnalysisManager &AM,
48940c770dSArtem Dergachev                         BugReporter &BR) const;
49940c770dSArtem Dergachev };
50940c770dSArtem Dergachev 
51940c770dSArtem Dergachev class Callback : public MatchFinder::MatchCallback {
52940c770dSArtem Dergachev   const NumberObjectConversionChecker *C;
53940c770dSArtem Dergachev   BugReporter &BR;
54940c770dSArtem Dergachev   AnalysisDeclContext *ADC;
55940c770dSArtem Dergachev 
56940c770dSArtem Dergachev public:
Callback(const NumberObjectConversionChecker * C,BugReporter & BR,AnalysisDeclContext * ADC)57940c770dSArtem Dergachev   Callback(const NumberObjectConversionChecker *C,
58940c770dSArtem Dergachev            BugReporter &BR, AnalysisDeclContext *ADC)
59940c770dSArtem Dergachev       : C(C), BR(BR), ADC(ADC) {}
602c2a297bSLogan Smith   void run(const MatchFinder::MatchResult &Result) override;
61940c770dSArtem Dergachev };
62940c770dSArtem Dergachev } // end of anonymous namespace
63940c770dSArtem Dergachev 
run(const MatchFinder::MatchResult & Result)64940c770dSArtem Dergachev void Callback::run(const MatchFinder::MatchResult &Result) {
65e14d8818SArtem Dergachev   bool IsPedanticMatch =
66e14d8818SArtem Dergachev       (Result.Nodes.getNodeAs<Stmt>("pedantic") != nullptr);
67940c770dSArtem Dergachev   if (IsPedanticMatch && !C->Pedantic)
68940c770dSArtem Dergachev     return;
69940c770dSArtem Dergachev 
70940c770dSArtem Dergachev   ASTContext &ACtx = ADC->getASTContext();
71940c770dSArtem Dergachev 
72940c770dSArtem Dergachev   if (const Expr *CheckIfNull =
73940c770dSArtem Dergachev           Result.Nodes.getNodeAs<Expr>("check_if_null")) {
74e14d8818SArtem Dergachev     // Unless the macro indicates that the intended type is clearly not
75e14d8818SArtem Dergachev     // a pointer type, we should avoid warning on comparing pointers
76e14d8818SArtem Dergachev     // to zero literals in non-pedantic mode.
77e14d8818SArtem Dergachev     // FIXME: Introduce an AST matcher to implement the macro-related logic?
78e14d8818SArtem Dergachev     bool MacroIndicatesWeShouldSkipTheCheck = false;
79f2ceec48SStephen Kelly     SourceLocation Loc = CheckIfNull->getBeginLoc();
80940c770dSArtem Dergachev     if (Loc.isMacroID()) {
81940c770dSArtem Dergachev       StringRef MacroName = Lexer::getImmediateMacroName(
82940c770dSArtem Dergachev           Loc, ACtx.getSourceManager(), ACtx.getLangOpts());
83e14d8818SArtem Dergachev       if (MacroName == "NULL" || MacroName == "nil")
84940c770dSArtem Dergachev         return;
85e14d8818SArtem Dergachev       if (MacroName == "YES" || MacroName == "NO")
86e14d8818SArtem Dergachev         MacroIndicatesWeShouldSkipTheCheck = true;
87e14d8818SArtem Dergachev     }
88e14d8818SArtem Dergachev     if (!MacroIndicatesWeShouldSkipTheCheck) {
89407659abSFangrui Song       Expr::EvalResult EVResult;
90940c770dSArtem Dergachev       if (CheckIfNull->IgnoreParenCasts()->EvaluateAsInt(
91407659abSFangrui Song               EVResult, ACtx, Expr::SE_AllowSideEffects)) {
92407659abSFangrui Song         llvm::APSInt Result = EVResult.Val.getInt();
93940c770dSArtem Dergachev         if (Result == 0) {
94940c770dSArtem Dergachev           if (!C->Pedantic)
95940c770dSArtem Dergachev             return;
96940c770dSArtem Dergachev           IsPedanticMatch = true;
97940c770dSArtem Dergachev         }
98940c770dSArtem Dergachev       }
99940c770dSArtem Dergachev     }
100940c770dSArtem Dergachev   }
101940c770dSArtem Dergachev 
102e14d8818SArtem Dergachev   const Stmt *Conv = Result.Nodes.getNodeAs<Stmt>("conv");
103e14d8818SArtem Dergachev   assert(Conv);
104e14d8818SArtem Dergachev 
105e14d8818SArtem Dergachev   const Expr *ConvertedCObject = Result.Nodes.getNodeAs<Expr>("c_object");
106e14d8818SArtem Dergachev   const Expr *ConvertedCppObject = Result.Nodes.getNodeAs<Expr>("cpp_object");
107e14d8818SArtem Dergachev   const Expr *ConvertedObjCObject = Result.Nodes.getNodeAs<Expr>("objc_object");
108e14d8818SArtem Dergachev   bool IsCpp = (ConvertedCppObject != nullptr);
109e14d8818SArtem Dergachev   bool IsObjC = (ConvertedObjCObject != nullptr);
110e14d8818SArtem Dergachev   const Expr *Obj = IsObjC ? ConvertedObjCObject
111e14d8818SArtem Dergachev                   : IsCpp ? ConvertedCppObject
112e14d8818SArtem Dergachev                   : ConvertedCObject;
113e14d8818SArtem Dergachev   assert(Obj);
114e14d8818SArtem Dergachev 
115e14d8818SArtem Dergachev   bool IsComparison =
116e14d8818SArtem Dergachev       (Result.Nodes.getNodeAs<Stmt>("comparison") != nullptr);
117e14d8818SArtem Dergachev 
118e14d8818SArtem Dergachev   bool IsOSNumber =
119e14d8818SArtem Dergachev       (Result.Nodes.getNodeAs<Decl>("osnumber") != nullptr);
120e14d8818SArtem Dergachev 
121e14d8818SArtem Dergachev   bool IsInteger =
122e14d8818SArtem Dergachev       (Result.Nodes.getNodeAs<QualType>("int_type") != nullptr);
123e14d8818SArtem Dergachev   bool IsObjCBool =
124e14d8818SArtem Dergachev       (Result.Nodes.getNodeAs<QualType>("objc_bool_type") != nullptr);
125e14d8818SArtem Dergachev   bool IsCppBool =
126e14d8818SArtem Dergachev       (Result.Nodes.getNodeAs<QualType>("cpp_bool_type") != nullptr);
127e14d8818SArtem Dergachev 
128940c770dSArtem Dergachev   llvm::SmallString<64> Msg;
129940c770dSArtem Dergachev   llvm::raw_svector_ostream OS(Msg);
130940c770dSArtem Dergachev 
131e14d8818SArtem Dergachev   // Remove ObjC ARC qualifiers.
132e14d8818SArtem Dergachev   QualType ObjT = Obj->getType().getUnqualifiedType();
133e14d8818SArtem Dergachev 
134e14d8818SArtem Dergachev   // Remove consts from pointers.
135e14d8818SArtem Dergachev   if (IsCpp) {
136e14d8818SArtem Dergachev     assert(ObjT.getCanonicalType()->isPointerType());
137e14d8818SArtem Dergachev     ObjT = ACtx.getPointerType(
138e14d8818SArtem Dergachev         ObjT->getPointeeType().getCanonicalType().getUnqualifiedType());
139e14d8818SArtem Dergachev   }
140e14d8818SArtem Dergachev 
141e14d8818SArtem Dergachev   if (IsComparison)
142e14d8818SArtem Dergachev     OS << "Comparing ";
143940c770dSArtem Dergachev   else
144e14d8818SArtem Dergachev     OS << "Converting ";
145940c770dSArtem Dergachev 
146cfb81690SNathan James   OS << "a pointer value of type '" << ObjT << "' to a ";
147e14d8818SArtem Dergachev 
148e14d8818SArtem Dergachev   std::string EuphemismForPlain = "primitive";
149e14d8818SArtem Dergachev   std::string SuggestedApi = IsObjC ? (IsInteger ? "" : "-boolValue")
150e14d8818SArtem Dergachev                            : IsCpp ? (IsOSNumber ? "" : "getValue()")
151e14d8818SArtem Dergachev                            : "CFNumberGetValue()";
152e14d8818SArtem Dergachev   if (SuggestedApi.empty()) {
153e14d8818SArtem Dergachev     // A generic message if we're not sure what API should be called.
154e14d8818SArtem Dergachev     // FIXME: Pattern-match the integer type to make a better guess?
155e14d8818SArtem Dergachev     SuggestedApi =
156e14d8818SArtem Dergachev         "a method on '" + ObjT.getAsString() + "' to get the scalar value";
157e14d8818SArtem Dergachev     // "scalar" is not quite correct or common, but some documentation uses it
158e14d8818SArtem Dergachev     // when describing object methods we suggest. For consistency, we use
159e14d8818SArtem Dergachev     // "scalar" in the whole sentence when we need to use this word in at least
160e14d8818SArtem Dergachev     // one place, otherwise we use "primitive".
161e14d8818SArtem Dergachev     EuphemismForPlain = "scalar";
162940c770dSArtem Dergachev   }
163e14d8818SArtem Dergachev 
164e14d8818SArtem Dergachev   if (IsInteger)
165e14d8818SArtem Dergachev     OS << EuphemismForPlain << " integer value";
166e14d8818SArtem Dergachev   else if (IsObjCBool)
167e14d8818SArtem Dergachev     OS << EuphemismForPlain << " BOOL value";
168e14d8818SArtem Dergachev   else if (IsCppBool)
169e14d8818SArtem Dergachev     OS << EuphemismForPlain << " bool value";
170e14d8818SArtem Dergachev   else // Branch condition?
171e14d8818SArtem Dergachev     OS << EuphemismForPlain << " boolean value";
172e14d8818SArtem Dergachev 
173e14d8818SArtem Dergachev 
174e14d8818SArtem Dergachev   if (IsPedanticMatch)
175e14d8818SArtem Dergachev     OS << "; instead, either compare the pointer to "
176e14d8818SArtem Dergachev        << (IsObjC ? "nil" : IsCpp ? "nullptr" : "NULL") << " or ";
177e14d8818SArtem Dergachev   else
178e14d8818SArtem Dergachev     OS << "; did you mean to ";
179e14d8818SArtem Dergachev 
180e14d8818SArtem Dergachev   if (IsComparison)
181e14d8818SArtem Dergachev     OS << "compare the result of calling " << SuggestedApi;
182e14d8818SArtem Dergachev   else
183e14d8818SArtem Dergachev     OS << "call " << SuggestedApi;
184e14d8818SArtem Dergachev 
185e14d8818SArtem Dergachev   if (!IsPedanticMatch)
186e14d8818SArtem Dergachev     OS << "?";
187940c770dSArtem Dergachev 
188940c770dSArtem Dergachev   BR.EmitBasicReport(
189940c770dSArtem Dergachev       ADC->getDecl(), C, "Suspicious number object conversion", "Logic error",
190940c770dSArtem Dergachev       OS.str(),
191940c770dSArtem Dergachev       PathDiagnosticLocation::createBegin(Obj, BR.getSourceManager(), ADC),
192940c770dSArtem Dergachev       Conv->getSourceRange());
193940c770dSArtem Dergachev }
194940c770dSArtem Dergachev 
checkASTCodeBody(const Decl * D,AnalysisManager & AM,BugReporter & BR) const195940c770dSArtem Dergachev void NumberObjectConversionChecker::checkASTCodeBody(const Decl *D,
196940c770dSArtem Dergachev                                                      AnalysisManager &AM,
197940c770dSArtem Dergachev                                                      BugReporter &BR) const {
198e14d8818SArtem Dergachev   // Currently this matches CoreFoundation opaque pointer typedefs.
199*888673b6SJonas Devlieghere   auto CSuspiciousNumberObjectExprM =
200*888673b6SJonas Devlieghere       expr(ignoringParenImpCasts(
201*888673b6SJonas Devlieghere           expr(hasType(
202*888673b6SJonas Devlieghere               typedefType(hasDeclaration(anyOf(
203*888673b6SJonas Devlieghere                   typedefDecl(hasName("CFNumberRef")),
204*888673b6SJonas Devlieghere                   typedefDecl(hasName("CFBooleanRef")))))))
205e14d8818SArtem Dergachev           .bind("c_object")));
206940c770dSArtem Dergachev 
207e14d8818SArtem Dergachev   // Currently this matches XNU kernel number-object pointers.
208e14d8818SArtem Dergachev   auto CppSuspiciousNumberObjectExprM =
209940c770dSArtem Dergachev       expr(ignoringParenImpCasts(
210940c770dSArtem Dergachev           expr(hasType(hasCanonicalType(
211940c770dSArtem Dergachev               pointerType(pointee(hasCanonicalType(
212940c770dSArtem Dergachev                   recordType(hasDeclaration(
213e14d8818SArtem Dergachev                       anyOf(
214e14d8818SArtem Dergachev                         cxxRecordDecl(hasName("OSBoolean")),
215e14d8818SArtem Dergachev                         cxxRecordDecl(hasName("OSNumber"))
216e14d8818SArtem Dergachev                             .bind("osnumber"))))))))))
217e14d8818SArtem Dergachev           .bind("cpp_object")));
218940c770dSArtem Dergachev 
219e14d8818SArtem Dergachev   // Currently this matches NeXTSTEP number objects.
220e14d8818SArtem Dergachev   auto ObjCSuspiciousNumberObjectExprM =
221e14d8818SArtem Dergachev       expr(ignoringParenImpCasts(
222e14d8818SArtem Dergachev           expr(hasType(hasCanonicalType(
223940c770dSArtem Dergachev               objcObjectPointerType(pointee(
224940c770dSArtem Dergachev                   qualType(hasCanonicalType(
225940c770dSArtem Dergachev                       qualType(hasDeclaration(
226e14d8818SArtem Dergachev                           objcInterfaceDecl(hasName("NSNumber")))))))))))
227e14d8818SArtem Dergachev           .bind("objc_object")));
228940c770dSArtem Dergachev 
229e14d8818SArtem Dergachev   auto SuspiciousNumberObjectExprM = anyOf(
230e14d8818SArtem Dergachev       CSuspiciousNumberObjectExprM,
231e14d8818SArtem Dergachev       CppSuspiciousNumberObjectExprM,
232e14d8818SArtem Dergachev       ObjCSuspiciousNumberObjectExprM);
233940c770dSArtem Dergachev 
234e14d8818SArtem Dergachev   // Useful for predicates like "Unless we've seen the same object elsewhere".
235e14d8818SArtem Dergachev   auto AnotherSuspiciousNumberObjectExprM =
236e14d8818SArtem Dergachev       expr(anyOf(
237e14d8818SArtem Dergachev           equalsBoundNode("c_object"),
238e14d8818SArtem Dergachev           equalsBoundNode("objc_object"),
239e14d8818SArtem Dergachev           equalsBoundNode("cpp_object")));
240940c770dSArtem Dergachev 
241940c770dSArtem Dergachev   // The .bind here is in order to compose the error message more accurately.
242e14d8818SArtem Dergachev   auto ObjCSuspiciousScalarBooleanTypeM =
243*888673b6SJonas Devlieghere       qualType(typedefType(hasDeclaration(
244*888673b6SJonas Devlieghere                    typedefDecl(hasName("BOOL"))))).bind("objc_bool_type");
245940c770dSArtem Dergachev 
246940c770dSArtem Dergachev   // The .bind here is in order to compose the error message more accurately.
247e14d8818SArtem Dergachev   auto SuspiciousScalarBooleanTypeM =
248940c770dSArtem Dergachev       qualType(anyOf(qualType(booleanType()).bind("cpp_bool_type"),
249e14d8818SArtem Dergachev                      ObjCSuspiciousScalarBooleanTypeM));
250940c770dSArtem Dergachev 
251940c770dSArtem Dergachev   // The .bind here is in order to compose the error message more accurately.
252e14d8818SArtem Dergachev   // Also avoid intptr_t and uintptr_t because they were specifically created
253e14d8818SArtem Dergachev   // for storing pointers.
254e14d8818SArtem Dergachev   auto SuspiciousScalarNumberTypeM =
255940c770dSArtem Dergachev       qualType(hasCanonicalType(isInteger()),
256*888673b6SJonas Devlieghere                unless(typedefType(hasDeclaration(
257*888673b6SJonas Devlieghere                    typedefDecl(matchesName("^::u?intptr_t$"))))))
258940c770dSArtem Dergachev       .bind("int_type");
259940c770dSArtem Dergachev 
260e14d8818SArtem Dergachev   auto SuspiciousScalarTypeM =
261e14d8818SArtem Dergachev       qualType(anyOf(SuspiciousScalarBooleanTypeM,
262e14d8818SArtem Dergachev                      SuspiciousScalarNumberTypeM));
263940c770dSArtem Dergachev 
264e14d8818SArtem Dergachev   auto SuspiciousScalarExprM =
265e14d8818SArtem Dergachev       expr(ignoringParenImpCasts(expr(hasType(SuspiciousScalarTypeM))));
266940c770dSArtem Dergachev 
267940c770dSArtem Dergachev   auto ConversionThroughAssignmentM =
26893fd165bSArtem Dergachev       binaryOperator(allOf(hasOperatorName("="),
269e14d8818SArtem Dergachev                            hasLHS(SuspiciousScalarExprM),
27093fd165bSArtem Dergachev                            hasRHS(SuspiciousNumberObjectExprM)));
271940c770dSArtem Dergachev 
272940c770dSArtem Dergachev   auto ConversionThroughBranchingM =
2738dad0e6cSGeorge Karpenkov       ifStmt(allOf(
2748dad0e6cSGeorge Karpenkov           hasCondition(SuspiciousNumberObjectExprM),
2758dad0e6cSGeorge Karpenkov           unless(hasConditionVariableStatement(declStmt())
2768dad0e6cSGeorge Karpenkov       ))).bind("pedantic");
277940c770dSArtem Dergachev 
278940c770dSArtem Dergachev   auto ConversionThroughCallM =
279e14d8818SArtem Dergachev       callExpr(hasAnyArgument(allOf(hasType(SuspiciousScalarTypeM),
280e14d8818SArtem Dergachev                                     ignoringParenImpCasts(
281e14d8818SArtem Dergachev                                         SuspiciousNumberObjectExprM))));
282940c770dSArtem Dergachev 
283940c770dSArtem Dergachev   // We bind "check_if_null" to modify the warning message
284940c770dSArtem Dergachev   // in case it was intended to compare a pointer to 0 with a relatively-ok
285940c770dSArtem Dergachev   // construct "x == 0" or "x != 0".
286940c770dSArtem Dergachev   auto ConversionThroughEquivalenceM =
28793fd165bSArtem Dergachev       binaryOperator(allOf(anyOf(hasOperatorName("=="), hasOperatorName("!=")),
288e14d8818SArtem Dergachev                            hasEitherOperand(SuspiciousNumberObjectExprM),
289e14d8818SArtem Dergachev                            hasEitherOperand(SuspiciousScalarExprM
29093fd165bSArtem Dergachev                                             .bind("check_if_null"))))
291e14d8818SArtem Dergachev       .bind("comparison");
292940c770dSArtem Dergachev 
293940c770dSArtem Dergachev   auto ConversionThroughComparisonM =
29493fd165bSArtem Dergachev       binaryOperator(allOf(anyOf(hasOperatorName(">="), hasOperatorName(">"),
295940c770dSArtem Dergachev                                  hasOperatorName("<="), hasOperatorName("<")),
296e14d8818SArtem Dergachev                            hasEitherOperand(SuspiciousNumberObjectExprM),
29793fd165bSArtem Dergachev                            hasEitherOperand(SuspiciousScalarExprM)))
298e14d8818SArtem Dergachev       .bind("comparison");
299940c770dSArtem Dergachev 
300940c770dSArtem Dergachev   auto ConversionThroughConditionalOperatorM =
30193fd165bSArtem Dergachev       conditionalOperator(allOf(
302e14d8818SArtem Dergachev           hasCondition(SuspiciousNumberObjectExprM),
303e14d8818SArtem Dergachev           unless(hasTrueExpression(
304e14d8818SArtem Dergachev               hasDescendant(AnotherSuspiciousNumberObjectExprM))),
305e14d8818SArtem Dergachev           unless(hasFalseExpression(
30693fd165bSArtem Dergachev               hasDescendant(AnotherSuspiciousNumberObjectExprM)))))
307940c770dSArtem Dergachev       .bind("pedantic");
308940c770dSArtem Dergachev 
309940c770dSArtem Dergachev   auto ConversionThroughExclamationMarkM =
31093fd165bSArtem Dergachev       unaryOperator(allOf(hasOperatorName("!"),
31193fd165bSArtem Dergachev                           has(expr(SuspiciousNumberObjectExprM))))
312940c770dSArtem Dergachev       .bind("pedantic");
313940c770dSArtem Dergachev 
314940c770dSArtem Dergachev   auto ConversionThroughExplicitBooleanCastM =
31593fd165bSArtem Dergachev       explicitCastExpr(allOf(hasType(SuspiciousScalarBooleanTypeM),
31693fd165bSArtem Dergachev                              has(expr(SuspiciousNumberObjectExprM))));
317940c770dSArtem Dergachev 
318940c770dSArtem Dergachev   auto ConversionThroughExplicitNumberCastM =
31993fd165bSArtem Dergachev       explicitCastExpr(allOf(hasType(SuspiciousScalarNumberTypeM),
32093fd165bSArtem Dergachev                              has(expr(SuspiciousNumberObjectExprM))));
321940c770dSArtem Dergachev 
322940c770dSArtem Dergachev   auto ConversionThroughInitializerM =
323940c770dSArtem Dergachev       declStmt(hasSingleDecl(
324e14d8818SArtem Dergachev           varDecl(hasType(SuspiciousScalarTypeM),
325e14d8818SArtem Dergachev                   hasInitializer(SuspiciousNumberObjectExprM))));
326940c770dSArtem Dergachev 
327940c770dSArtem Dergachev   auto FinalM = stmt(anyOf(ConversionThroughAssignmentM,
328940c770dSArtem Dergachev                            ConversionThroughBranchingM,
329940c770dSArtem Dergachev                            ConversionThroughCallM,
330940c770dSArtem Dergachev                            ConversionThroughComparisonM,
331940c770dSArtem Dergachev                            ConversionThroughConditionalOperatorM,
332940c770dSArtem Dergachev                            ConversionThroughEquivalenceM,
333940c770dSArtem Dergachev                            ConversionThroughExclamationMarkM,
334940c770dSArtem Dergachev                            ConversionThroughExplicitBooleanCastM,
335940c770dSArtem Dergachev                            ConversionThroughExplicitNumberCastM,
336940c770dSArtem Dergachev                            ConversionThroughInitializerM)).bind("conv");
337940c770dSArtem Dergachev 
338e14d8818SArtem Dergachev   MatchFinder F;
339e14d8818SArtem Dergachev   Callback CB(this, BR, AM.getAnalysisDeclContext(D));
340e14d8818SArtem Dergachev 
3418d62eba1SStephen Kelly   F.addMatcher(traverse(TK_AsIs, stmt(forEachDescendant(FinalM))), &CB);
342940c770dSArtem Dergachev   F.match(*D->getBody(), AM.getASTContext());
343940c770dSArtem Dergachev }
344940c770dSArtem Dergachev 
registerNumberObjectConversionChecker(CheckerManager & Mgr)345940c770dSArtem Dergachev void ento::registerNumberObjectConversionChecker(CheckerManager &Mgr) {
346940c770dSArtem Dergachev   NumberObjectConversionChecker *Chk =
347940c770dSArtem Dergachev       Mgr.registerChecker<NumberObjectConversionChecker>();
348940c770dSArtem Dergachev   Chk->Pedantic =
34983cc1b35SKristof Umann       Mgr.getAnalyzerOptions().getCheckerBooleanOption(Chk, "Pedantic");
350940c770dSArtem Dergachev }
351058a7a45SKristof Umann 
shouldRegisterNumberObjectConversionChecker(const CheckerManager & mgr)352bda3dd0dSKirstóf Umann bool ento::shouldRegisterNumberObjectConversionChecker(const CheckerManager &mgr) {
353058a7a45SKristof Umann   return true;
354058a7a45SKristof Umann }
355