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