1 //===---------- IssueHash.cpp - Generate identification hashes --*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
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"
28 using namespace clang
;
30 // Get a string representation of the parts of the signature that can be
32 static std::string
GetSignature(const FunctionDecl
*Target
) {
35 std::string Signature
;
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
40 if (const FunctionDecl
*InstantiatedFrom
=
41 Target
->getTemplateInstantiationPattern())
42 Target
= InstantiatedFrom
;
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("(");
49 for (int i
= 0, paramsCount
= Target
->getNumParams(); i
< paramsCount
; ++i
) {
51 Signature
.append(", ");
52 Signature
.append(Target
->getParamDecl(i
)->getType().getAsString());
55 if (Target
->isVariadic())
56 Signature
.append(", ...");
57 Signature
.append(")");
60 llvm::dyn_cast_or_null
<FunctionType
>(Target
->getType().getTypePtr());
62 if (!TargetT
|| !isa
<CXXMethodDecl
>(Target
))
65 if (TargetT
->isConst())
66 Signature
.append(" const");
67 if (TargetT
->isVolatile())
68 Signature
.append(" volatile");
69 if (TargetT
->isRestrict())
70 Signature
.append(" restrict");
72 if (const auto *TargetPT
=
73 dyn_cast_or_null
<FunctionProtoType
>(Target
->getType().getTypePtr())) {
74 switch (TargetPT
->getRefQualifier()) {
76 Signature
.append(" &");
79 Signature
.append(" &&");
89 static std::string
GetEnclosingDeclContextSignature(const Decl
*D
) {
93 if (const auto *ND
= dyn_cast
<NamedDecl
>(D
)) {
96 switch (ND
->getKind()) {
101 DeclName
= ND
->getQualifiedNameAsString();
103 case Decl::CXXConstructor
:
104 case Decl::CXXDestructor
:
105 case Decl::CXXConversion
:
106 case Decl::CXXMethod
:
108 DeclName
= GetSignature(dyn_cast_or_null
<FunctionDecl
>(ND
));
110 case Decl::ObjCMethod
:
111 // ObjC Methods can not be overloaded, qualified name uniquely identifies
113 DeclName
= ND
->getQualifiedNameAsString();
125 static StringRef
GetNthLineOfFile(std::optional
<llvm::MemoryBufferRef
> Buffer
,
130 llvm::line_iterator
LI(*Buffer
, false);
131 for (; !LI
.is_at_eof() && LI
.line_number() != Line
; ++LI
)
137 static std::string
NormalizeLine(const SourceManager
&SM
, const FullSourceLoc
&L
,
138 const LangOptions
&LangOpts
) {
139 static StringRef Whitespaces
= " \t\n";
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.
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
);
155 const char *BufferPos
= SM
.getCharacterData(StartOfLine
);
158 Lexer
Lexer(SM
.getLocForStartOfFile(SM
.getFileID(StartOfLine
)), LangOpts
,
159 Buffer
->getBufferStart(), BufferPos
, Buffer
->getBufferEnd());
161 size_t NextStart
= 0;
162 std::ostringstream LineBuff
;
163 while (!Lexer
.LexFromRawLexer(Token
) && NextStart
< 2) {
164 if (Token
.isAtStartOfLine() && NextStart
++ > 0)
166 LineBuff
<< std::string(SM
.getCharacterData(Token
.getLocation()),
170 return LineBuff
.str();
173 static llvm::SmallString
<32> GetMD5HashOfContent(StringRef Content
) {
175 llvm::MD5::MD5Result MD5Res
;
178 Hash
.update(Content
);
180 llvm::MD5::stringifyResult(MD5Res
, Res
);
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
= "$";
192 return (llvm::Twine(CheckerName
) + Delimiter
+
193 GetEnclosingDeclContextSignature(IssueDecl
) + Delimiter
+
194 Twine(IssueLoc
.getExpansionColumnNumber()) + Delimiter
+
195 NormalizeLine(IssueLoc
.getManager(), IssueLoc
, LangOpts
) +
196 Delimiter
+ WarningMessage
)
200 SmallString
<32> clang::getIssueHash(const FullSourceLoc
&IssueLoc
,
201 StringRef CheckerName
,
202 StringRef WarningMessage
,
203 const Decl
*IssueDecl
,
204 const LangOptions
&LangOpts
) {
206 return GetMD5HashOfContent(getIssueString(
207 IssueLoc
, CheckerName
, WarningMessage
, IssueDecl
, LangOpts
));