IssueHash.cpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  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 <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(llvm::Optional<llvm::MemoryBufferRef> Buffer,
  106. int Line) {
  107. if (!Buffer)
  108. return "";
  109. llvm::line_iterator LI(*Buffer, false);
  110. for (; !LI.is_at_eof() && LI.line_number() != Line; ++LI)
  111. ;
  112. return *LI;
  113. }
  114. static std::string NormalizeLine(const SourceManager &SM, const FullSourceLoc &L,
  115. const LangOptions &LangOpts) {
  116. static StringRef Whitespaces = " \t\n";
  117. StringRef Str = GetNthLineOfFile(SM.getBufferOrNone(L.getFileID(), L),
  118. L.getExpansionLineNumber());
  119. StringRef::size_type col = Str.find_first_not_of(Whitespaces);
  120. if (col == StringRef::npos)
  121. col = 1; // The line only contains whitespace.
  122. else
  123. col++;
  124. SourceLocation StartOfLine =
  125. SM.translateLineCol(SM.getFileID(L), L.getExpansionLineNumber(), col);
  126. Optional<llvm::MemoryBufferRef> Buffer =
  127. SM.getBufferOrNone(SM.getFileID(StartOfLine), StartOfLine);
  128. if (!Buffer)
  129. return {};
  130. const char *BufferPos = SM.getCharacterData(StartOfLine);
  131. Token Token;
  132. Lexer Lexer(SM.getLocForStartOfFile(SM.getFileID(StartOfLine)), LangOpts,
  133. Buffer->getBufferStart(), BufferPos, Buffer->getBufferEnd());
  134. size_t NextStart = 0;
  135. std::ostringstream LineBuff;
  136. while (!Lexer.LexFromRawLexer(Token) && NextStart < 2) {
  137. if (Token.isAtStartOfLine() && NextStart++ > 0)
  138. continue;
  139. LineBuff << std::string(SM.getCharacterData(Token.getLocation()),
  140. Token.getLength());
  141. }
  142. return LineBuff.str();
  143. }
  144. static llvm::SmallString<32> GetMD5HashOfContent(StringRef Content) {
  145. llvm::MD5 Hash;
  146. llvm::MD5::MD5Result MD5Res;
  147. SmallString<32> Res;
  148. Hash.update(Content);
  149. Hash.final(MD5Res);
  150. llvm::MD5::stringifyResult(MD5Res, Res);
  151. return Res;
  152. }
  153. std::string clang::getIssueString(const FullSourceLoc &IssueLoc,
  154. StringRef CheckerName,
  155. StringRef WarningMessage,
  156. const Decl *IssueDecl,
  157. const LangOptions &LangOpts) {
  158. static StringRef Delimiter = "$";
  159. return (llvm::Twine(CheckerName) + Delimiter +
  160. GetEnclosingDeclContextSignature(IssueDecl) + Delimiter +
  161. Twine(IssueLoc.getExpansionColumnNumber()) + Delimiter +
  162. NormalizeLine(IssueLoc.getManager(), IssueLoc, LangOpts) +
  163. Delimiter + WarningMessage)
  164. .str();
  165. }
  166. SmallString<32> clang::getIssueHash(const FullSourceLoc &IssueLoc,
  167. StringRef CheckerName,
  168. StringRef WarningMessage,
  169. const Decl *IssueDecl,
  170. const LangOptions &LangOpts) {
  171. return GetMD5HashOfContent(getIssueString(
  172. IssueLoc, CheckerName, WarningMessage, IssueDecl, LangOpts));
  173. }