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 =
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, 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.
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
CHECKER * registerChecker()
Used to register checkers.
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