例如,以下函数使用 Windows API 将文件加载到内存中,并使用在其他地方定义为
POLYNOMIAL
的多项式计算其 CRC32 校验和:
#include <Windows.h>
UINT32 WINAPI GetFileCRC32(WCHAR *wszFileName, LPDWORD lpdwError)
{
HANDLE hHeap = GetProcessHeap();
HANDLE hFile = INVALID_HANDLE_VALUE;
LARGE_INTEGER liSize;
DWORD dwError, dwRead;
SIZE_T cbAlloc, i, i2;
BYTE *bBuffer = NULL;
UINT32 uCRC = 0xFFFFFFFFU;
hFile = CreateFileW(wszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE == hFile)
{
dwError = GetLastError();
goto cleanup;
}
GetFileSizeEx(hFile, &liSize);
do
{
cbAlloc = min(liSize.QuadPart, ALLOC_MAX);
bBuffer = (BYTE *) HeapAlloc(hHeap, HEAP_ZERO_MEMORY, cbAlloc);
if (NULL == bBuffer)
{
dwError = ERROR_OUTOFMEMORY;
goto cleanup;
}
if (!ReadFile(hFile, bBuffer, cbAlloc, &dwRead, NULL))
{
dwError = GetLastError();
goto cleanup;
}
for(i = 0; i < cbAlloc; i++)
{
uCRC ^= bBuffer[i];
for (i2 = 0; i2 < 8; i2++)
{
uCRC = (uCRC >> 1) ^ ((uCRC & 1) ? POLYNOMIAL : 0);
}
}
HeapFree(hHeap, 0, bBuffer);
bBuffer = NULL;
liSize.QuadPart -= cbAlloc;
}
while(liSize.QuadPart);
dwError = ERROR_SUCCESS;
cleanup:
if (hFile != INVALID_HANDLE_VALUE)
{
CloseHandle(hFile);
hFile = INVALID_HANDLE_VALUE;
}
if (bBuffer != NULL)
{
HeapFree(hHeap, 0, bBuffer);
bBuffer = NULL;
}
*lpdwError = dwError;
return uCRC;
}
默认情况下,Clang-Tidy 不会将
bBuffer = NULL
行检测为死存储,我认为是因为它认识到将指针设置为 NULL
的做法是一种防御性编程做法,以防止双重释放错误。不过,我还在关闭后将 hFile
的值设置回 INVALID_HANDLE_VALUE
,作为同一模式的一部分。
但是,clang-tidy 确实抱怨函数末尾的
INVALID_HANDLE_VALUE
分配(这是强制转换为 -1
的数值 HANDLE
)作为“死存储”。
如何配置 clang-tidy 分析以接受
NULL
以外的值作为免于死存储分析的值?
不,无法配置
clang-tidy
deadcode.DeadStores
检查器忽略 nullptr
以外的值分配。
忽略
nullptr
赋值的代码位于
DeadStoresChecker.cpp:340
:
if (DeclRefExpr *DR = dyn_cast<DeclRefExpr>(B->getLHS()))
if (VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
// Special case: check for assigning null to a pointer.
// This is a common form of defensive programming.
const Expr *RHS =
LookThroughTransitiveAssignmentsAndCommaOperators(B->getRHS());
QualType T = VD->getType();
if (T.isVolatileQualified())
return;
if (T->isPointerType() || T->isObjCObjectPointerType()) {
===> if (RHS->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNull))
return;
}
// Special case: self-assignments. These are often used to shut up
// "unused variable" compiler warnings.
if (const DeclRefExpr *RhsDR = dyn_cast<DeclRefExpr>(RHS))
if (VD == dyn_cast<VarDecl>(RhsDR->getDecl()))
return;
// Otherwise, issue a warning.
DeadStoreKind dsk = Parents.isConsumedExpr(B)
? Enclosing
: (isIncrement(VD,B) ? DeadIncrement : Standard);
CheckVarDecl(VD, DR, B->getRHS(), dsk, Live);
}
Expr::isNullPointerConstant
检查 nullptr
,但这里没有任何灵活性允许
其他值。
在引用片段的末尾有一个对
CheckVarDecl
的调用,
它根据变量的类型进行更多的过滤
(例如,忽略引用)在发布报告之前。它
接受 RHS 表达式,但仅使用它来获取源位置
范围;没有根据分配的值进行过滤。
我看到的唯一半可行的解决方法是将作业包装在 调用恒等宏 (
#define ID(x) x
),因为检查器拒绝
报告宏扩展内的代码。