brintos

brintos / llvm-project-archived public Read only

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