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.
GetSignature(const FunctionDecl * Target)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
GetEnclosingDeclContextSignature(const Decl * D)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
GetNthLineOfFile(llvm::Optional<llvm::MemoryBufferRef> Buffer,int Line)124 static StringRef GetNthLineOfFile(llvm::Optional<llvm::MemoryBufferRef> Buffer,
125 int Line) {
126 if (!Buffer)
127 return "";
128
129 llvm::line_iterator LI(*Buffer, false);
130 for (; !LI.is_at_eof() && LI.line_number() != Line; ++LI)
131 ;
132
133 return *LI;
134 }
135
NormalizeLine(const SourceManager & SM,const FullSourceLoc & L,const LangOptions & LangOpts)136 static std::string NormalizeLine(const SourceManager &SM, const FullSourceLoc &L,
137 const LangOptions &LangOpts) {
138 static StringRef Whitespaces = " \t\n";
139
140 StringRef Str = GetNthLineOfFile(SM.getBufferOrNone(L.getFileID(), L),
141 L.getExpansionLineNumber());
142 StringRef::size_type col = Str.find_first_not_of(Whitespaces);
143 if (col == StringRef::npos)
144 col = 1; // The line only contains whitespace.
145 else
146 col++;
147 SourceLocation StartOfLine =
148 SM.translateLineCol(SM.getFileID(L), L.getExpansionLineNumber(), col);
149 Optional<llvm::MemoryBufferRef> Buffer =
150 SM.getBufferOrNone(SM.getFileID(StartOfLine), StartOfLine);
151 if (!Buffer)
152 return {};
153
154 const char *BufferPos = SM.getCharacterData(StartOfLine);
155
156 Token Token;
157 Lexer Lexer(SM.getLocForStartOfFile(SM.getFileID(StartOfLine)), LangOpts,
158 Buffer->getBufferStart(), BufferPos, Buffer->getBufferEnd());
159
160 size_t NextStart = 0;
161 std::ostringstream LineBuff;
162 while (!Lexer.LexFromRawLexer(Token) && NextStart < 2) {
163 if (Token.isAtStartOfLine() && NextStart++ > 0)
164 continue;
165 LineBuff << std::string(SM.getCharacterData(Token.getLocation()),
166 Token.getLength());
167 }
168
169 return LineBuff.str();
170 }
171
GetMD5HashOfContent(StringRef Content)172 static llvm::SmallString<32> GetMD5HashOfContent(StringRef Content) {
173 llvm::MD5 Hash;
174 llvm::MD5::MD5Result MD5Res;
175 SmallString<32> Res;
176
177 Hash.update(Content);
178 Hash.final(MD5Res);
179 llvm::MD5::stringifyResult(MD5Res, Res);
180
181 return Res;
182 }
183
getIssueString(const FullSourceLoc & IssueLoc,StringRef CheckerName,StringRef WarningMessage,const Decl * IssueDecl,const LangOptions & LangOpts)184 std::string clang::getIssueString(const FullSourceLoc &IssueLoc,
185 StringRef CheckerName,
186 StringRef WarningMessage,
187 const Decl *IssueDecl,
188 const LangOptions &LangOpts) {
189 static StringRef Delimiter = "$";
190
191 return (llvm::Twine(CheckerName) + Delimiter +
192 GetEnclosingDeclContextSignature(IssueDecl) + Delimiter +
193 Twine(IssueLoc.getExpansionColumnNumber()) + Delimiter +
194 NormalizeLine(IssueLoc.getManager(), IssueLoc, LangOpts) +
195 Delimiter + WarningMessage)
196 .str();
197 }
198
getIssueHash(const FullSourceLoc & IssueLoc,StringRef CheckerName,StringRef WarningMessage,const Decl * IssueDecl,const LangOptions & LangOpts)199 SmallString<32> clang::getIssueHash(const FullSourceLoc &IssueLoc,
200 StringRef CheckerName,
201 StringRef WarningMessage,
202 const Decl *IssueDecl,
203 const LangOptions &LangOpts) {
204
205 return GetMD5HashOfContent(getIssueString(
206 IssueLoc, CheckerName, WarningMessage, IssueDecl, LangOpts));
207 }
208