Ted Kremenek | f45d18c | 2008-09-18 06:33:41 +0000 | [diff] [blame] | 1 | //=- CheckNSError.cpp - Coding conventions for uses of NSError ---*- C++ -*-==// |
| 2 | // |
| 3 | // The LLVM Compiler Infrastructure |
| 4 | // |
| 5 | // This file is distributed under the University of Illinois Open Source |
| 6 | // License. See LICENSE.TXT for details. |
| 7 | // |
| 8 | //===----------------------------------------------------------------------===// |
| 9 | // |
| 10 | // This file defines a CheckNSError, a flow-insenstive check |
| 11 | // that determines if an Objective-C class interface correctly returns |
| 12 | // a non-void return type. |
| 13 | // |
| 14 | // File under feature request PR 2600. |
| 15 | // |
| 16 | //===----------------------------------------------------------------------===// |
| 17 | |
| 18 | #include "clang/Analysis/LocalCheckers.h" |
| 19 | #include "clang/Analysis/PathSensitive/BugReporter.h" |
Ted Kremenek | cfdf9b4 | 2008-09-18 21:25:13 +0000 | [diff] [blame] | 20 | #include "clang/Analysis/PathSensitive/GRExprEngine.h" |
| 21 | #include "BasicObjCFoundationChecks.h" |
| 22 | #include "llvm/Support/Compiler.h" |
Ted Kremenek | f45d18c | 2008-09-18 06:33:41 +0000 | [diff] [blame] | 23 | #include "clang/AST/DeclObjC.h" |
Ted Kremenek | cfdf9b4 | 2008-09-18 21:25:13 +0000 | [diff] [blame] | 24 | #include "clang/AST/Decl.h" |
| 25 | #include "llvm/ADT/SmallVector.h" |
Ted Kremenek | f45d18c | 2008-09-18 06:33:41 +0000 | [diff] [blame] | 26 | |
| 27 | using namespace clang; |
| 28 | |
Ted Kremenek | cfdf9b4 | 2008-09-18 21:25:13 +0000 | [diff] [blame] | 29 | namespace { |
| 30 | class VISIBILITY_HIDDEN NSErrorCheck : public BugTypeCacheLocation { |
| 31 | |
| 32 | void EmitGRWarnings(GRBugReporter& BR); |
Ted Kremenek | f45d18c | 2008-09-18 06:33:41 +0000 | [diff] [blame] | 33 | |
Ted Kremenek | cfdf9b4 | 2008-09-18 21:25:13 +0000 | [diff] [blame] | 34 | void CheckSignature(ObjCMethodDecl& MD, QualType& ResultTy, |
| 35 | llvm::SmallVectorImpl<VarDecl*>& Params, |
| 36 | IdentifierInfo* NSErrorII); |
| 37 | |
| 38 | bool CheckArgument(QualType ArgTy, IdentifierInfo* NSErrorII); |
| 39 | |
Ted Kremenek | 7360fda | 2008-09-18 23:09:54 +0000 | [diff] [blame^] | 40 | void CheckParamDeref(VarDecl* V, GRStateRef state, GRExprEngine& Eng, |
| 41 | GRBugReporter& BR); |
| 42 | |
| 43 | const char* desc; |
Ted Kremenek | cfdf9b4 | 2008-09-18 21:25:13 +0000 | [diff] [blame] | 44 | public: |
Ted Kremenek | 7360fda | 2008-09-18 23:09:54 +0000 | [diff] [blame^] | 45 | NSErrorCheck() : desc(0) {} |
| 46 | |
Ted Kremenek | cfdf9b4 | 2008-09-18 21:25:13 +0000 | [diff] [blame] | 47 | void EmitWarnings(BugReporter& BR) { EmitGRWarnings(cast<GRBugReporter>(BR));} |
| 48 | const char* getName() const { return "NSError** null dereference"; } |
Ted Kremenek | 7360fda | 2008-09-18 23:09:54 +0000 | [diff] [blame^] | 49 | const char* getDescription() const { return desc; } |
Ted Kremenek | cfdf9b4 | 2008-09-18 21:25:13 +0000 | [diff] [blame] | 50 | }; |
| 51 | |
| 52 | } // end anonymous namespace |
| 53 | |
| 54 | BugType* clang::CreateNSErrorCheck() { |
| 55 | return new NSErrorCheck(); |
| 56 | } |
| 57 | |
| 58 | void NSErrorCheck::EmitGRWarnings(GRBugReporter& BR) { |
| 59 | // Get the analysis engine and the exploded analysis graph. |
| 60 | GRExprEngine& Eng = BR.getEngine(); |
| 61 | GRExprEngine::GraphTy& G = Eng.getGraph(); |
| 62 | |
| 63 | // Get the declaration of the method/function that was analyzed. |
| 64 | Decl& CodeDecl = G.getCodeDecl(); |
| 65 | |
| 66 | ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(&CodeDecl); |
| 67 | if (!MD) |
| 68 | return; |
| 69 | |
| 70 | // Get the ASTContext, which is useful for querying type information. |
Ted Kremenek | f45d18c | 2008-09-18 06:33:41 +0000 | [diff] [blame] | 71 | ASTContext &Ctx = BR.getContext(); |
Ted Kremenek | cfdf9b4 | 2008-09-18 21:25:13 +0000 | [diff] [blame] | 72 | |
| 73 | QualType ResultTy; |
| 74 | llvm::SmallVector<VarDecl*, 5> Params; |
| 75 | CheckSignature(*MD, ResultTy, Params, &Ctx.Idents.get("NSError")); |
Ted Kremenek | f45d18c | 2008-09-18 06:33:41 +0000 | [diff] [blame] | 76 | |
Ted Kremenek | cfdf9b4 | 2008-09-18 21:25:13 +0000 | [diff] [blame] | 77 | if (Params.empty()) |
| 78 | return; |
| 79 | |
| 80 | if (ResultTy == Ctx.VoidTy) { |
| 81 | BR.EmitBasicReport("Bad return type when passing NSError**", |
| 82 | "Method accepting NSError** argument should have " |
| 83 | "non-void return value to indicate that an error occurred.", |
| 84 | CodeDecl.getLocation()); |
Ted Kremenek | f45d18c | 2008-09-18 06:33:41 +0000 | [diff] [blame] | 85 | |
Ted Kremenek | f45d18c | 2008-09-18 06:33:41 +0000 | [diff] [blame] | 86 | } |
Ted Kremenek | 7360fda | 2008-09-18 23:09:54 +0000 | [diff] [blame^] | 87 | |
| 88 | // Scan the NSError** parameters for an implicit null dereference. |
| 89 | for (llvm::SmallVectorImpl<VarDecl*>::iterator I=Params.begin(), |
| 90 | E=Params.end(); I!=E; ++I) |
| 91 | for (GRExprEngine::GraphTy::roots_iterator RI=G.roots_begin(), |
| 92 | RE=G.roots_end(); RI!=RE; ++RI) |
| 93 | CheckParamDeref(*I, GRStateRef((*RI)->getState(), Eng.getStateManager()), |
| 94 | Eng, BR); |
Ted Kremenek | f45d18c | 2008-09-18 06:33:41 +0000 | [diff] [blame] | 95 | } |
Ted Kremenek | cfdf9b4 | 2008-09-18 21:25:13 +0000 | [diff] [blame] | 96 | |
| 97 | void NSErrorCheck::CheckSignature(ObjCMethodDecl& M, QualType& ResultTy, |
| 98 | llvm::SmallVectorImpl<VarDecl*>& Params, |
| 99 | IdentifierInfo* NSErrorII) { |
| 100 | |
| 101 | ResultTy = M.getResultType(); |
| 102 | |
| 103 | for (ObjCMethodDecl::param_iterator I=M.param_begin(), |
| 104 | E=M.param_end(); I!=E; ++I) |
| 105 | if (CheckArgument((*I)->getType(), NSErrorII)) |
| 106 | Params.push_back(*I); |
| 107 | } |
| 108 | |
| 109 | bool NSErrorCheck::CheckArgument(QualType ArgTy, IdentifierInfo* NSErrorII) { |
| 110 | const PointerType* PPT = ArgTy->getAsPointerType(); |
| 111 | if (!PPT) return false; |
| 112 | |
| 113 | const PointerType* PT = PPT->getPointeeType()->getAsPointerType(); |
| 114 | if (!PT) return false; |
| 115 | |
| 116 | const ObjCInterfaceType *IT = |
| 117 | PT->getPointeeType()->getAsObjCInterfaceType(); |
| 118 | |
| 119 | if (!IT) return false; |
| 120 | return IT->getDecl()->getIdentifier() == NSErrorII; |
| 121 | } |
Ted Kremenek | 7360fda | 2008-09-18 23:09:54 +0000 | [diff] [blame^] | 122 | |
| 123 | void NSErrorCheck::CheckParamDeref(VarDecl* Param, GRStateRef rootState, |
| 124 | GRExprEngine& Eng, GRBugReporter& BR) { |
| 125 | |
| 126 | RVal ParamRVal = rootState.GetRVal(lval::DeclVal(Param)); |
| 127 | |
| 128 | // FIXME: For now assume that ParamRVal is symbolic. We need to generalize |
| 129 | // this later. |
| 130 | lval::SymbolVal* SV = dyn_cast<lval::SymbolVal>(&ParamRVal); |
| 131 | if (!SV) return; |
| 132 | |
| 133 | // Iterate over the implicit-null dereferences. |
| 134 | for (GRExprEngine::null_deref_iterator I=Eng.implicit_null_derefs_begin(), |
| 135 | E=Eng.implicit_null_derefs_end(); I!=E; ++I) { |
| 136 | |
| 137 | GRStateRef state = GRStateRef((*I)->getState(), Eng.getStateManager()); |
| 138 | const RVal* X = state.get<GRState::NullDerefTag>(); |
| 139 | const lval::SymbolVal* SVX = dyn_cast_or_null<lval::SymbolVal>(X); |
| 140 | if (!SVX || SVX->getSymbol() != SV->getSymbol()) continue; |
| 141 | |
| 142 | // Emit an error. |
| 143 | BugReport R(*this, *I); |
| 144 | |
| 145 | std::string msg; |
| 146 | llvm::raw_string_ostream os(msg); |
| 147 | os << "Potential null dereference. According to coding standards in " |
| 148 | "'Creating and Returning NSError Objects' the parameter '" |
| 149 | << Param->getName() << "' may be null."; |
| 150 | desc = os.str().c_str(); |
| 151 | |
| 152 | BR.EmitWarning(R); |
| 153 | } |
| 154 | } |