IssueHash.cpp 6.1 KB

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