• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
28 using namespace clang;
29 
30 // Get a string representation of the parts of the signature that can be
31 // overloaded on.
GetSignature(const FunctionDecl * Target)32 static 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 
GetEnclosingDeclContextSignature(const Decl * D)82 static 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 
GetNthLineOfFile(llvm::MemoryBuffer * Buffer,int Line)118 static 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 
NormalizeLine(const SourceManager & SM,FullSourceLoc & L,const LangOptions & LangOpts)129 static 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 
GetHashOfContent(StringRef Content)165 static 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 
GetIssueString(const SourceManager & SM,FullSourceLoc & IssueLoc,StringRef CheckerName,StringRef BugType,const Decl * D,const LangOptions & LangOpts)177 std::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 
GetIssueHash(const SourceManager & SM,FullSourceLoc & IssueLoc,StringRef CheckerName,StringRef BugType,const Decl * D,const LangOptions & LangOpts)191 SmallString<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