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
28using namespace clang;
29
30// Get a string representation of the parts of the signature that can be
31// overloaded on.
32static 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
82static 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
118static 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
129static 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
165static 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
177std::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
191SmallString<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