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