mirror of
https://github.com/llvm/llvm-project.git
synced 2025-05-15 04:56:07 +00:00

Several of the existing methods were identical to their respective specializations, and so have been removed entirely. Several more 'leaf' optimizations were introduced. The getAsFoo() methods which imposed extra conditions, like getAsObjCInterfacePointerType(), have been left in place. llvm-svn: 82501
239 lines
7.4 KiB
C++
239 lines
7.4 KiB
C++
//=- CheckNSError.cpp - Coding conventions for uses of NSError ---*- C++ -*-==//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file defines a CheckNSError, a flow-insenstive check
|
|
// that determines if an Objective-C class interface correctly returns
|
|
// a non-void return type.
|
|
//
|
|
// File under feature request PR 2600.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/Analysis/LocalCheckers.h"
|
|
#include "clang/Analysis/PathSensitive/BugReporter.h"
|
|
#include "clang/Analysis/PathSensitive/GRExprEngine.h"
|
|
#include "BasicObjCFoundationChecks.h"
|
|
#include "llvm/Support/Compiler.h"
|
|
#include "clang/AST/DeclObjC.h"
|
|
#include "clang/AST/Decl.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
|
|
using namespace clang;
|
|
|
|
namespace {
|
|
class VISIBILITY_HIDDEN NSErrorCheck : public BugType {
|
|
const Decl &CodeDecl;
|
|
const bool isNSErrorWarning;
|
|
IdentifierInfo * const II;
|
|
GRExprEngine &Eng;
|
|
|
|
void CheckSignature(const ObjCMethodDecl& MD, QualType& ResultTy,
|
|
llvm::SmallVectorImpl<VarDecl*>& ErrorParams);
|
|
|
|
void CheckSignature(const FunctionDecl& MD, QualType& ResultTy,
|
|
llvm::SmallVectorImpl<VarDecl*>& ErrorParams);
|
|
|
|
bool CheckNSErrorArgument(QualType ArgTy);
|
|
bool CheckCFErrorArgument(QualType ArgTy);
|
|
|
|
void CheckParamDeref(const VarDecl *V, const LocationContext *LC,
|
|
const GRState *state, BugReporter& BR);
|
|
|
|
void EmitRetTyWarning(BugReporter& BR, const Decl& CodeDecl);
|
|
|
|
public:
|
|
NSErrorCheck(const Decl &D, bool isNSError, GRExprEngine& eng)
|
|
: BugType(isNSError ? "NSError** null dereference"
|
|
: "CFErrorRef* null dereference",
|
|
"Coding conventions (Apple)"),
|
|
CodeDecl(D),
|
|
isNSErrorWarning(isNSError),
|
|
II(&eng.getContext().Idents.get(isNSErrorWarning ? "NSError":"CFErrorRef")),
|
|
Eng(eng) {}
|
|
|
|
void FlushReports(BugReporter& BR);
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
void clang::RegisterNSErrorChecks(BugReporter& BR, GRExprEngine &Eng,
|
|
const Decl &D) {
|
|
BR.Register(new NSErrorCheck(D, true, Eng));
|
|
BR.Register(new NSErrorCheck(D, false, Eng));
|
|
}
|
|
|
|
void NSErrorCheck::FlushReports(BugReporter& BR) {
|
|
// Get the analysis engine and the exploded analysis graph.
|
|
ExplodedGraph& G = Eng.getGraph();
|
|
|
|
// Get the ASTContext, which is useful for querying type information.
|
|
ASTContext &Ctx = BR.getContext();
|
|
|
|
QualType ResultTy;
|
|
llvm::SmallVector<VarDecl*, 5> ErrorParams;
|
|
|
|
if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(&CodeDecl))
|
|
CheckSignature(*MD, ResultTy, ErrorParams);
|
|
else if (const FunctionDecl* FD = dyn_cast<FunctionDecl>(&CodeDecl))
|
|
CheckSignature(*FD, ResultTy, ErrorParams);
|
|
else
|
|
return;
|
|
|
|
if (ErrorParams.empty())
|
|
return;
|
|
|
|
if (ResultTy == Ctx.VoidTy) EmitRetTyWarning(BR, CodeDecl);
|
|
|
|
for (ExplodedGraph::roots_iterator RI=G.roots_begin(), RE=G.roots_end();
|
|
RI!=RE; ++RI) {
|
|
// Scan the parameters for an implicit null dereference.
|
|
for (llvm::SmallVectorImpl<VarDecl*>::iterator I=ErrorParams.begin(),
|
|
E=ErrorParams.end(); I!=E; ++I)
|
|
CheckParamDeref(*I, (*RI)->getLocationContext(), (*RI)->getState(), BR);
|
|
}
|
|
}
|
|
|
|
void NSErrorCheck::EmitRetTyWarning(BugReporter& BR, const Decl& CodeDecl) {
|
|
std::string sbuf;
|
|
llvm::raw_string_ostream os(sbuf);
|
|
|
|
if (isa<ObjCMethodDecl>(CodeDecl))
|
|
os << "Method";
|
|
else
|
|
os << "Function";
|
|
|
|
os << " accepting ";
|
|
os << (isNSErrorWarning ? "NSError**" : "CFErrorRef*");
|
|
os << " should have a non-void return value to indicate whether or not an "
|
|
"error occurred";
|
|
|
|
BR.EmitBasicReport(isNSErrorWarning
|
|
? "Bad return type when passing NSError**"
|
|
: "Bad return type when passing CFError*",
|
|
getCategory().c_str(), os.str().c_str(),
|
|
CodeDecl.getLocation());
|
|
}
|
|
|
|
void
|
|
NSErrorCheck::CheckSignature(const ObjCMethodDecl& M, QualType& ResultTy,
|
|
llvm::SmallVectorImpl<VarDecl*>& ErrorParams) {
|
|
|
|
ResultTy = M.getResultType();
|
|
|
|
for (ObjCMethodDecl::param_iterator I=M.param_begin(),
|
|
E=M.param_end(); I!=E; ++I) {
|
|
|
|
QualType T = (*I)->getType();
|
|
|
|
if (isNSErrorWarning) {
|
|
if (CheckNSErrorArgument(T)) ErrorParams.push_back(*I);
|
|
}
|
|
else if (CheckCFErrorArgument(T))
|
|
ErrorParams.push_back(*I);
|
|
}
|
|
}
|
|
|
|
void
|
|
NSErrorCheck::CheckSignature(const FunctionDecl& F, QualType& ResultTy,
|
|
llvm::SmallVectorImpl<VarDecl*>& ErrorParams) {
|
|
|
|
ResultTy = F.getResultType();
|
|
|
|
for (FunctionDecl::param_const_iterator I = F.param_begin(),
|
|
E = F.param_end(); I != E; ++I) {
|
|
|
|
QualType T = (*I)->getType();
|
|
|
|
if (isNSErrorWarning) {
|
|
if (CheckNSErrorArgument(T)) ErrorParams.push_back(*I);
|
|
}
|
|
else if (CheckCFErrorArgument(T))
|
|
ErrorParams.push_back(*I);
|
|
}
|
|
}
|
|
|
|
|
|
bool NSErrorCheck::CheckNSErrorArgument(QualType ArgTy) {
|
|
|
|
const PointerType* PPT = ArgTy->getAs<PointerType>();
|
|
if (!PPT)
|
|
return false;
|
|
|
|
const ObjCObjectPointerType* PT =
|
|
PPT->getPointeeType()->getAs<ObjCObjectPointerType>();
|
|
|
|
if (!PT)
|
|
return false;
|
|
|
|
const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
|
|
|
|
// FIXME: Can ID ever be NULL?
|
|
if (ID)
|
|
return II == ID->getIdentifier();
|
|
|
|
return false;
|
|
}
|
|
|
|
bool NSErrorCheck::CheckCFErrorArgument(QualType ArgTy) {
|
|
|
|
const PointerType* PPT = ArgTy->getAs<PointerType>();
|
|
if (!PPT) return false;
|
|
|
|
const TypedefType* TT = PPT->getPointeeType()->getAs<TypedefType>();
|
|
if (!TT) return false;
|
|
|
|
return TT->getDecl()->getIdentifier() == II;
|
|
}
|
|
|
|
void NSErrorCheck::CheckParamDeref(const VarDecl *Param,
|
|
const LocationContext *LC,
|
|
const GRState *rootState,
|
|
BugReporter& BR) {
|
|
|
|
SVal ParamL = rootState->getLValue(Param, LC);
|
|
const MemRegion* ParamR = cast<loc::MemRegionVal>(ParamL).getRegionAs<VarRegion>();
|
|
assert (ParamR && "Parameters always have VarRegions.");
|
|
SVal ParamSVal = rootState->getSVal(ParamR);
|
|
|
|
// FIXME: For now assume that ParamSVal is symbolic. We need to generalize
|
|
// this later.
|
|
SymbolRef ParamSym = ParamSVal.getAsLocSymbol();
|
|
if (!ParamSym)
|
|
return;
|
|
|
|
// Iterate over the implicit-null dereferences.
|
|
for (GRExprEngine::null_deref_iterator I=Eng.implicit_null_derefs_begin(),
|
|
E=Eng.implicit_null_derefs_end(); I!=E; ++I) {
|
|
|
|
const GRState *state = (*I)->getState();
|
|
const SVal* X = state->get<GRState::NullDerefTag>();
|
|
|
|
if (!X || X->getAsSymbol() != ParamSym)
|
|
continue;
|
|
|
|
// Emit an error.
|
|
std::string sbuf;
|
|
llvm::raw_string_ostream os(sbuf);
|
|
os << "Potential null dereference. According to coding standards ";
|
|
|
|
if (isNSErrorWarning)
|
|
os << "in 'Creating and Returning NSError Objects' the parameter '";
|
|
else
|
|
os << "documented in CoreFoundation/CFError.h the parameter '";
|
|
|
|
os << Param->getNameAsString() << "' may be null.";
|
|
|
|
BugReport *report = new BugReport(*this, os.str().c_str(), *I);
|
|
// FIXME: Notable symbols are now part of the report. We should
|
|
// add support for notable symbols in BugReport.
|
|
// BR.addNotableSymbol(SV->getSymbol());
|
|
BR.EmitReport(report);
|
|
}
|
|
}
|