mirror of
https://github.com/llvm/llvm-project.git
synced 2025-05-17 10:06:07 +00:00

Now when a template is instantiated more times and there is a bug found in the instantiations the issue hash will be different for each instantiation even if every other property of the bug (path, message, location) is the same. This patch aims to resolve this issue. Note that explicit specializations still generate different hashes but that is intended. Differential Revision: https://reviews.llvm.org/D38728 llvm-svn: 316900
206 lines
6.1 KiB
C++
206 lines
6.1 KiB
C++
//===---------- IssueHash.cpp - Generate identification hashes --*- C++ -*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
#include "clang/StaticAnalyzer/Core/IssueHash.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/Decl.h"
|
|
#include "clang/AST/DeclCXX.h"
|
|
#include "clang/Basic/SourceManager.h"
|
|
#include "clang/Basic/Specifiers.h"
|
|
#include "clang/Lex/Lexer.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/ADT/Twine.h"
|
|
#include "llvm/Support/LineIterator.h"
|
|
#include "llvm/Support/MD5.h"
|
|
#include "llvm/Support/Path.h"
|
|
|
|
#include <functional>
|
|
#include <sstream>
|
|
#include <string>
|
|
|
|
using namespace clang;
|
|
|
|
// Get a string representation of the parts of the signature that can be
|
|
// overloaded on.
|
|
static std::string GetSignature(const FunctionDecl *Target) {
|
|
if (!Target)
|
|
return "";
|
|
std::string Signature;
|
|
|
|
// When a flow sensitive bug happens in templated code we should not generate
|
|
// distinct hash value for every instantiation. Use the signature from the
|
|
// primary template.
|
|
if (const FunctionDecl *InstantiatedFrom =
|
|
Target->getTemplateInstantiationPattern())
|
|
Target = InstantiatedFrom;
|
|
|
|
if (!isa<CXXConstructorDecl>(Target) && !isa<CXXDestructorDecl>(Target) &&
|
|
!isa<CXXConversionDecl>(Target))
|
|
Signature.append(Target->getReturnType().getAsString()).append(" ");
|
|
Signature.append(Target->getQualifiedNameAsString()).append("(");
|
|
|
|
for (int i = 0, paramsCount = Target->getNumParams(); i < paramsCount; ++i) {
|
|
if (i)
|
|
Signature.append(", ");
|
|
Signature.append(Target->getParamDecl(i)->getType().getAsString());
|
|
}
|
|
|
|
if (Target->isVariadic())
|
|
Signature.append(", ...");
|
|
Signature.append(")");
|
|
|
|
const auto *TargetT =
|
|
llvm::dyn_cast_or_null<FunctionType>(Target->getType().getTypePtr());
|
|
|
|
if (!TargetT || !isa<CXXMethodDecl>(Target))
|
|
return Signature;
|
|
|
|
if (TargetT->isConst())
|
|
Signature.append(" const");
|
|
if (TargetT->isVolatile())
|
|
Signature.append(" volatile");
|
|
if (TargetT->isRestrict())
|
|
Signature.append(" restrict");
|
|
|
|
if (const auto *TargetPT =
|
|
dyn_cast_or_null<FunctionProtoType>(Target->getType().getTypePtr())) {
|
|
switch (TargetPT->getRefQualifier()) {
|
|
case RQ_LValue:
|
|
Signature.append(" &");
|
|
break;
|
|
case RQ_RValue:
|
|
Signature.append(" &&");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return Signature;
|
|
}
|
|
|
|
static std::string GetEnclosingDeclContextSignature(const Decl *D) {
|
|
if (!D)
|
|
return "";
|
|
|
|
if (const auto *ND = dyn_cast<NamedDecl>(D)) {
|
|
std::string DeclName;
|
|
|
|
switch (ND->getKind()) {
|
|
case Decl::Namespace:
|
|
case Decl::Record:
|
|
case Decl::CXXRecord:
|
|
case Decl::Enum:
|
|
DeclName = ND->getQualifiedNameAsString();
|
|
break;
|
|
case Decl::CXXConstructor:
|
|
case Decl::CXXDestructor:
|
|
case Decl::CXXConversion:
|
|
case Decl::CXXMethod:
|
|
case Decl::Function:
|
|
DeclName = GetSignature(dyn_cast_or_null<FunctionDecl>(ND));
|
|
break;
|
|
case Decl::ObjCMethod:
|
|
// ObjC Methods can not be overloaded, qualified name uniquely identifies
|
|
// the method.
|
|
DeclName = ND->getQualifiedNameAsString();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return DeclName;
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
static StringRef GetNthLineOfFile(llvm::MemoryBuffer *Buffer, int Line) {
|
|
if (!Buffer)
|
|
return "";
|
|
|
|
llvm::line_iterator LI(*Buffer, false);
|
|
for (; !LI.is_at_eof() && LI.line_number() != Line; ++LI)
|
|
;
|
|
|
|
return *LI;
|
|
}
|
|
|
|
static std::string NormalizeLine(const SourceManager &SM, FullSourceLoc &L,
|
|
const LangOptions &LangOpts) {
|
|
static StringRef Whitespaces = " \t\n";
|
|
|
|
StringRef Str = GetNthLineOfFile(SM.getBuffer(L.getFileID(), L),
|
|
L.getExpansionLineNumber());
|
|
StringRef::size_type col = Str.find_first_not_of(Whitespaces);
|
|
if (col == StringRef::npos)
|
|
col = 1; // The line only contains whitespace.
|
|
else
|
|
col++;
|
|
SourceLocation StartOfLine =
|
|
SM.translateLineCol(SM.getFileID(L), L.getExpansionLineNumber(), col);
|
|
llvm::MemoryBuffer *Buffer =
|
|
SM.getBuffer(SM.getFileID(StartOfLine), StartOfLine);
|
|
if (!Buffer)
|
|
return {};
|
|
|
|
const char *BufferPos = SM.getCharacterData(StartOfLine);
|
|
|
|
Token Token;
|
|
Lexer Lexer(SM.getLocForStartOfFile(SM.getFileID(StartOfLine)), LangOpts,
|
|
Buffer->getBufferStart(), BufferPos, Buffer->getBufferEnd());
|
|
|
|
size_t NextStart = 0;
|
|
std::ostringstream LineBuff;
|
|
while (!Lexer.LexFromRawLexer(Token) && NextStart < 2) {
|
|
if (Token.isAtStartOfLine() && NextStart++ > 0)
|
|
continue;
|
|
LineBuff << std::string(SM.getCharacterData(Token.getLocation()),
|
|
Token.getLength());
|
|
}
|
|
|
|
return LineBuff.str();
|
|
}
|
|
|
|
static llvm::SmallString<32> GetHashOfContent(StringRef Content) {
|
|
llvm::MD5 Hash;
|
|
llvm::MD5::MD5Result MD5Res;
|
|
SmallString<32> Res;
|
|
|
|
Hash.update(Content);
|
|
Hash.final(MD5Res);
|
|
llvm::MD5::stringifyResult(MD5Res, Res);
|
|
|
|
return Res;
|
|
}
|
|
|
|
std::string clang::GetIssueString(const SourceManager &SM,
|
|
FullSourceLoc &IssueLoc,
|
|
StringRef CheckerName, StringRef BugType,
|
|
const Decl *D,
|
|
const LangOptions &LangOpts) {
|
|
static StringRef Delimiter = "$";
|
|
|
|
return (llvm::Twine(CheckerName) + Delimiter +
|
|
GetEnclosingDeclContextSignature(D) + Delimiter +
|
|
Twine(IssueLoc.getExpansionColumnNumber()) + Delimiter +
|
|
NormalizeLine(SM, IssueLoc, LangOpts) + Delimiter + BugType)
|
|
.str();
|
|
}
|
|
|
|
SmallString<32> clang::GetIssueHash(const SourceManager &SM,
|
|
FullSourceLoc &IssueLoc,
|
|
StringRef CheckerName, StringRef BugType,
|
|
const Decl *D,
|
|
const LangOptions &LangOpts) {
|
|
|
|
return GetHashOfContent(
|
|
GetIssueString(SM, IssueLoc, CheckerName, BugType, D, LangOpts));
|
|
}
|