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