//===- DynamicTypePropagation.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 contains two checkers. One helps the static analyzer core to track // types, the other does type inference on Obj-C generics and report type // errors. // // Dynamic Type Propagation: // This checker defines the rules for dynamic type gathering and propagation. // // Generics Checker for Objective-C: // This checker tries to find type errors that the compiler is not able to catch // due to the implicit conversions that were introduced for backward // compatibility. // //===----------------------------------------------------------------------===// #include "clang/AST/ParentMap.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Basic/Builtins.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "llvm/ADT/STLExtras.h" #include <optional> usingnamespaceclang; usingnamespaceento; // ProgramState trait - The type inflation is tracked by DynamicTypeMap. This is // an auxiliary map that tracks more information about generic types, because in // some cases the most derived type is not the most informative one about the // type parameters. This types that are stored for each symbol in this map must // be specialized. // TODO: In some case the type stored in this map is exactly the same that is // stored in DynamicTypeMap. We should no store duplicated information in those // cases. REGISTER_MAP_WITH_PROGRAMSTATE(…) … namespace { class DynamicTypePropagation: public Checker< check::PreCall, check::PostCall, check::DeadSymbols, check::PostStmt<CastExpr>, check::PostStmt<CXXNewExpr>, check::PreObjCMessage, check::PostObjCMessage > { … }; bool isObjCClassType(QualType Type) { … } struct RuntimeType { … }; RuntimeType inferReceiverType(const ObjCMethodCall &Message, CheckerContext &C) { … } } // end anonymous namespace void DynamicTypePropagation::checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const { … } static void recordFixedType(const MemRegion *Region, const CXXMethodDecl *MD, CheckerContext &C) { … } void DynamicTypePropagation::checkPreCall(const CallEvent &Call, CheckerContext &C) const { … } void DynamicTypePropagation::checkPostCall(const CallEvent &Call, CheckerContext &C) const { … } /// TODO: Handle explicit casts. /// Handle C++ casts. /// /// Precondition: the cast is between ObjCObjectPointers. ExplodedNode *DynamicTypePropagation::dynamicTypePropagationOnCasts( const CastExpr *CE, ProgramStateRef &State, CheckerContext &C) const { … } void DynamicTypePropagation::checkPostStmt(const CXXNewExpr *NewE, CheckerContext &C) const { … } // Return a better dynamic type if one can be derived from the cast. // Compare the current dynamic type of the region and the new type to which we // are casting. If the new type is lower in the inheritance hierarchy, pick it. const ObjCObjectPointerType * DynamicTypePropagation::getBetterObjCType(const Expr *CastE, CheckerContext &C) const { … } static const ObjCObjectPointerType *getMostInformativeDerivedClassImpl( const ObjCObjectPointerType *From, const ObjCObjectPointerType *To, const ObjCObjectPointerType *MostInformativeCandidate, ASTContext &C) { … } /// A downcast may loose specialization information. E. g.: /// MutableMap<T, U> : Map /// The downcast to MutableMap looses the information about the types of the /// Map (due to the type parameters are not being forwarded to Map), and in /// general there is no way to recover that information from the /// declaration. In order to have to most information, lets find the most /// derived type that has all the type parameters forwarded. /// /// Get the a subclass of \p From (which has a lower bound \p To) that do not /// loose information about type parameters. \p To has to be a subclass of /// \p From. From has to be specialized. static const ObjCObjectPointerType * getMostInformativeDerivedClass(const ObjCObjectPointerType *From, const ObjCObjectPointerType *To, ASTContext &C) { … } /// Inputs: /// \param StaticLowerBound Static lower bound for a symbol. The dynamic lower /// bound might be the subclass of this type. /// \param StaticUpperBound A static upper bound for a symbol. /// \p StaticLowerBound expected to be the subclass of \p StaticUpperBound. /// \param Current The type that was inferred for a symbol in a previous /// context. Might be null when this is the first time that inference happens. /// Precondition: /// \p StaticLowerBound or \p StaticUpperBound is specialized. If \p Current /// is not null, it is specialized. /// Possible cases: /// (1) The \p Current is null and \p StaticLowerBound <: \p StaticUpperBound /// (2) \p StaticLowerBound <: \p Current <: \p StaticUpperBound /// (3) \p Current <: \p StaticLowerBound <: \p StaticUpperBound /// (4) \p StaticLowerBound <: \p StaticUpperBound <: \p Current /// Effect: /// Use getMostInformativeDerivedClass with the upper and lower bound of the /// set {\p StaticLowerBound, \p Current, \p StaticUpperBound}. The computed /// lower bound must be specialized. If the result differs from \p Current or /// \p Current is null, store the result. static bool storeWhenMoreInformative(ProgramStateRef &State, SymbolRef Sym, const ObjCObjectPointerType *const *Current, const ObjCObjectPointerType *StaticLowerBound, const ObjCObjectPointerType *StaticUpperBound, ASTContext &C) { … } /// Type inference based on static type information that is available for the /// cast and the tracked type information for the given symbol. When the tracked /// symbol and the destination type of the cast are unrelated, report an error. void DynamicTypePropagation::checkPostStmt(const CastExpr *CE, CheckerContext &C) const { … } static const Expr *stripCastsAndSugar(const Expr *E) { … } static bool isObjCTypeParamDependent(QualType Type) { … } /// A method might not be available in the interface indicated by the static /// type. However it might be available in the tracked type. In order to /// properly substitute the type parameters we need the declaration context of /// the method. The more specialized the enclosing class of the method is, the /// more likely that the parameter substitution will be successful. static const ObjCMethodDecl * findMethodDecl(const ObjCMessageExpr *MessageExpr, const ObjCObjectPointerType *TrackedType, ASTContext &ASTCtxt) { … } /// Get the returned ObjCObjectPointerType by a method based on the tracked type /// information, or null pointer when the returned type is not an /// ObjCObjectPointerType. static QualType getReturnTypeForMethod( const ObjCMethodDecl *Method, ArrayRef<QualType> TypeArgs, const ObjCObjectPointerType *SelfType, ASTContext &C) { … } /// When the receiver has a tracked type, use that type to validate the /// argumments of the message expression and the return value. void DynamicTypePropagation::checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const { … } /// This callback is used to infer the types for Class variables. This info is /// used later to validate messages that sent to classes. Class variables are /// initialized with by invoking the 'class' method on a class. /// This method is also used to infer the type information for the return /// types. // TODO: right now it only tracks generic types. Extend this to track every // type in the DynamicTypeMap and diagnose type errors! void DynamicTypePropagation::checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const { … } void DynamicTypePropagation::reportGenericsBug( const ObjCObjectPointerType *From, const ObjCObjectPointerType *To, ExplodedNode *N, SymbolRef Sym, CheckerContext &C, const Stmt *ReportedNode) const { … } PathDiagnosticPieceRef DynamicTypePropagation::GenericsBugVisitor::VisitNode( const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &BR) { … } /// Register checkers. void ento::registerObjCGenericsChecker(CheckerManager &mgr) { … } bool ento::shouldRegisterObjCGenericsChecker(const CheckerManager &mgr) { … } void ento::registerDynamicTypePropagation(CheckerManager &mgr) { … } bool ento::shouldRegisterDynamicTypePropagation(const CheckerManager &mgr) { … }