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/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 24 using namespace clang; 25 26 // Get a string representation of the parts of the signature that can be 27 // overloaded on. 28 static 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 generate 34 // distinct hash value for every instantiation. Use the signature from the 35 // 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 85 static 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 identifies 108 // the method. 109 DeclName = ND->getQualifiedNameAsString(); 110 break; 111 default: 112 break; 113 } 114 115 return DeclName; 116 } 117 118 return ""; 119 } 120 121 static 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 133 static 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 else 143 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 169 static 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 181 std::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 196 SmallString<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