Move identification of memory setting and copying functions (memset,

memcmp, strncmp,..) out of Sema and into FunctionDecl so that the logic
could be reused in the analyzer.

llvm-svn: 148142
This commit is contained in:
Anna Zaks 2012-01-13 21:52:01 +00:00
parent d749c6bf2e
commit 201d489cb8
4 changed files with 120 additions and 103 deletions

View File

@ -1968,6 +1968,27 @@ public:
/// definition of a member function. /// definition of a member function.
virtual bool isOutOfLine() const; virtual bool isOutOfLine() const;
/// \brief Enumeration used to identify memory setting or copying functions
/// identified by getMemoryFunctionKind().
enum MemoryFunctionKind {
MFK_Memset,
MFK_Memcpy,
MFK_Memmove,
MFK_Memcmp,
MFK_Strncpy,
MFK_Strncmp,
MFK_Strncasecmp,
MFK_Strncat,
MFK_Strndup,
MFK_Strlcpy,
MFK_Strlcat,
MFK_Invalid
};
/// \brief If the given function is a memory copy or setting function, return
/// it's kind. If the function is not a memory function, returns MFK_Invalid.
MemoryFunctionKind getMemoryFunctionKind();
// Implement isa/cast/dyncast/etc. // Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classof(const FunctionDecl *D) { return true; } static bool classof(const FunctionDecl *D) { return true; }

View File

@ -6311,21 +6311,8 @@ private:
unsigned format_idx, unsigned firstDataArg, unsigned format_idx, unsigned firstDataArg,
bool isPrintf); bool isPrintf);
/// \brief Enumeration used to describe which of the memory setting or copying void CheckMemaccessArguments(const CallExpr *Call,
/// functions is being checked by \c CheckMemaccessArguments(). FunctionDecl::MemoryFunctionKind CMF,
enum CheckedMemoryFunction {
CMF_Memset,
CMF_Memcpy,
CMF_Memmove,
CMF_Memcmp,
CMF_Strncpy,
CMF_Strncmp,
CMF_Strncasecmp,
CMF_Strncat,
CMF_Strndup
};
void CheckMemaccessArguments(const CallExpr *Call, CheckedMemoryFunction CMF,
IdentifierInfo *FnName); IdentifierInfo *FnName);
void CheckStrlcpycatArguments(const CallExpr *Call, void CheckStrlcpycatArguments(const CallExpr *Call,

View File

@ -2302,6 +2302,83 @@ SourceRange FunctionDecl::getSourceRange() const {
return SourceRange(getOuterLocStart(), EndRangeLoc); return SourceRange(getOuterLocStart(), EndRangeLoc);
} }
FunctionDecl::MemoryFunctionKind FunctionDecl::getMemoryFunctionKind() {
IdentifierInfo *FnInfo = getIdentifier();
if (!FnInfo)
return MFK_Invalid;
// Builtin handling.
switch (getBuiltinID()) {
case Builtin::BI__builtin_memset:
case Builtin::BI__builtin___memset_chk:
case Builtin::BImemset:
return MFK_Memset;
case Builtin::BI__builtin_memcpy:
case Builtin::BI__builtin___memcpy_chk:
case Builtin::BImemcpy:
return MFK_Memcpy;
case Builtin::BI__builtin_memmove:
case Builtin::BI__builtin___memmove_chk:
case Builtin::BImemmove:
return MFK_Memmove;
case Builtin::BIstrlcpy:
return MFK_Strlcpy;
case Builtin::BIstrlcat:
return MFK_Strlcat;
case Builtin::BI__builtin_memcmp:
return MFK_Memcmp;
case Builtin::BI__builtin_strncpy:
case Builtin::BI__builtin___strncpy_chk:
case Builtin::BIstrncpy:
return MFK_Strncpy;
case Builtin::BI__builtin_strncmp:
return MFK_Strncmp;
case Builtin::BI__builtin_strncasecmp:
return MFK_Strncasecmp;
case Builtin::BI__builtin_strncat:
case Builtin::BIstrncat:
return MFK_Strncat;
case Builtin::BI__builtin_strndup:
case Builtin::BIstrndup:
return MFK_Strndup;
default:
if (getLinkage() == ExternalLinkage &&
(!getASTContext().getLangOptions().CPlusPlus || isExternC())) {
if (FnInfo->isStr("memset"))
return MFK_Memset;
else if (FnInfo->isStr("memcpy"))
return MFK_Memcpy;
else if (FnInfo->isStr("memmove"))
return MFK_Memmove;
else if (FnInfo->isStr("memcmp"))
return MFK_Memcmp;
else if (FnInfo->isStr("strncpy"))
return MFK_Strncpy;
else if (FnInfo->isStr("strncmp"))
return MFK_Strncmp;
else if (FnInfo->isStr("strncasecmp"))
return MFK_Strncasecmp;
else if (FnInfo->isStr("strncat"))
return MFK_Strncat;
else if (FnInfo->isStr("strndup"))
return MFK_Strndup;
}
break;
}
return MFK_Invalid;
}
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// FieldDecl Implementation // FieldDecl Implementation
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//

View File

@ -480,88 +480,15 @@ bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall) {
TheCall->getCallee()->getLocStart()); TheCall->getCallee()->getLocStart());
} }
// Builtin handling FunctionDecl::MemoryFunctionKind CMF = FDecl->getMemoryFunctionKind();
int CMF = -1; if (CMF == FunctionDecl::MFK_Invalid)
switch (FDecl->getBuiltinID()) { return false;
case Builtin::BI__builtin_memset:
case Builtin::BI__builtin___memset_chk:
case Builtin::BImemset:
CMF = CMF_Memset;
break;
case Builtin::BI__builtin_memcpy:
case Builtin::BI__builtin___memcpy_chk:
case Builtin::BImemcpy:
CMF = CMF_Memcpy;
break;
case Builtin::BI__builtin_memmove:
case Builtin::BI__builtin___memmove_chk:
case Builtin::BImemmove:
CMF = CMF_Memmove;
break;
case Builtin::BIstrlcpy: // Handle memory setting and copying functions.
case Builtin::BIstrlcat: if (CMF == FunctionDecl::MFK_Strlcpy || CMF == FunctionDecl::MFK_Strlcat)
CheckStrlcpycatArguments(TheCall, FnInfo); CheckStrlcpycatArguments(TheCall, FnInfo);
break; else
CheckMemaccessArguments(TheCall, CMF, FnInfo);
case Builtin::BI__builtin_memcmp:
CMF = CMF_Memcmp;
break;
case Builtin::BI__builtin_strncpy:
case Builtin::BI__builtin___strncpy_chk:
case Builtin::BIstrncpy:
CMF = CMF_Strncpy;
break;
case Builtin::BI__builtin_strncmp:
CMF = CMF_Strncmp;
break;
case Builtin::BI__builtin_strncasecmp:
CMF = CMF_Strncasecmp;
break;
case Builtin::BI__builtin_strncat:
case Builtin::BIstrncat:
CMF = CMF_Strncat;
break;
case Builtin::BI__builtin_strndup:
case Builtin::BIstrndup:
CMF = CMF_Strndup;
break;
default:
if (FDecl->getLinkage() == ExternalLinkage &&
(!getLangOptions().CPlusPlus || FDecl->isExternC())) {
if (FnInfo->isStr("memset"))
CMF = CMF_Memset;
else if (FnInfo->isStr("memcpy"))
CMF = CMF_Memcpy;
else if (FnInfo->isStr("memmove"))
CMF = CMF_Memmove;
else if (FnInfo->isStr("memcmp"))
CMF = CMF_Memcmp;
else if (FnInfo->isStr("strncpy"))
CMF = CMF_Strncpy;
else if (FnInfo->isStr("strncmp"))
CMF = CMF_Strncmp;
else if (FnInfo->isStr("strncasecmp"))
CMF = CMF_Strncasecmp;
else if (FnInfo->isStr("strncat"))
CMF = CMF_Strncat;
else if (FnInfo->isStr("strndup"))
CMF = CMF_Strndup;
}
break;
}
// Memset/memcpy/memmove handling
if (CMF != -1)
CheckMemaccessArguments(TheCall, CheckedMemoryFunction(CMF), FnInfo);
return false; return false;
} }
@ -2500,16 +2427,17 @@ static QualType getSizeOfArgType(const Expr* E) {
/// ///
/// \param Call The call expression to diagnose. /// \param Call The call expression to diagnose.
void Sema::CheckMemaccessArguments(const CallExpr *Call, void Sema::CheckMemaccessArguments(const CallExpr *Call,
CheckedMemoryFunction CMF, FunctionDecl::MemoryFunctionKind CMF,
IdentifierInfo *FnName) { IdentifierInfo *FnName) {
// It is possible to have a non-standard definition of memset. Validate // It is possible to have a non-standard definition of memset. Validate
// we have enough arguments, and if not, abort further checking. // we have enough arguments, and if not, abort further checking.
unsigned ExpectedNumArgs = (CMF == CMF_Strndup ? 2 : 3); unsigned ExpectedNumArgs = (CMF == FunctionDecl::MFK_Strndup ? 2 : 3);
if (Call->getNumArgs() < ExpectedNumArgs) if (Call->getNumArgs() < ExpectedNumArgs)
return; return;
unsigned LastArg = (CMF == CMF_Memset || CMF == CMF_Strndup ? 1 : 2); unsigned LastArg = (CMF == FunctionDecl::MFK_Memset ||
unsigned LenArg = (CMF == CMF_Strndup ? 1 : 2); CMF == FunctionDecl::MFK_Strndup ? 1 : 2);
unsigned LenArg = (CMF == FunctionDecl::MFK_Strndup ? 1 : 2);
const Expr *LenExpr = Call->getArg(LenArg)->IgnoreParenImpCasts(); const Expr *LenExpr = Call->getArg(LenArg)->IgnoreParenImpCasts();
// We have special checking when the length is a sizeof expression. // We have special checking when the length is a sizeof expression.
@ -2553,7 +2481,8 @@ void Sema::CheckMemaccessArguments(const CallExpr *Call,
if (Context.getTypeSize(PointeeTy) == Context.getCharWidth()) if (Context.getTypeSize(PointeeTy) == Context.getCharWidth())
ActionIdx = 2; // If the pointee's size is sizeof(char), ActionIdx = 2; // If the pointee's size is sizeof(char),
// suggest an explicit length. // suggest an explicit length.
unsigned DestSrcSelect = (CMF == CMF_Strndup ? 1 : ArgIdx); unsigned DestSrcSelect =
(CMF == FunctionDecl::MFK_Strndup ? 1 : ArgIdx);
DiagRuntimeBehavior(SizeOfArg->getExprLoc(), Dest, DiagRuntimeBehavior(SizeOfArg->getExprLoc(), Dest,
PDiag(diag::warn_sizeof_pointer_expr_memaccess) PDiag(diag::warn_sizeof_pointer_expr_memaccess)
<< FnName << DestSrcSelect << ActionIdx << FnName << DestSrcSelect << ActionIdx
@ -2583,12 +2512,15 @@ void Sema::CheckMemaccessArguments(const CallExpr *Call,
DiagRuntimeBehavior( DiagRuntimeBehavior(
Dest->getExprLoc(), Dest, Dest->getExprLoc(), Dest,
PDiag(diag::warn_dyn_class_memaccess) PDiag(diag::warn_dyn_class_memaccess)
<< (CMF == CMF_Memcmp ? ArgIdx + 2 : ArgIdx) << FnName << PointeeTy << (CMF == FunctionDecl::MFK_Memcmp ? ArgIdx + 2 : ArgIdx)
<< FnName << PointeeTy
// "overwritten" if we're warning about the destination for any call // "overwritten" if we're warning about the destination for any call
// but memcmp; otherwise a verb appropriate to the call. // but memcmp; otherwise a verb appropriate to the call.
<< (ArgIdx == 0 && CMF != CMF_Memcmp ? 0 : (unsigned)CMF) << (ArgIdx == 0 &&
CMF != FunctionDecl::MFK_Memcmp ? 0 : (unsigned)CMF)
<< Call->getCallee()->getSourceRange()); << Call->getCallee()->getSourceRange());
else if (PointeeTy.hasNonTrivialObjCLifetime() && CMF != CMF_Memset) else if (PointeeTy.hasNonTrivialObjCLifetime() &&
CMF != FunctionDecl::MFK_Memset)
DiagRuntimeBehavior( DiagRuntimeBehavior(
Dest->getExprLoc(), Dest, Dest->getExprLoc(), Dest,
PDiag(diag::warn_arc_object_memaccess) PDiag(diag::warn_arc_object_memaccess)