Path: blob/main/contrib/llvm-project/clang/lib/Analysis/IssueHash.cpp
35233 views
//===---------- IssueHash.cpp - Generate identification hashes --*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "clang/Analysis/IssueHash.h"9#include "clang/AST/ASTContext.h"10#include "clang/AST/Decl.h"11#include "clang/AST/DeclCXX.h"12#include "clang/Basic/SourceManager.h"13#include "clang/Basic/Specifiers.h"14#include "clang/Lex/Lexer.h"15#include "llvm/ADT/StringExtras.h"16#include "llvm/ADT/StringRef.h"17#include "llvm/ADT/Twine.h"18#include "llvm/Support/LineIterator.h"19#include "llvm/Support/MD5.h"20#include "llvm/Support/Path.h"2122#include <functional>23#include <optional>24#include <sstream>25#include <string>2627using namespace clang;2829// Get a string representation of the parts of the signature that can be30// overloaded on.31static std::string GetSignature(const FunctionDecl *Target) {32if (!Target)33return "";34std::string Signature;3536// When a flow sensitive bug happens in templated code we should not generate37// distinct hash value for every instantiation. Use the signature from the38// primary template.39if (const FunctionDecl *InstantiatedFrom =40Target->getTemplateInstantiationPattern())41Target = InstantiatedFrom;4243if (!isa<CXXConstructorDecl>(Target) && !isa<CXXDestructorDecl>(Target) &&44!isa<CXXConversionDecl>(Target))45Signature.append(Target->getReturnType().getAsString()).append(" ");46Signature.append(Target->getQualifiedNameAsString()).append("(");4748for (int i = 0, paramsCount = Target->getNumParams(); i < paramsCount; ++i) {49if (i)50Signature.append(", ");51Signature.append(Target->getParamDecl(i)->getType().getAsString());52}5354if (Target->isVariadic())55Signature.append(", ...");56Signature.append(")");5758const auto *TargetT =59llvm::dyn_cast_or_null<FunctionType>(Target->getType().getTypePtr());6061if (!TargetT || !isa<CXXMethodDecl>(Target))62return Signature;6364if (TargetT->isConst())65Signature.append(" const");66if (TargetT->isVolatile())67Signature.append(" volatile");68if (TargetT->isRestrict())69Signature.append(" restrict");7071if (const auto *TargetPT =72dyn_cast_or_null<FunctionProtoType>(Target->getType().getTypePtr())) {73switch (TargetPT->getRefQualifier()) {74case RQ_LValue:75Signature.append(" &");76break;77case RQ_RValue:78Signature.append(" &&");79break;80default:81break;82}83}8485return Signature;86}8788static std::string GetEnclosingDeclContextSignature(const Decl *D) {89if (!D)90return "";9192if (const auto *ND = dyn_cast<NamedDecl>(D)) {93std::string DeclName;9495switch (ND->getKind()) {96case Decl::Namespace:97case Decl::Record:98case Decl::CXXRecord:99case Decl::Enum:100DeclName = ND->getQualifiedNameAsString();101break;102case Decl::CXXConstructor:103case Decl::CXXDestructor:104case Decl::CXXConversion:105case Decl::CXXMethod:106case Decl::Function:107DeclName = GetSignature(dyn_cast_or_null<FunctionDecl>(ND));108break;109case Decl::ObjCMethod:110// ObjC Methods can not be overloaded, qualified name uniquely identifies111// the method.112DeclName = ND->getQualifiedNameAsString();113break;114default:115break;116}117118return DeclName;119}120121return "";122}123124static StringRef GetNthLineOfFile(std::optional<llvm::MemoryBufferRef> Buffer,125int Line) {126if (!Buffer)127return "";128129llvm::line_iterator LI(*Buffer, false);130for (; !LI.is_at_eof() && LI.line_number() != Line; ++LI)131;132133return *LI;134}135136static std::string NormalizeLine(const SourceManager &SM, const FullSourceLoc &L,137const LangOptions &LangOpts) {138static StringRef Whitespaces = " \t\n";139140StringRef Str = GetNthLineOfFile(SM.getBufferOrNone(L.getFileID(), L),141L.getExpansionLineNumber());142StringRef::size_type col = Str.find_first_not_of(Whitespaces);143if (col == StringRef::npos)144col = 1; // The line only contains whitespace.145else146col++;147SourceLocation StartOfLine =148SM.translateLineCol(SM.getFileID(L), L.getExpansionLineNumber(), col);149std::optional<llvm::MemoryBufferRef> Buffer =150SM.getBufferOrNone(SM.getFileID(StartOfLine), StartOfLine);151if (!Buffer)152return {};153154const char *BufferPos = SM.getCharacterData(StartOfLine);155156Token Token;157Lexer Lexer(SM.getLocForStartOfFile(SM.getFileID(StartOfLine)), LangOpts,158Buffer->getBufferStart(), BufferPos, Buffer->getBufferEnd());159160size_t NextStart = 0;161std::ostringstream LineBuff;162while (!Lexer.LexFromRawLexer(Token) && NextStart < 2) {163if (Token.isAtStartOfLine() && NextStart++ > 0)164continue;165LineBuff << std::string(SM.getCharacterData(Token.getLocation()),166Token.getLength());167}168169return LineBuff.str();170}171172static llvm::SmallString<32> GetMD5HashOfContent(StringRef Content) {173llvm::MD5 Hash;174llvm::MD5::MD5Result MD5Res;175SmallString<32> Res;176177Hash.update(Content);178Hash.final(MD5Res);179llvm::MD5::stringifyResult(MD5Res, Res);180181return Res;182}183184std::string clang::getIssueString(const FullSourceLoc &IssueLoc,185StringRef CheckerName,186StringRef WarningMessage,187const Decl *IssueDecl,188const LangOptions &LangOpts) {189static StringRef Delimiter = "$";190191return (llvm::Twine(CheckerName) + Delimiter +192GetEnclosingDeclContextSignature(IssueDecl) + Delimiter +193Twine(IssueLoc.getExpansionColumnNumber()) + Delimiter +194NormalizeLine(IssueLoc.getManager(), IssueLoc, LangOpts) +195Delimiter + WarningMessage)196.str();197}198199SmallString<32> clang::getIssueHash(const FullSourceLoc &IssueLoc,200StringRef CheckerName,201StringRef WarningMessage,202const Decl *IssueDecl,203const LangOptions &LangOpts) {204205return GetMD5HashOfContent(getIssueString(206IssueLoc, CheckerName, WarningMessage, IssueDecl, LangOpts));207}208209210