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