35#include "llvm/ADT/APSInt.h"
43class NumberObjectConversionChecker :
public Checker<check::ASTCodeBody> {
47 void checkASTCodeBody(
const Decl *D, AnalysisManager &AM,
48 BugReporter &BR)
const;
52 const NumberObjectConversionChecker *C;
54 AnalysisDeclContext *ADC;
57 Callback(
const NumberObjectConversionChecker *C,
58 BugReporter &BR, AnalysisDeclContext *ADC)
59 : C(C), BR(BR), ADC(ADC) {}
60 void run(
const MatchFinder::MatchResult &
Result)
override;
65 bool IsPedanticMatch =
66 (
Result.Nodes.getNodeAs<Stmt>(
"pedantic") !=
nullptr);
67 if (IsPedanticMatch && !
C->Pedantic)
72 if (
const Expr *CheckIfNull =
73 Result.Nodes.getNodeAs<Expr>(
"check_if_null")) {
78 bool MacroIndicatesWeShouldSkipTheCheck =
false;
79 SourceLocation Loc = CheckIfNull->getBeginLoc();
83 if (MacroName ==
"NULL" || MacroName ==
"nil")
85 if (MacroName ==
"YES" || MacroName ==
"NO")
86 MacroIndicatesWeShouldSkipTheCheck =
true;
88 if (!MacroIndicatesWeShouldSkipTheCheck) {
89 Expr::EvalResult EVResult;
90 if (CheckIfNull->IgnoreParenCasts()->EvaluateAsInt(
96 IsPedanticMatch =
true;
102 const Stmt *Conv =
Result.Nodes.getNodeAs<Stmt>(
"conv");
105 const Expr *ConvertedCObject =
Result.Nodes.getNodeAs<Expr>(
"c_object");
106 const Expr *ConvertedCppObject =
Result.Nodes.getNodeAs<Expr>(
"cpp_object");
107 const Expr *ConvertedObjCObject =
Result.Nodes.getNodeAs<Expr>(
"objc_object");
108 bool IsCpp = (ConvertedCppObject !=
nullptr);
109 bool IsObjC = (ConvertedObjCObject !=
nullptr);
110 const Expr *Obj = IsObjC ? ConvertedObjCObject
111 : IsCpp ? ConvertedCppObject
116 (
Result.Nodes.getNodeAs<Stmt>(
"comparison") !=
nullptr);
119 (
Result.Nodes.getNodeAs<
Decl>(
"osnumber") !=
nullptr);
122 (
Result.Nodes.getNodeAs<QualType>(
"int_type") !=
nullptr);
124 (
Result.Nodes.getNodeAs<QualType>(
"objc_bool_type") !=
nullptr);
126 (
Result.Nodes.getNodeAs<QualType>(
"cpp_bool_type") !=
nullptr);
128 llvm::SmallString<64> Msg;
129 llvm::raw_svector_ostream
OS(Msg);
146 OS <<
"a pointer value of type '" << ObjT <<
"' to a ";
148 std::string EuphemismForPlain =
"primitive";
149 std::string SuggestedApi = IsObjC ? (IsInteger ?
"" :
"-boolValue")
150 : IsCpp ? (IsOSNumber ?
"" :
"getValue()")
151 :
"CFNumberGetValue()";
152 if (SuggestedApi.empty()) {
156 "a method on '" + ObjT.getAsString() +
"' to get the scalar value";
161 EuphemismForPlain =
"scalar";
165 OS << EuphemismForPlain <<
" integer value";
167 OS << EuphemismForPlain <<
" BOOL value";
169 OS << EuphemismForPlain <<
" bool value";
171 OS << EuphemismForPlain <<
" boolean value";
175 OS <<
"; instead, either compare the pointer to "
176 << (IsObjC ?
"nil" : IsCpp ?
"nullptr" :
"NULL") <<
" or ";
178 OS <<
"; did you mean to ";
181 OS <<
"compare the result of calling " << SuggestedApi;
183 OS <<
"call " << SuggestedApi;
185 if (!IsPedanticMatch)
189 ADC->
getDecl(),
C,
"Suspicious number object conversion",
"Logic error",
195void NumberObjectConversionChecker::checkASTCodeBody(
const Decl *D,
197 BugReporter &BR)
const {
199 auto CSuspiciousNumberObjectExprM =
expr(ignoringParenImpCasts(
206 auto CppSuspiciousNumberObjectExprM =
207 expr(ignoringParenImpCasts(
208 expr(hasType(hasCanonicalType(
214 .bind(
"osnumber"))))))))))
215 .bind(
"cpp_object")));
218 auto ObjCSuspiciousNumberObjectExprM =
219 expr(ignoringParenImpCasts(
220 expr(hasType(hasCanonicalType(
225 .bind(
"objc_object")));
227 auto SuspiciousNumberObjectExprM =
anyOf(
228 CSuspiciousNumberObjectExprM,
229 CppSuspiciousNumberObjectExprM,
230 ObjCSuspiciousNumberObjectExprM);
233 auto AnotherSuspiciousNumberObjectExprM =
235 equalsBoundNode(
"c_object"),
236 equalsBoundNode(
"objc_object"),
237 equalsBoundNode(
"cpp_object")));
240 auto ObjCSuspiciousScalarBooleanTypeM =
242 .bind(
"objc_bool_type");
245 auto SuspiciousScalarBooleanTypeM =
247 ObjCSuspiciousScalarBooleanTypeM));
252 auto SuspiciousScalarNumberTypeM =
253 qualType(hasCanonicalType(isInteger()),
258 auto SuspiciousScalarTypeM =
260 SuspiciousScalarNumberTypeM));
262 auto SuspiciousScalarExprM =
263 expr(ignoringParenImpCasts(
expr(hasType(SuspiciousScalarTypeM))));
265 auto ConversionThroughAssignmentM =
267 hasLHS(SuspiciousScalarExprM),
268 hasRHS(SuspiciousNumberObjectExprM)));
270 auto ConversionThroughBranchingM =
272 hasCondition(SuspiciousNumberObjectExprM),
274 ))).bind(
"pedantic");
276 auto ConversionThroughCallM =
277 callExpr(hasAnyArgument(
allOf(hasType(SuspiciousScalarTypeM),
278 ignoringParenImpCasts(
279 SuspiciousNumberObjectExprM))));
284 auto ConversionThroughEquivalenceM =
286 hasEitherOperand(SuspiciousNumberObjectExprM),
287 hasEitherOperand(SuspiciousScalarExprM
288 .bind(
"check_if_null"))))
291 auto ConversionThroughComparisonM =
293 hasOperatorName(
"<="), hasOperatorName(
"<")),
294 hasEitherOperand(SuspiciousNumberObjectExprM),
295 hasEitherOperand(SuspiciousScalarExprM)))
298 auto ConversionThroughConditionalOperatorM =
300 hasCondition(SuspiciousNumberObjectExprM),
303 unless(hasFalseExpression(
307 auto ConversionThroughExclamationMarkM =
309 has(
expr(SuspiciousNumberObjectExprM))))
312 auto ConversionThroughExplicitBooleanCastM =
314 has(
expr(SuspiciousNumberObjectExprM))));
316 auto ConversionThroughExplicitNumberCastM =
318 has(
expr(SuspiciousNumberObjectExprM))));
320 auto ConversionThroughInitializerM =
322 varDecl(hasType(SuspiciousScalarTypeM),
323 hasInitializer(SuspiciousNumberObjectExprM))));
325 auto FinalM =
stmt(
anyOf(ConversionThroughAssignmentM,
326 ConversionThroughBranchingM,
327 ConversionThroughCallM,
328 ConversionThroughComparisonM,
329 ConversionThroughConditionalOperatorM,
330 ConversionThroughEquivalenceM,
331 ConversionThroughExclamationMarkM,
332 ConversionThroughExplicitBooleanCastM,
333 ConversionThroughExplicitNumberCastM,
334 ConversionThroughInitializerM)).bind(
"conv");
343void ento::registerNumberObjectConversionChecker(CheckerManager &Mgr) {
344 NumberObjectConversionChecker *Chk =
350bool ento::shouldRegisterNumberObjectConversionChecker(
const CheckerManager &mgr) {
SourceManager & getSourceManager()
QualType getPointerType(QualType T) const
Return the uniqued reference to the type for a pointer to the specified type.
const LangOptions & getLangOpts() const
const Decl * getDecl() const
ASTContext & getASTContext() const
virtual Stmt * getBody() const
getBody - If this Decl represents a declaration for a body of code, such as a function or method defi...
@ SE_AllowSideEffects
Allow any unmodeled side effect.
static StringRef getImmediateMacroName(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)
Retrieve the name of the immediate macro expansion.
QualType getCanonicalType() const
QualType getUnqualifiedType() const
Retrieve the unqualified variant of the given type, removing as little sugar as possible.
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
bool isPointerType() const
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Called when the Match registered for it was successfully found in the AST.
void addMatcher(const DeclarationMatcher &NodeMatch, MatchCallback *Action)
Adds a matcher to execute when running over the AST.
void match(const T &Node, ASTContext &Context)
Calls the registered callbacks on all matches on the given Node.
ASTContext & getASTContext() override
AnalysisDeclContext * getAnalysisDeclContext(const Decl *D)
const SourceManager & getSourceManager()
void EmitBasicReport(const Decl *DeclWithIssue, const CheckerFrontend *Checker, StringRef BugName, StringRef BugCategory, StringRef BugStr, PathDiagnosticLocation Loc, ArrayRef< SourceRange > Ranges={}, ArrayRef< FixItHint > Fixits={})
const AnalyzerOptions & getAnalyzerOptions() const
CHECKER * registerChecker(AT &&...Args)
Register a single-part checker (derived from Checker): construct its singleton instance,...
Simple checker classes that implement one frontend (i.e.
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
const internal::VariadicDynCastAllOfMatcher< Decl, VarDecl > varDecl
Matches variable declarations.
const internal::VariadicOperatorMatcherFunc< 1, 1 > unless
Matches if the provided matcher does not match.
const AstTypeMatcher< ObjCObjectPointerType > objcObjectPointerType
const internal::ArgumentAdaptingMatcherFunc< internal::HasDescendantMatcher > hasDescendant
Matches AST nodes that have descendant AST nodes that match the provided matcher.
const internal::VariadicDynCastAllOfMatcher< Decl, TypedefDecl > typedefDecl
Matches typedef declarations.
internal::Matcher< NamedDecl > hasName(StringRef Name)
Matches NamedDecl nodes that have the specified name.
const internal::VariadicDynCastAllOfMatcher< Stmt, CallExpr > callExpr
Matches call expressions.
const internal::ArgumentAdaptingMatcherFunc< internal::ForEachDescendantMatcher > forEachDescendant
Matches AST nodes that have descendant AST nodes that match the provided matcher.
const internal::VariadicDynCastAllOfMatcher< Stmt, UnaryOperator > unaryOperator
Matches unary operator expressions.
const internal::VariadicDynCastAllOfMatcher< Stmt, BinaryOperator > binaryOperator
Matches binary operator expressions.
const internal::ArgumentAdaptingMatcherFunc< internal::HasMatcher > has
Matches AST nodes that have child AST nodes that match the provided matcher.
const internal::VariadicDynCastAllOfMatcher< Stmt, ExplicitCastExpr > explicitCastExpr
Matches explicit cast expressions.
const internal::VariadicDynCastAllOfMatcher< Decl, ObjCInterfaceDecl > objcInterfaceDecl
Matches Objective-C interface declarations.
const AstTypeMatcher< PointerType > pointerType
const internal::VariadicOperatorMatcherFunc< 2, std::numeric_limits< unsigned >::max()> allOf
Matches if all given matchers match.
const AstTypeMatcher< RecordType > recordType
const internal::VariadicDynCastAllOfMatcher< Decl, CXXRecordDecl > cxxRecordDecl
Matches C++ class declarations.
internal::Matcher< T > traverse(TraversalKind TK, const internal::Matcher< T > &InnerMatcher)
Causes all nested matchers to be matched with the specified traversal kind.
internal::PolymorphicMatcher< internal::HasDeclarationMatcher, void(internal::HasDeclarationSupportedTypes), internal::Matcher< Decl > > hasDeclaration(const internal::Matcher< Decl > &InnerMatcher)
Matches a node if the declaration associated with that node matches the given matcher.
const AstTypeMatcher< TypedefType > typedefType
const internal::VariadicDynCastAllOfMatcher< Stmt, DeclStmt > declStmt
Matches declaration statements.
const internal::VariadicAllOfMatcher< Stmt > stmt
Matches statements.
const internal::VariadicDynCastAllOfMatcher< Stmt, Expr > expr
Matches expressions.
const internal::VariadicDynCastAllOfMatcher< Stmt, ConditionalOperator > conditionalOperator
Matches conditional operator expressions.
const internal::VariadicOperatorMatcherFunc< 2, std::numeric_limits< unsigned >::max()> anyOf
Matches if any of the given matchers matches.
const internal::VariadicAllOfMatcher< QualType > qualType
Matches QualTypes in the clang AST.
const internal::VariadicDynCastAllOfMatcher< Stmt, IfStmt > ifStmt
Matches if statements.
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...
std::variant< struct RequiresDecl, struct HeaderDecl, struct UmbrellaDirDecl, struct ModuleDecl, struct ExcludeDecl, struct ExportDecl, struct ExportAsDecl, struct ExternModuleDecl, struct UseDecl, struct LinkDecl, struct ConfigMacrosDecl, struct ConflictDecl > Decl
All declarations that can appear in a module declaration.
The JSON file list parser is used to communicate input to InstallAPI.
if(T->getSizeExpr()) TRY_TO(TraverseStmt(const_cast< Expr * >(T -> getSizeExpr())))
@ TK_AsIs
Will traverse all child nodes.
@ Result
The result type of a method or function.
APValue Val
Val - This is the value the expression can be folded to.
Contains all information for a given match.