36 #include "llvm/ADT/APSInt.h" 38 using namespace clang;
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(
216 .bind(
"osnumber"))))))))))
217 .bind(
"cpp_object")));
220 auto ObjCSuspiciousNumberObjectExprM =
221 expr(ignoringParenImpCasts(
222 expr(hasType(hasCanonicalType(
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 =
274 hasCondition(SuspiciousNumberObjectExprM),
276 ))).bind(
"pedantic");
278 auto ConversionThroughCallM =
279 callExpr(hasAnyArgument(
allOf(hasType(SuspiciousScalarTypeM),
280 ignoringParenImpCasts(
281 SuspiciousNumberObjectExprM))));
286 auto ConversionThroughEquivalenceM =
290 .bind(
"check_if_null"))))
293 auto ConversionThroughComparisonM =
295 hasOperatorName(
"<="), hasOperatorName(
"<")),
300 auto ConversionThroughConditionalOperatorM =
302 hasCondition(SuspiciousNumberObjectExprM),
305 unless(hasFalseExpression(
309 auto ConversionThroughExclamationMarkM =
311 has(
expr(SuspiciousNumberObjectExprM))))
314 auto ConversionThroughExplicitBooleanCastM =
316 has(
expr(SuspiciousNumberObjectExprM))));
318 auto ConversionThroughExplicitNumberCastM =
320 has(
expr(SuspiciousNumberObjectExprM))));
322 auto ConversionThroughInitializerM =
324 varDecl(hasType(SuspiciousScalarTypeM),
325 hasInitializer(SuspiciousNumberObjectExprM))));
327 auto FinalM =
stmt(
anyOf(ConversionThroughAssignmentM,
328 ConversionThroughBranchingM,
329 ConversionThroughCallM,
330 ConversionThroughComparisonM,
331 ConversionThroughConditionalOperatorM,
332 ConversionThroughEquivalenceM,
333 ConversionThroughExclamationMarkM,
334 ConversionThroughExplicitBooleanCastM,
335 ConversionThroughExplicitNumberCastM,
336 ConversionThroughInitializerM)).bind(
"conv");
345 void ento::registerNumberObjectConversionChecker(
CheckerManager &Mgr) {
346 NumberObjectConversionChecker *Chk =
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.
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.
const internal::VariadicDynCastAllOfMatcher< Stmt, IfStmt > ifStmt
Matches if statements.
ASTContext & getASTContext() override
AnalysisDeclContext * getAnalysisDeclContext(const Decl *D)
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.
Expr - This represents one expression.
Allow any unmodeled side effect.
const internal::VariadicDynCastAllOfMatcher< Stmt, ConditionalOperator > conditionalOperator
Matches conditional operator expressions.
bool getBooleanOption(StringRef Name, bool DefaultVal, const ento::CheckerBase *C=nullptr, bool SearchInParents=false)
Interprets an option's string value as a boolean.
CHECKER * registerChecker(AT... Args)
Used to register checkers.
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.
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.
Dataflow Directional Tag Classes.
const internal::VariadicDynCastAllOfMatcher< Decl, CXXRecordDecl > cxxRecordDecl
Matches C++ class declarations.
static std::string getAsString(SplitQualType split, const PrintingPolicy &Policy)
AnalyzerOptions & getAnalyzerOptions()
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
const LangOptions & getLangOpts() const