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