IssueHash.cpp 6.3 KB

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