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