36 #include "llvm/ADT/APSInt.h"
38 using namespace clang;
40 using namespace ast_matchers;
44 class NumberObjectConversionChecker :
public Checker<check::ASTCodeBody> {
52 class Callback :
public MatchFinder::MatchCallback {
53 const NumberObjectConversionChecker *
C;
58 Callback(
const NumberObjectConversionChecker *C,
60 : C(C), BR(BR), ADC(ADC) {}
61 virtual void run(
const MatchFinder::MatchResult &Result);
65 void Callback::run(
const MatchFinder::MatchResult &Result) {
66 bool IsPedanticMatch =
67 (Result.Nodes.getNodeAs<
Stmt>(
"pedantic") !=
nullptr);
68 if (IsPedanticMatch && !
C->Pedantic)
73 if (
const Expr *CheckIfNull =
74 Result.Nodes.getNodeAs<
Expr>(
"check_if_null")) {
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(
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);
138 ObjT->getPointeeType().getCanonicalType().getUnqualifiedType());
146 OS <<
"a pointer value of type '" << ObjT.
getAsString() <<
"' 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",
195 void NumberObjectConversionChecker::checkASTCodeBody(
const Decl *D,
199 auto CSuspiciousNumberObjectExprM =
200 expr(ignoringParenImpCasts(
208 auto CppSuspiciousNumberObjectExprM =
209 expr(ignoringParenImpCasts(
210 expr(hasType(hasCanonicalType(
211 pointerType(pointee(hasCanonicalType(
216 .bind(
"osnumber"))))))))))
217 .bind(
"cpp_object")));
220 auto ObjCSuspiciousNumberObjectExprM =
221 expr(ignoringParenImpCasts(
222 expr(hasType(hasCanonicalType(
223 objcObjectPointerType(pointee(
227 .bind(
"objc_object")));
229 auto SuspiciousNumberObjectExprM =
anyOf(
230 CSuspiciousNumberObjectExprM,
231 CppSuspiciousNumberObjectExprM,
232 ObjCSuspiciousNumberObjectExprM);
235 auto AnotherSuspiciousNumberObjectExprM =
237 equalsBoundNode(
"c_object"),
238 equalsBoundNode(
"objc_object"),
239 equalsBoundNode(
"cpp_object")));
242 auto ObjCSuspiciousScalarBooleanTypeM =
247 auto SuspiciousScalarBooleanTypeM =
249 ObjCSuspiciousScalarBooleanTypeM));
254 auto SuspiciousScalarNumberTypeM =
255 qualType(hasCanonicalType(isInteger()),
260 auto SuspiciousScalarTypeM =
262 SuspiciousScalarNumberTypeM));
264 auto SuspiciousScalarExprM =
265 expr(ignoringParenImpCasts(
expr(hasType(SuspiciousScalarTypeM))));
267 auto ConversionThroughAssignmentM =
269 hasLHS(SuspiciousScalarExprM),
270 hasRHS(SuspiciousNumberObjectExprM)));
272 auto ConversionThroughBranchingM =
273 ifStmt(hasCondition(SuspiciousNumberObjectExprM))
276 auto ConversionThroughCallM =
277 callExpr(hasAnyArgument(
allOf(hasType(SuspiciousScalarTypeM),
278 ignoringParenImpCasts(
279 SuspiciousNumberObjectExprM))));
284 auto ConversionThroughEquivalenceM =
288 .bind(
"check_if_null"))))
291 auto ConversionThroughComparisonM =
293 hasOperatorName(
"<="), hasOperatorName(
"<")),
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");
343 void ento::registerNumberObjectConversionChecker(
CheckerManager &Mgr) {
344 NumberObjectConversionChecker *Chk =
const internal::VariadicDynCastAllOfMatcher< Stmt, Expr > expr
Matches expressions.
A (possibly-)qualified type.
const internal::ArgumentAdaptingMatcherFunc< internal::HasDescendantMatcher > LLVM_ATTRIBUTE_UNUSED hasDescendant
Matches AST nodes that have descendant AST nodes that match the provided matcher. ...
Stmt - This represents one statement.
const internal::VariadicAllOfMatcher< Stmt > stmt
Matches statements.
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.
Decl - This represents one declaration (or definition), e.g.
std::string getAsString() const
const internal::VariadicDynCastAllOfMatcher< Decl, VarDecl > varDecl
Matches variable declarations.
const internal::VariadicDynCastAllOfMatcher< Stmt, ConditionalOperator > conditionalOperator
Matches conditional operator expressions.
const internal::VariadicAllOfMatcher< QualType > qualType
Matches QualTypes in the clang AST.
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 internal::VariadicDynCastAllOfMatcher< Decl, TypedefDecl > typedefDecl
Matches typedef declarations.
const LangOptions & getLangOpts() const
const internal::VariadicDynCastAllOfMatcher< Stmt, DeclStmt > declStmt
Matches declaration statements.
const internal::VariadicDynCastAllOfMatcher< Stmt, UnaryOperator > unaryOperator
Matches unary operator expressions.
ASTContext & getASTContext() override
AnalysisDeclContext * getAnalysisDeclContext(const Decl *D)
const internal::VariadicOperatorMatcherFunc< 2, UINT_MAX > allOf
Matches if all given matchers match.
const internal::VariadicDynCastAllOfMatcher< Stmt, BinaryOperator > binaryOperator
Matches binary operator expressions.
const internal::ArgumentAdaptingMatcherFunc< internal::ForEachDescendantMatcher > LLVM_ATTRIBUTE_UNUSED forEachDescendant
Matches AST nodes that have descendant AST nodes that match the provided matcher. ...
Expr - This represents one expression.
Allow any unmodeled side effect.
MatchFinder::MatchCallback * Callback
const internal::VariadicOperatorMatcherFunc< 1, 1 > unless
Matches if the provided matcher does not match.
bool getBooleanOption(StringRef Name, bool DefaultVal, const ento::CheckerBase *C=nullptr, bool SearchInParents=false)
Interprets an option's string value as a boolean.
const internal::VariadicDynCastAllOfMatcher< Decl, ObjCInterfaceDecl > objcInterfaceDecl
Matches Objective-C interface declarations.
BugReporter is a utility class for generating PathDiagnostics for analysis.
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
CHECKER * registerChecker()
Used to register checkers.
Encodes a location in the source.
const internal::ArgumentAdaptingMatcherFunc< internal::HasMatcher > LLVM_ATTRIBUTE_UNUSED 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...
const internal::VariadicDynCastAllOfMatcher< Stmt, ExplicitCastExpr > explicitCastExpr
Matches explicit cast expressions.
static StringRef getImmediateMacroName(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)
Retrieve the name of the immediate macro expansion.
QualType getPointerType(QualType T) const
Return the uniqued reference to the type for a pointer to the specified type.
AnalyzerOptions & getAnalyzerOptions()
QualType getCanonicalType() const
const internal::VariadicDynCastAllOfMatcher< Stmt, IfStmt > ifStmt
Matches if statements.
const internal::VariadicDynCastAllOfMatcher< Stmt, CallExpr > callExpr
Matches call expressions.
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::VariadicOperatorMatcherFunc< 2, UINT_MAX > anyOf
Matches if any of the given matchers matches.
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.
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
const internal::VariadicDynCastAllOfMatcher< Decl, CXXRecordDecl > cxxRecordDecl
Matches C++ class declarations.
bool isPointerType() const