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