blob: 94816747668da9704487330e7da703463a22d195 [file] [log] [blame]
//===---------- IssueHash.cpp - Generate identification hashes --*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "clang/Analysis/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::Optional<llvm::MemoryBufferRef> 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, const FullSourceLoc &L,
const LangOptions &LangOpts) {
static StringRef Whitespaces = " \t\n";
StringRef Str = GetNthLineOfFile(SM.getBufferOrNone(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);
Optional<llvm::MemoryBufferRef> Buffer =
SM.getBufferOrNone(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> GetMD5HashOfContent(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 FullSourceLoc &IssueLoc,
StringRef CheckerName,
StringRef WarningMessage,
const Decl *IssueDecl,
const LangOptions &LangOpts) {
static StringRef Delimiter = "$";
return (llvm::Twine(CheckerName) + Delimiter +
GetEnclosingDeclContextSignature(IssueDecl) + Delimiter +
Twine(IssueLoc.getExpansionColumnNumber()) + Delimiter +
NormalizeLine(IssueLoc.getManager(), IssueLoc, LangOpts) +
Delimiter + WarningMessage)
.str();
}
SmallString<32> clang::getIssueHash(const FullSourceLoc &IssueLoc,
StringRef CheckerName,
StringRef WarningMessage,
const Decl *IssueDecl,
const LangOptions &LangOpts) {
return GetMD5HashOfContent(getIssueString(
IssueLoc, CheckerName, WarningMessage, IssueDecl, LangOpts));
}