36 #include "llvm/ADT/APSInt.h" 38 using namespace clang;
44 class NumberObjectConversionChecker :
public Checker<check::ASTCodeBody> {
48 void checkASTCodeBody(
const Decl *D, AnalysisManager &AM,
49 BugReporter &BR)
const;
53 const NumberObjectConversionChecker *
C;
58 Callback(
const NumberObjectConversionChecker *C,
60 : C(C), BR(BR), ADC(ADC) {}
66 bool IsPedanticMatch =
68 if (IsPedanticMatch && !
C->Pedantic)
73 if (
const Expr *CheckIfNull =
79 bool MacroIndicatesWeShouldSkipTheCheck =
false;
84 if (MacroName ==
"NULL" || MacroName ==
"nil")
86 if (MacroName ==
"YES" || MacroName ==
"NO")
87 MacroIndicatesWeShouldSkipTheCheck =
true;
89 if (!MacroIndicatesWeShouldSkipTheCheck) {
91 if (CheckIfNull->IgnoreParenCasts()->EvaluateAsInt(
93 llvm::APSInt Result = EVResult.
Val.
getInt();
97 IsPedanticMatch =
true;
103 const Stmt *Conv = Result.Nodes.getNodeAs<
Stmt>(
"conv");
106 const Expr *ConvertedCObject = Result.Nodes.getNodeAs<
Expr>(
"c_object");
107 const Expr *ConvertedCppObject = Result.Nodes.getNodeAs<
Expr>(
"cpp_object");
108 const Expr *ConvertedObjCObject = Result.Nodes.getNodeAs<
Expr>(
"objc_object");
109 bool IsCpp = (ConvertedCppObject !=
nullptr);
110 bool IsObjC = (ConvertedObjCObject !=
nullptr);
111 const Expr *Obj = IsObjC ? ConvertedObjCObject
112 : IsCpp ? ConvertedCppObject
117 (Result.Nodes.getNodeAs<
Stmt>(
"comparison") !=
nullptr);
120 (Result.Nodes.getNodeAs<
Decl>(
"osnumber") !=
nullptr);
123 (Result.Nodes.getNodeAs<
QualType>(
"int_type") !=
nullptr);
125 (Result.Nodes.getNodeAs<
QualType>(
"objc_bool_type") !=
nullptr);
127 (Result.Nodes.getNodeAs<
QualType>(
"cpp_bool_type") !=
nullptr);
130 llvm::raw_svector_ostream
OS(Msg);
139 ObjT->getPointeeType().getCanonicalType().getUnqualifiedType());
147 OS <<
"a pointer value of type '" << ObjT.getAsString() <<
"' to a ";
149 std::string EuphemismForPlain =
"primitive";
150 std::string SuggestedApi = IsObjC ? (IsInteger ?
"" :
"-boolValue")
151 : IsCpp ? (IsOSNumber ?
"" :
"getValue()")
152 :
"CFNumberGetValue()";
153 if (SuggestedApi.empty()) {
157 "a method on '" + ObjT.getAsString() +
"' to get the scalar value";
162 EuphemismForPlain =
"scalar";
166 OS << EuphemismForPlain <<
" integer value";
168 OS << EuphemismForPlain <<
" BOOL value";
170 OS << EuphemismForPlain <<
" bool value";
172 OS << EuphemismForPlain <<
" boolean value";
176 OS <<
"; instead, either compare the pointer to " 177 << (IsObjC ?
"nil" : IsCpp ?
"nullptr" :
"NULL") <<
" or ";
179 OS <<
"; did you mean to ";
182 OS <<
"compare the result of calling " << SuggestedApi;
184 OS <<
"call " << SuggestedApi;
186 if (!IsPedanticMatch)
190 ADC->getDecl(),
C,
"Suspicious number object conversion",
"Logic error",
196 void NumberObjectConversionChecker::checkASTCodeBody(
const Decl *D,
198 BugReporter &BR)
const {
200 auto CSuspiciousNumberObjectExprM =
201 expr(ignoringParenImpCasts(
209 auto CppSuspiciousNumberObjectExprM =
210 expr(ignoringParenImpCasts(
211 expr(hasType(hasCanonicalType(
217 .bind(
"osnumber"))))))))))
218 .bind(
"cpp_object")));
221 auto ObjCSuspiciousNumberObjectExprM =
222 expr(ignoringParenImpCasts(
223 expr(hasType(hasCanonicalType(
228 .bind(
"objc_object")));
230 auto SuspiciousNumberObjectExprM =
anyOf(
231 CSuspiciousNumberObjectExprM,
232 CppSuspiciousNumberObjectExprM,
233 ObjCSuspiciousNumberObjectExprM);
236 auto AnotherSuspiciousNumberObjectExprM =
238 equalsBoundNode(
"c_object"),
239 equalsBoundNode(
"objc_object"),
240 equalsBoundNode(
"cpp_object")));
243 auto ObjCSuspiciousScalarBooleanTypeM =
248 auto SuspiciousScalarBooleanTypeM =
250 ObjCSuspiciousScalarBooleanTypeM));
255 auto SuspiciousScalarNumberTypeM =
256 qualType(hasCanonicalType(isInteger()),
261 auto SuspiciousScalarTypeM =
263 SuspiciousScalarNumberTypeM));
265 auto SuspiciousScalarExprM =
266 expr(ignoringParenImpCasts(
expr(hasType(SuspiciousScalarTypeM))));
268 auto ConversionThroughAssignmentM =
270 hasLHS(SuspiciousScalarExprM),
271 hasRHS(SuspiciousNumberObjectExprM)));
273 auto ConversionThroughBranchingM =
275 hasCondition(SuspiciousNumberObjectExprM),
277 ))).bind(
"pedantic");
279 auto ConversionThroughCallM =
280 callExpr(hasAnyArgument(
allOf(hasType(SuspiciousScalarTypeM),
281 ignoringParenImpCasts(
282 SuspiciousNumberObjectExprM))));
287 auto ConversionThroughEquivalenceM =
291 .bind(
"check_if_null"))))
294 auto ConversionThroughComparisonM =
296 hasOperatorName(
"<="), hasOperatorName(
"<")),
301 auto ConversionThroughConditionalOperatorM =
303 hasCondition(SuspiciousNumberObjectExprM),
306 unless(hasFalseExpression(
310 auto ConversionThroughExclamationMarkM =
312 has(
expr(SuspiciousNumberObjectExprM))))
315 auto ConversionThroughExplicitBooleanCastM =
317 has(
expr(SuspiciousNumberObjectExprM))));
319 auto ConversionThroughExplicitNumberCastM =
321 has(
expr(SuspiciousNumberObjectExprM))));
323 auto ConversionThroughInitializerM =
325 varDecl(hasType(SuspiciousScalarTypeM),
326 hasInitializer(SuspiciousNumberObjectExprM))));
328 auto FinalM =
stmt(
anyOf(ConversionThroughAssignmentM,
329 ConversionThroughBranchingM,
330 ConversionThroughCallM,
331 ConversionThroughComparisonM,
332 ConversionThroughConditionalOperatorM,
333 ConversionThroughEquivalenceM,
334 ConversionThroughExclamationMarkM,
335 ConversionThroughExplicitBooleanCastM,
336 ConversionThroughExplicitNumberCastM,
337 ConversionThroughInitializerM)).bind(
"conv");
340 Callback CB(
this, BR, AM.getAnalysisDeclContext(D));
346 void ento::registerNumberObjectConversionChecker(CheckerManager &Mgr) {
347 NumberObjectConversionChecker *Chk =
348 Mgr.registerChecker<NumberObjectConversionChecker>();
350 Mgr.getAnalyzerOptions().getCheckerBooleanOption(
"Pedantic",
false, Chk);
A class to allow finding matches over the Clang AST.
const internal::VariadicDynCastAllOfMatcher< Stmt, CallExpr > callExpr
Matches call expressions.
const internal::VariadicDynCastAllOfMatcher< Decl, TypedefDecl > typedefDecl
Matches typedef declarations.
A (possibly-)qualified type.
const internal::VariadicAllOfMatcher< Stmt > stmt
Matches statements.
const internal::VariadicDynCastAllOfMatcher< Decl, ObjCInterfaceDecl > objcInterfaceDecl
Matches Objective-C interface declarations.
const internal::ArgumentAdaptingMatcherFunc< internal::HasMatcher > has
Matches AST nodes that have child AST nodes that match the provided matcher.
virtual Stmt * getBody() const
getBody - If this Decl represents a declaration for a body of code, such as a function or method defi...
Stmt - This represents one statement.
internal::PolymorphicMatcherWithParam1< internal::HasDeclarationMatcher, internal::Matcher< Decl >, void(internal::HasDeclarationSupportedTypes)> hasDeclaration(const internal::Matcher< Decl > &InnerMatcher)
Matches a node if the declaration associated with that node matches the given matcher.
const internal::VariadicOperatorMatcherFunc< 2, std::numeric_limits< unsigned >::max()> anyOf
Matches if any of the given matchers matches.
Decl - This represents one declaration (or definition), e.g.
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< Stmt, Expr > expr
Matches expressions.
const internal::VariadicOperatorMatcherFunc< 2, std::numeric_limits< unsigned >::max()> allOf
Matches if all given matchers match.
const internal::VariadicDynCastAllOfMatcher< Stmt, BinaryOperator > binaryOperator
Matches binary operator expressions.
const internal::VariadicDynCastAllOfMatcher< Decl, VarDecl > varDecl
Matches variable declarations.
const AstTypeMatcher< RecordType > recordType
Matches record types (e.g.
void match(const T &Node, ASTContext &Context)
Calls the registered callbacks on all matches on the given Node.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
AnalysisDeclContext contains the context data for the function or method under analysis.
const AstTypeMatcher< TypedefType > typedefType
Matches typedef types.
const internal::VariadicDynCastAllOfMatcher< Stmt, DeclStmt > declStmt
Matches declaration statements.
void addMatcher(const DeclarationMatcher &NodeMatch, MatchCallback *Action)
Adds a matcher to execute when running over the AST.
const internal::VariadicDynCastAllOfMatcher< Stmt, IfStmt > ifStmt
Matches if statements.
APValue Val
Val - This is the value the expression can be folded to.
const T * getNodeAs(StringRef ID) const
Returns the AST node bound to ID.
const internal::ArgumentAdaptingMatcherFunc< internal::ForEachDescendantMatcher > forEachDescendant
Matches AST nodes that have descendant AST nodes that match the provided matcher. ...
const internal::VariadicDynCastAllOfMatcher< Stmt, ExplicitCastExpr > explicitCastExpr
Matches explicit cast expressions.
This represents one expression.
Allow any unmodeled side effect.
const internal::VariadicDynCastAllOfMatcher< Stmt, ConditionalOperator > conditionalOperator
Matches conditional operator expressions.
Contains all information for a given match.
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
QualType getCanonicalType() const
Encodes a location in the source.
const internal::VariadicAllOfMatcher< QualType > qualType
Matches QualTypes in the clang AST.
const internal::VariadicOperatorMatcherFunc< 1, 1 > unless
Matches if the provided matcher does not match.
static StringRef getImmediateMacroName(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)
Retrieve the name of the immediate macro expansion.
const BoundNodes Nodes
Contains the nodes bound on the current match.
Dataflow Directional Tag Classes.
EvalResult is a struct with detailed info about an evaluated expression.
const internal::VariadicDynCastAllOfMatcher< Decl, CXXRecordDecl > cxxRecordDecl
Matches C++ class declarations.
Indicates that the tracking object is a descendant of a referenced-counted OSObject, used in the Darwin kernel.
SourceManager & getSourceManager()
internal::Matcher< BinaryOperator > hasEitherOperand(const internal::Matcher< Expr > &InnerMatcher)
Matches if either the left hand side or the right hand side of a binary operator matches.
const internal::VariadicDynCastAllOfMatcher< Stmt, UnaryOperator > unaryOperator
Matches unary operator expressions.
QualType getUnqualifiedType() const
Retrieve the unqualified variant of the given type, removing as little sugar as possible.
internal::Matcher< NamedDecl > hasName(const std::string &Name)
Matches NamedDecl nodes that have the specified name.
QualType getPointerType(QualType T) const
Return the uniqued reference to the type for a pointer to the specified type.
const AstTypeMatcher< ObjCObjectPointerType > objcObjectPointerType
Matches an Objective-C object pointer type, which is different from a pointer type, despite being syntactically similar.
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
bool isPointerType() const
Called when the Match registered for it was successfully found in the AST.
const LangOptions & getLangOpts() const