205 lines · cpp
1//===---------- IssueHash.cpp - Generate identification hashes --*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "clang/Analysis/IssueHash.h"10#include "clang/AST/ASTContext.h"11#include "clang/AST/Decl.h"12#include "clang/AST/DeclCXX.h"13#include "clang/Basic/SourceManager.h"14#include "clang/Lex/Lexer.h"15#include "llvm/ADT/StringRef.h"16#include "llvm/ADT/Twine.h"17#include "llvm/Support/LineIterator.h"18#include "llvm/Support/MD5.h"19 20#include <optional>21#include <sstream>22#include <string>23 24using namespace clang;25 26// Get a string representation of the parts of the signature that can be27// overloaded on.28static std::string GetSignature(const FunctionDecl *Target) {29 if (!Target)30 return "";31 std::string Signature;32 33 // When a flow sensitive bug happens in templated code we should not generate34 // distinct hash value for every instantiation. Use the signature from the35 // primary template.36 if (const FunctionDecl *InstantiatedFrom =37 Target->getTemplateInstantiationPattern())38 Target = InstantiatedFrom;39 40 if (!isa<CXXConstructorDecl>(Target) && !isa<CXXDestructorDecl>(Target) &&41 !isa<CXXConversionDecl>(Target))42 Signature.append(Target->getReturnType().getAsString()).append(" ");43 Signature.append(Target->getQualifiedNameAsString()).append("(");44 45 for (int i = 0, paramsCount = Target->getNumParams(); i < paramsCount; ++i) {46 if (i)47 Signature.append(", ");48 Signature.append(Target->getParamDecl(i)->getType().getAsString());49 }50 51 if (Target->isVariadic())52 Signature.append(", ...");53 Signature.append(")");54 55 const auto *TargetT =56 llvm::dyn_cast_or_null<FunctionType>(Target->getType().getTypePtr());57 58 if (!TargetT || !isa<CXXMethodDecl>(Target))59 return Signature;60 61 if (TargetT->isConst())62 Signature.append(" const");63 if (TargetT->isVolatile())64 Signature.append(" volatile");65 if (TargetT->isRestrict())66 Signature.append(" restrict");67 68 if (const auto *TargetPT =69 dyn_cast_or_null<FunctionProtoType>(Target->getType().getTypePtr())) {70 switch (TargetPT->getRefQualifier()) {71 case RQ_LValue:72 Signature.append(" &");73 break;74 case RQ_RValue:75 Signature.append(" &&");76 break;77 default:78 break;79 }80 }81 82 return Signature;83}84 85static std::string GetEnclosingDeclContextSignature(const Decl *D) {86 if (!D)87 return "";88 89 if (const auto *ND = dyn_cast<NamedDecl>(D)) {90 std::string DeclName;91 92 switch (ND->getKind()) {93 case Decl::Namespace:94 case Decl::Record:95 case Decl::CXXRecord:96 case Decl::Enum:97 DeclName = ND->getQualifiedNameAsString();98 break;99 case Decl::CXXConstructor:100 case Decl::CXXDestructor:101 case Decl::CXXConversion:102 case Decl::CXXMethod:103 case Decl::Function:104 DeclName = GetSignature(dyn_cast_or_null<FunctionDecl>(ND));105 break;106 case Decl::ObjCMethod:107 // ObjC Methods can not be overloaded, qualified name uniquely identifies108 // the method.109 DeclName = ND->getQualifiedNameAsString();110 break;111 default:112 break;113 }114 115 return DeclName;116 }117 118 return "";119}120 121static StringRef GetNthLineOfFile(std::optional<llvm::MemoryBufferRef> Buffer,122 int Line) {123 if (!Buffer)124 return "";125 126 llvm::line_iterator LI(*Buffer, false);127 for (; !LI.is_at_eof() && LI.line_number() != Line; ++LI)128 ;129 130 return *LI;131}132 133static std::string NormalizeLine(const SourceManager &SM, const FullSourceLoc &L,134 const LangOptions &LangOpts) {135 static StringRef Whitespaces = " \t\n";136 137 StringRef Str = GetNthLineOfFile(SM.getBufferOrNone(L.getFileID(), L),138 L.getExpansionLineNumber());139 StringRef::size_type col = Str.find_first_not_of(Whitespaces);140 if (col == StringRef::npos)141 col = 1; // The line only contains whitespace.142 else143 col++;144 SourceLocation StartOfLine =145 SM.translateLineCol(SM.getFileID(L), L.getExpansionLineNumber(), col);146 std::optional<llvm::MemoryBufferRef> Buffer =147 SM.getBufferOrNone(SM.getFileID(StartOfLine), StartOfLine);148 if (!Buffer)149 return {};150 151 const char *BufferPos = SM.getCharacterData(StartOfLine);152 153 Token Token;154 Lexer Lexer(SM.getLocForStartOfFile(SM.getFileID(StartOfLine)), LangOpts,155 Buffer->getBufferStart(), BufferPos, Buffer->getBufferEnd());156 157 size_t NextStart = 0;158 std::ostringstream LineBuff;159 while (!Lexer.LexFromRawLexer(Token) && NextStart < 2) {160 if (Token.isAtStartOfLine() && NextStart++ > 0)161 continue;162 LineBuff << std::string(SM.getCharacterData(Token.getLocation()),163 Token.getLength());164 }165 166 return LineBuff.str();167}168 169static llvm::SmallString<32> GetMD5HashOfContent(StringRef Content) {170 llvm::MD5 Hash;171 llvm::MD5::MD5Result MD5Res;172 SmallString<32> Res;173 174 Hash.update(Content);175 Hash.final(MD5Res);176 llvm::MD5::stringifyResult(MD5Res, Res);177 178 return Res;179}180 181std::string clang::getIssueString(const FullSourceLoc &IssueLoc,182 StringRef CheckerName,183 StringRef WarningMessage,184 const Decl *IssueDecl,185 const LangOptions &LangOpts) {186 static StringRef Delimiter = "$";187 188 return (llvm::Twine(CheckerName) + Delimiter +189 GetEnclosingDeclContextSignature(IssueDecl) + Delimiter +190 Twine(IssueLoc.getExpansionColumnNumber()) + Delimiter +191 NormalizeLine(IssueLoc.getManager(), IssueLoc, LangOpts) +192 Delimiter + WarningMessage)193 .str();194}195 196SmallString<32> clang::getIssueHash(const FullSourceLoc &IssueLoc,197 StringRef CheckerName,198 StringRef WarningMessage,199 const Decl *IssueDecl,200 const LangOptions &LangOpts) {201 202 return GetMD5HashOfContent(getIssueString(203 IssueLoc, CheckerName, WarningMessage, IssueDecl, LangOpts));204}205