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