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