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