35#include "llvm/ADT/APSInt.h"
39using namespace ast_matchers;
43class NumberObjectConversionChecker :
public Checker<check::ASTCodeBody> {
52 const NumberObjectConversionChecker *
C;
57 Callback(
const NumberObjectConversionChecker *C,
59 :
C(
C), BR(BR), ADC(ADC) {}
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;
80 if (
Loc.isMacroID()) {
83 if (MacroName ==
"NULL" || MacroName ==
"nil")
85 if (MacroName ==
"YES" || MacroName ==
"NO")
86 MacroIndicatesWeShouldSkipTheCheck =
true;
88 if (!MacroIndicatesWeShouldSkipTheCheck) {
90 if (CheckIfNull->IgnoreParenCasts()->EvaluateAsInt(
92 llvm::APSInt Result = EVResult.
Val.
getInt();
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);
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,
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 =
243 .bind(
"objc_bool_type");
246 auto SuspiciousScalarBooleanTypeM =
248 ObjCSuspiciousScalarBooleanTypeM));
253 auto SuspiciousScalarNumberTypeM =
254 qualType(hasCanonicalType(isInteger()),
259 auto SuspiciousScalarTypeM =
261 SuspiciousScalarNumberTypeM));
263 auto SuspiciousScalarExprM =
264 expr(ignoringParenImpCasts(
expr(hasType(SuspiciousScalarTypeM))));
266 auto ConversionThroughAssignmentM =
268 hasLHS(SuspiciousScalarExprM),
269 hasRHS(SuspiciousNumberObjectExprM)));
271 auto ConversionThroughBranchingM =
273 hasCondition(SuspiciousNumberObjectExprM),
275 ))).bind(
"pedantic");
277 auto ConversionThroughCallM =
278 callExpr(hasAnyArgument(
allOf(hasType(SuspiciousScalarTypeM),
279 ignoringParenImpCasts(
280 SuspiciousNumberObjectExprM))));
285 auto ConversionThroughEquivalenceM =
287 hasEitherOperand(SuspiciousNumberObjectExprM),
288 hasEitherOperand(SuspiciousScalarExprM
289 .bind(
"check_if_null"))))
292 auto ConversionThroughComparisonM =
294 hasOperatorName(
"<="), hasOperatorName(
"<")),
295 hasEitherOperand(SuspiciousNumberObjectExprM),
296 hasEitherOperand(SuspiciousScalarExprM)))
299 auto ConversionThroughConditionalOperatorM =
301 hasCondition(SuspiciousNumberObjectExprM),
304 unless(hasFalseExpression(
308 auto ConversionThroughExclamationMarkM =
310 has(
expr(SuspiciousNumberObjectExprM))))
313 auto ConversionThroughExplicitBooleanCastM =
315 has(
expr(SuspiciousNumberObjectExprM))));
317 auto ConversionThroughExplicitNumberCastM =
319 has(
expr(SuspiciousNumberObjectExprM))));
321 auto ConversionThroughInitializerM =
323 varDecl(hasType(SuspiciousScalarTypeM),
324 hasInitializer(SuspiciousNumberObjectExprM))));
326 auto FinalM =
stmt(
anyOf(ConversionThroughAssignmentM,
327 ConversionThroughBranchingM,
328 ConversionThroughCallM,
329 ConversionThroughComparisonM,
330 ConversionThroughConditionalOperatorM,
331 ConversionThroughEquivalenceM,
332 ConversionThroughExclamationMarkM,
333 ConversionThroughExplicitBooleanCastM,
334 ConversionThroughExplicitNumberCastM,
335 ConversionThroughInitializerM)).bind(
"conv");
344void ento::registerNumberObjectConversionChecker(
CheckerManager &Mgr) {
345 NumberObjectConversionChecker *Chk =
351bool ento::shouldRegisterNumberObjectConversionChecker(
const CheckerManager &mgr) {
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
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
AnalysisDeclContext contains the context data for the function, method or block under analysis.
bool getCheckerBooleanOption(StringRef CheckerName, StringRef OptionName, bool SearchInParents=false) const
Interprets an option's string value as a boolean.
Decl - This represents one declaration (or definition), e.g.
virtual Stmt * getBody() const
getBody - If this Decl represents a declaration for a body of code, such as a function or method defi...
This represents one expression.
@ 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.
A (possibly-)qualified type.
QualType getCanonicalType() const
QualType getUnqualifiedType() const
Retrieve the unqualified variant of the given type, removing as little sugar as possible.
Encodes a location in the source.
Stmt - This represents one statement.
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.
virtual void run(const MatchResult &Result)=0
Called on every match by the MatchFinder.
A class to allow finding matches over the Clang 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)
BugReporter is a utility class for generating PathDiagnostics for analysis.
const AnalyzerOptions & getAnalyzerOptions() const
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
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 AstTypeMatcher< ElaboratedType > elaboratedType
Matches types specified with an elaborated type keyword or with a qualified name.
const AstTypeMatcher< ObjCObjectPointerType > objcObjectPointerType
Matches an Objective-C object pointer type, which is different from a pointer type,...
const internal::VariadicOperatorMatcherFunc< 1, 1 > unless
Matches if the provided matcher does not match.
const internal::ArgumentAdaptingMatcherFunc< internal::HasDescendantMatcher > hasDescendant
Matches AST nodes that have descendant AST nodes that match the provided matcher.
const AstTypeMatcher< PointerType > pointerType
Matches pointer types, but does not match Objective-C object pointer types.
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 AstTypeMatcher< TypedefType > typedefType
Matches typedef types.
const internal::VariadicDynCastAllOfMatcher< Stmt, ExplicitCastExpr > explicitCastExpr
Matches explicit cast expressions.
const internal::VariadicDynCastAllOfMatcher< Decl, ObjCInterfaceDecl > objcInterfaceDecl
Matches Objective-C interface declarations.
const internal::VariadicOperatorMatcherFunc< 2, std::numeric_limits< unsigned >::max()> allOf
Matches if all given matchers match.
const AstTypeMatcher< RecordType > recordType
Matches record types (e.g.
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 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.
The JSON file list parser is used to communicate input to InstallAPI.
@ TK_AsIs
Will traverse all child nodes.
EvalResult is a struct with detailed info about an evaluated expression.
APValue Val
Val - This is the value the expression can be folded to.
Contains all information for a given match.