diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp')
-rw-r--r-- | contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp | 354 |
1 files changed, 0 insertions, 354 deletions
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp deleted file mode 100644 index 1053424ae6fa..000000000000 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp +++ /dev/null @@ -1,354 +0,0 @@ -//===- NumberObjectConversionChecker.cpp -------------------------*- C++ -*-==// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file defines NumberObjectConversionChecker, which checks for a -// particular common mistake when dealing with numbers represented as objects -// passed around by pointers. Namely, the language allows to reinterpret the -// pointer as a number directly, often without throwing any warnings, -// but in most cases the result of such conversion is clearly unexpected, -// as pointer value, rather than number value represented by the pointee object, -// becomes the result of such operation. -// -// Currently the checker supports the Objective-C NSNumber class, -// and the OSBoolean class found in macOS low-level code; the latter -// can only hold boolean values. -// -// This checker has an option "Pedantic" (boolean), which enables detection of -// more conversion patterns (which are most likely more harmless, and therefore -// are more likely to produce false positives) - disabled by default, -// enabled with `-analyzer-config osx.NumberObjectConversion:Pedantic=true'. -// -//===----------------------------------------------------------------------===// - -#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" -#include "clang/StaticAnalyzer/Core/Checker.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" -#include "clang/Lex/Lexer.h" -#include "llvm/ADT/APSInt.h" - -using namespace clang; -using namespace ento; -using namespace ast_matchers; - -namespace { - -class NumberObjectConversionChecker : public Checker<check::ASTCodeBody> { -public: - bool Pedantic; - - void checkASTCodeBody(const Decl *D, AnalysisManager &AM, - BugReporter &BR) const; -}; - -class Callback : public MatchFinder::MatchCallback { - const NumberObjectConversionChecker *C; - BugReporter &BR; - AnalysisDeclContext *ADC; - -public: - Callback(const NumberObjectConversionChecker *C, - BugReporter &BR, AnalysisDeclContext *ADC) - : C(C), BR(BR), ADC(ADC) {} - virtual void run(const MatchFinder::MatchResult &Result); -}; -} // end of anonymous namespace - -void Callback::run(const MatchFinder::MatchResult &Result) { - bool IsPedanticMatch = - (Result.Nodes.getNodeAs<Stmt>("pedantic") != nullptr); - if (IsPedanticMatch && !C->Pedantic) - return; - - ASTContext &ACtx = ADC->getASTContext(); - - if (const Expr *CheckIfNull = - Result.Nodes.getNodeAs<Expr>("check_if_null")) { - // Unless the macro indicates that the intended type is clearly not - // a pointer type, we should avoid warning on comparing pointers - // to zero literals in non-pedantic mode. - // FIXME: Introduce an AST matcher to implement the macro-related logic? - bool MacroIndicatesWeShouldSkipTheCheck = false; - SourceLocation Loc = CheckIfNull->getBeginLoc(); - if (Loc.isMacroID()) { - StringRef MacroName = Lexer::getImmediateMacroName( - Loc, ACtx.getSourceManager(), ACtx.getLangOpts()); - if (MacroName == "NULL" || MacroName == "nil") - return; - if (MacroName == "YES" || MacroName == "NO") - MacroIndicatesWeShouldSkipTheCheck = true; - } - if (!MacroIndicatesWeShouldSkipTheCheck) { - Expr::EvalResult EVResult; - if (CheckIfNull->IgnoreParenCasts()->EvaluateAsInt( - EVResult, ACtx, Expr::SE_AllowSideEffects)) { - llvm::APSInt Result = EVResult.Val.getInt(); - if (Result == 0) { - if (!C->Pedantic) - return; - IsPedanticMatch = true; - } - } - } - } - - const Stmt *Conv = Result.Nodes.getNodeAs<Stmt>("conv"); - assert(Conv); - - const Expr *ConvertedCObject = Result.Nodes.getNodeAs<Expr>("c_object"); - const Expr *ConvertedCppObject = Result.Nodes.getNodeAs<Expr>("cpp_object"); - const Expr *ConvertedObjCObject = Result.Nodes.getNodeAs<Expr>("objc_object"); - bool IsCpp = (ConvertedCppObject != nullptr); - bool IsObjC = (ConvertedObjCObject != nullptr); - const Expr *Obj = IsObjC ? ConvertedObjCObject - : IsCpp ? ConvertedCppObject - : ConvertedCObject; - assert(Obj); - - bool IsComparison = - (Result.Nodes.getNodeAs<Stmt>("comparison") != nullptr); - - bool IsOSNumber = - (Result.Nodes.getNodeAs<Decl>("osnumber") != nullptr); - - bool IsInteger = - (Result.Nodes.getNodeAs<QualType>("int_type") != nullptr); - bool IsObjCBool = - (Result.Nodes.getNodeAs<QualType>("objc_bool_type") != nullptr); - bool IsCppBool = - (Result.Nodes.getNodeAs<QualType>("cpp_bool_type") != nullptr); - - llvm::SmallString<64> Msg; - llvm::raw_svector_ostream OS(Msg); - - // Remove ObjC ARC qualifiers. - QualType ObjT = Obj->getType().getUnqualifiedType(); - - // Remove consts from pointers. - if (IsCpp) { - assert(ObjT.getCanonicalType()->isPointerType()); - ObjT = ACtx.getPointerType( - ObjT->getPointeeType().getCanonicalType().getUnqualifiedType()); - } - - if (IsComparison) - OS << "Comparing "; - else - OS << "Converting "; - - OS << "a pointer value of type '" << ObjT.getAsString() << "' to a "; - - std::string EuphemismForPlain = "primitive"; - std::string SuggestedApi = IsObjC ? (IsInteger ? "" : "-boolValue") - : IsCpp ? (IsOSNumber ? "" : "getValue()") - : "CFNumberGetValue()"; - if (SuggestedApi.empty()) { - // A generic message if we're not sure what API should be called. - // FIXME: Pattern-match the integer type to make a better guess? - SuggestedApi = - "a method on '" + ObjT.getAsString() + "' to get the scalar value"; - // "scalar" is not quite correct or common, but some documentation uses it - // when describing object methods we suggest. For consistency, we use - // "scalar" in the whole sentence when we need to use this word in at least - // one place, otherwise we use "primitive". - EuphemismForPlain = "scalar"; - } - - if (IsInteger) - OS << EuphemismForPlain << " integer value"; - else if (IsObjCBool) - OS << EuphemismForPlain << " BOOL value"; - else if (IsCppBool) - OS << EuphemismForPlain << " bool value"; - else // Branch condition? - OS << EuphemismForPlain << " boolean value"; - - - if (IsPedanticMatch) - OS << "; instead, either compare the pointer to " - << (IsObjC ? "nil" : IsCpp ? "nullptr" : "NULL") << " or "; - else - OS << "; did you mean to "; - - if (IsComparison) - OS << "compare the result of calling " << SuggestedApi; - else - OS << "call " << SuggestedApi; - - if (!IsPedanticMatch) - OS << "?"; - - BR.EmitBasicReport( - ADC->getDecl(), C, "Suspicious number object conversion", "Logic error", - OS.str(), - PathDiagnosticLocation::createBegin(Obj, BR.getSourceManager(), ADC), - Conv->getSourceRange()); -} - -void NumberObjectConversionChecker::checkASTCodeBody(const Decl *D, - AnalysisManager &AM, - BugReporter &BR) const { - // Currently this matches CoreFoundation opaque pointer typedefs. - auto CSuspiciousNumberObjectExprM = - expr(ignoringParenImpCasts( - expr(hasType( - typedefType(hasDeclaration(anyOf( - typedefDecl(hasName("CFNumberRef")), - typedefDecl(hasName("CFBooleanRef"))))))) - .bind("c_object"))); - - // Currently this matches XNU kernel number-object pointers. - auto CppSuspiciousNumberObjectExprM = - expr(ignoringParenImpCasts( - expr(hasType(hasCanonicalType( - pointerType(pointee(hasCanonicalType( - recordType(hasDeclaration( - anyOf( - cxxRecordDecl(hasName("OSBoolean")), - cxxRecordDecl(hasName("OSNumber")) - .bind("osnumber")))))))))) - .bind("cpp_object"))); - - // Currently this matches NeXTSTEP number objects. - auto ObjCSuspiciousNumberObjectExprM = - expr(ignoringParenImpCasts( - expr(hasType(hasCanonicalType( - objcObjectPointerType(pointee( - qualType(hasCanonicalType( - qualType(hasDeclaration( - objcInterfaceDecl(hasName("NSNumber"))))))))))) - .bind("objc_object"))); - - auto SuspiciousNumberObjectExprM = anyOf( - CSuspiciousNumberObjectExprM, - CppSuspiciousNumberObjectExprM, - ObjCSuspiciousNumberObjectExprM); - - // Useful for predicates like "Unless we've seen the same object elsewhere". - auto AnotherSuspiciousNumberObjectExprM = - expr(anyOf( - equalsBoundNode("c_object"), - equalsBoundNode("objc_object"), - equalsBoundNode("cpp_object"))); - - // The .bind here is in order to compose the error message more accurately. - auto ObjCSuspiciousScalarBooleanTypeM = - qualType(typedefType(hasDeclaration( - typedefDecl(hasName("BOOL"))))).bind("objc_bool_type"); - - // The .bind here is in order to compose the error message more accurately. - auto SuspiciousScalarBooleanTypeM = - qualType(anyOf(qualType(booleanType()).bind("cpp_bool_type"), - ObjCSuspiciousScalarBooleanTypeM)); - - // The .bind here is in order to compose the error message more accurately. - // Also avoid intptr_t and uintptr_t because they were specifically created - // for storing pointers. - auto SuspiciousScalarNumberTypeM = - qualType(hasCanonicalType(isInteger()), - unless(typedefType(hasDeclaration( - typedefDecl(matchesName("^::u?intptr_t$")))))) - .bind("int_type"); - - auto SuspiciousScalarTypeM = - qualType(anyOf(SuspiciousScalarBooleanTypeM, - SuspiciousScalarNumberTypeM)); - - auto SuspiciousScalarExprM = - expr(ignoringParenImpCasts(expr(hasType(SuspiciousScalarTypeM)))); - - auto ConversionThroughAssignmentM = - binaryOperator(allOf(hasOperatorName("="), - hasLHS(SuspiciousScalarExprM), - hasRHS(SuspiciousNumberObjectExprM))); - - auto ConversionThroughBranchingM = - ifStmt(allOf( - hasCondition(SuspiciousNumberObjectExprM), - unless(hasConditionVariableStatement(declStmt()) - ))).bind("pedantic"); - - auto ConversionThroughCallM = - callExpr(hasAnyArgument(allOf(hasType(SuspiciousScalarTypeM), - ignoringParenImpCasts( - SuspiciousNumberObjectExprM)))); - - // We bind "check_if_null" to modify the warning message - // in case it was intended to compare a pointer to 0 with a relatively-ok - // construct "x == 0" or "x != 0". - auto ConversionThroughEquivalenceM = - binaryOperator(allOf(anyOf(hasOperatorName("=="), hasOperatorName("!=")), - hasEitherOperand(SuspiciousNumberObjectExprM), - hasEitherOperand(SuspiciousScalarExprM - .bind("check_if_null")))) - .bind("comparison"); - - auto ConversionThroughComparisonM = - binaryOperator(allOf(anyOf(hasOperatorName(">="), hasOperatorName(">"), - hasOperatorName("<="), hasOperatorName("<")), - hasEitherOperand(SuspiciousNumberObjectExprM), - hasEitherOperand(SuspiciousScalarExprM))) - .bind("comparison"); - - auto ConversionThroughConditionalOperatorM = - conditionalOperator(allOf( - hasCondition(SuspiciousNumberObjectExprM), - unless(hasTrueExpression( - hasDescendant(AnotherSuspiciousNumberObjectExprM))), - unless(hasFalseExpression( - hasDescendant(AnotherSuspiciousNumberObjectExprM))))) - .bind("pedantic"); - - auto ConversionThroughExclamationMarkM = - unaryOperator(allOf(hasOperatorName("!"), - has(expr(SuspiciousNumberObjectExprM)))) - .bind("pedantic"); - - auto ConversionThroughExplicitBooleanCastM = - explicitCastExpr(allOf(hasType(SuspiciousScalarBooleanTypeM), - has(expr(SuspiciousNumberObjectExprM)))); - - auto ConversionThroughExplicitNumberCastM = - explicitCastExpr(allOf(hasType(SuspiciousScalarNumberTypeM), - has(expr(SuspiciousNumberObjectExprM)))); - - auto ConversionThroughInitializerM = - declStmt(hasSingleDecl( - varDecl(hasType(SuspiciousScalarTypeM), - hasInitializer(SuspiciousNumberObjectExprM)))); - - auto FinalM = stmt(anyOf(ConversionThroughAssignmentM, - ConversionThroughBranchingM, - ConversionThroughCallM, - ConversionThroughComparisonM, - ConversionThroughConditionalOperatorM, - ConversionThroughEquivalenceM, - ConversionThroughExclamationMarkM, - ConversionThroughExplicitBooleanCastM, - ConversionThroughExplicitNumberCastM, - ConversionThroughInitializerM)).bind("conv"); - - MatchFinder F; - Callback CB(this, BR, AM.getAnalysisDeclContext(D)); - - F.addMatcher(stmt(forEachDescendant(FinalM)), &CB); - F.match(*D->getBody(), AM.getASTContext()); -} - -void ento::registerNumberObjectConversionChecker(CheckerManager &Mgr) { - NumberObjectConversionChecker *Chk = - Mgr.registerChecker<NumberObjectConversionChecker>(); - Chk->Pedantic = - Mgr.getAnalyzerOptions().getCheckerBooleanOption(Chk, "Pedantic"); -} - -bool ento::shouldRegisterNumberObjectConversionChecker(const LangOptions &LO) { - return true; -} |