35#include "llvm/ADT/APSInt.h" 
   43class NumberObjectConversionChecker : 
public Checker<check::ASTCodeBody> {
 
   47  void checkASTCodeBody(
const Decl *D, AnalysisManager &AM,
 
   48                        BugReporter &BR) 
const;
 
   52  const NumberObjectConversionChecker *C;
 
   54  AnalysisDeclContext *ADC;
 
   57  Callback(
const NumberObjectConversionChecker *C,
 
   58           BugReporter &BR, AnalysisDeclContext *ADC)
 
   59      : C(C), BR(BR), ADC(ADC) {}
 
   60  void run(
const MatchFinder::MatchResult &
Result) 
override;
 
   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;
 
   79    SourceLocation Loc = CheckIfNull->getBeginLoc();
 
   83      if (MacroName == 
"NULL" || MacroName == 
"nil")
 
   85      if (MacroName == 
"YES" || MacroName == 
"NO")
 
   86        MacroIndicatesWeShouldSkipTheCheck = 
true;
 
   88    if (!MacroIndicatesWeShouldSkipTheCheck) {
 
   89      Expr::EvalResult EVResult;
 
   90      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);
 
  128  llvm::SmallString<64> Msg;
 
  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,
 
  197                                                     BugReporter &BR)
 const {
 
  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 =
 
  242          .bind(
"objc_bool_type");
 
  245  auto SuspiciousScalarBooleanTypeM =
 
  247                     ObjCSuspiciousScalarBooleanTypeM));
 
  252  auto SuspiciousScalarNumberTypeM =
 
  253      qualType(hasCanonicalType(isInteger()),
 
  258  auto SuspiciousScalarTypeM =
 
  260                     SuspiciousScalarNumberTypeM));
 
  262  auto SuspiciousScalarExprM =
 
  263      expr(ignoringParenImpCasts(
expr(hasType(SuspiciousScalarTypeM))));
 
  265  auto ConversionThroughAssignmentM =
 
  267                           hasLHS(SuspiciousScalarExprM),
 
  268                           hasRHS(SuspiciousNumberObjectExprM)));
 
  270  auto ConversionThroughBranchingM =
 
  272          hasCondition(SuspiciousNumberObjectExprM),
 
  274      ))).bind(
"pedantic");
 
  276  auto ConversionThroughCallM =
 
  277      callExpr(hasAnyArgument(
allOf(hasType(SuspiciousScalarTypeM),
 
  278                                    ignoringParenImpCasts(
 
  279                                        SuspiciousNumberObjectExprM))));
 
  284  auto ConversionThroughEquivalenceM =
 
  286                           hasEitherOperand(SuspiciousNumberObjectExprM),
 
  287                           hasEitherOperand(SuspiciousScalarExprM
 
  288                                            .bind(
"check_if_null"))))
 
  291  auto ConversionThroughComparisonM =
 
  293                                 hasOperatorName(
"<="), hasOperatorName(
"<")),
 
  294                           hasEitherOperand(SuspiciousNumberObjectExprM),
 
  295                           hasEitherOperand(SuspiciousScalarExprM)))
 
  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");
 
  343void ento::registerNumberObjectConversionChecker(CheckerManager &Mgr) {
 
  344  NumberObjectConversionChecker *Chk =
 
  350bool ento::shouldRegisterNumberObjectConversionChecker(
const CheckerManager &mgr) {
 
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
 
const Decl * getDecl() const
 
ASTContext & getASTContext() const
 
virtual Stmt * getBody() const
getBody - If this Decl represents a declaration for a body of code, such as a function or method defi...
 
@ 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.
 
QualType getCanonicalType() const
 
QualType getUnqualifiedType() const
Retrieve the unqualified variant of the given type, removing as little sugar as possible.
 
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.
 
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)
 
const SourceManager & getSourceManager()
 
void EmitBasicReport(const Decl *DeclWithIssue, const CheckerFrontend *Checker, StringRef BugName, StringRef BugCategory, StringRef BugStr, PathDiagnosticLocation Loc, ArrayRef< SourceRange > Ranges={}, ArrayRef< FixItHint > Fixits={})
 
const AnalyzerOptions & getAnalyzerOptions() const
 
CHECKER * registerChecker(AT &&...Args)
Register a single-part checker (derived from Checker): construct its singleton instance,...
 
Simple checker classes that implement one frontend (i.e.
 
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 internal::VariadicOperatorMatcherFunc< 1, 1 > unless
Matches if the provided matcher does not match.
 
const AstTypeMatcher< ObjCObjectPointerType > objcObjectPointerType
 
const internal::ArgumentAdaptingMatcherFunc< internal::HasDescendantMatcher > hasDescendant
Matches AST nodes that have descendant AST nodes that match the provided matcher.
 
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 internal::VariadicDynCastAllOfMatcher< Stmt, ExplicitCastExpr > explicitCastExpr
Matches explicit cast expressions.
 
const internal::VariadicDynCastAllOfMatcher< Decl, ObjCInterfaceDecl > objcInterfaceDecl
Matches Objective-C interface declarations.
 
const AstTypeMatcher< PointerType > pointerType
 
const internal::VariadicOperatorMatcherFunc< 2, std::numeric_limits< unsigned >::max()> allOf
Matches if all given matchers match.
 
const AstTypeMatcher< RecordType > recordType
 
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 AstTypeMatcher< TypedefType > typedefType
 
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.
 
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...
 
std::variant< struct RequiresDecl, struct HeaderDecl, struct UmbrellaDirDecl, struct ModuleDecl, struct ExcludeDecl, struct ExportDecl, struct ExportAsDecl, struct ExternModuleDecl, struct UseDecl, struct LinkDecl, struct ConfigMacrosDecl, struct ConflictDecl > Decl
All declarations that can appear in a module declaration.
 
The JSON file list parser is used to communicate input to InstallAPI.
 
if(T->getSizeExpr()) TRY_TO(TraverseStmt(const_cast< Expr * >(T -> getSizeExpr())))
 
@ TK_AsIs
Will traverse all child nodes.
 
@ Result
The result type of a method or function.
 
APValue Val
Val - This is the value the expression can be folded to.
 
Contains all information for a given match.