xref: /netbsd-src/external/apache2/llvm/dist/clang/lib/Analysis/IssueHash.cpp (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
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