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>(Val: Target) && !isa<CXXDestructorDecl>(Val: Target) && |
45 | !isa<CXXConversionDecl>(Val: Target)) |
46 | Signature.append(str: Target->getReturnType().getAsString()).append(s: " " ); |
47 | Signature.append(Target->getQualifiedNameAsString()).append("(" ); |
48 | |
49 | for (int i = 0, paramsCount = Target->getNumParams(); i < paramsCount; ++i) { |
50 | if (i) |
51 | Signature.append(s: ", " ); |
52 | Signature.append(Target->getParamDecl(i)->getType().getAsString()); |
53 | } |
54 | |
55 | if (Target->isVariadic()) |
56 | Signature.append(s: ", ..." ); |
57 | Signature.append(s: ")" ); |
58 | |
59 | const auto *TargetT = |
60 | llvm::dyn_cast_or_null<FunctionType>(Target->getType().getTypePtr()); |
61 | |
62 | if (!TargetT || !isa<CXXMethodDecl>(Val: Target)) |
63 | return Signature; |
64 | |
65 | if (TargetT->isConst()) |
66 | Signature.append(s: " const" ); |
67 | if (TargetT->isVolatile()) |
68 | Signature.append(s: " volatile" ); |
69 | if (TargetT->isRestrict()) |
70 | Signature.append(s: " 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(s: " &" ); |
77 | break; |
78 | case RQ_RValue: |
79 | Signature.append(s: " &&" ); |
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>(Val: 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(Target: dyn_cast_or_null<FunctionDecl>(Val: 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(Buffer: SM.getBufferOrNone(FID: L.getFileID(), Loc: L), |
142 | Line: L.getExpansionLineNumber()); |
143 | StringRef::size_type col = Str.find_first_not_of(Chars: Whitespaces); |
144 | if (col == StringRef::npos) |
145 | col = 1; // The line only contains whitespace. |
146 | else |
147 | col++; |
148 | SourceLocation StartOfLine = |
149 | SM.translateLineCol(FID: SM.getFileID(SpellingLoc: L), Line: L.getExpansionLineNumber(), Col: col); |
150 | std::optional<llvm::MemoryBufferRef> Buffer = |
151 | SM.getBufferOrNone(FID: SM.getFileID(SpellingLoc: StartOfLine), Loc: StartOfLine); |
152 | if (!Buffer) |
153 | return {}; |
154 | |
155 | const char *BufferPos = SM.getCharacterData(SL: StartOfLine); |
156 | |
157 | Token Token; |
158 | Lexer Lexer(SM.getLocForStartOfFile(FID: SM.getFileID(SpellingLoc: StartOfLine)), LangOpts, |
159 | Buffer->getBufferStart(), BufferPos, Buffer->getBufferEnd()); |
160 | |
161 | size_t NextStart = 0; |
162 | std::ostringstream LineBuff; |
163 | while (!Lexer.LexFromRawLexer(Result&: Token) && NextStart < 2) { |
164 | if (Token.isAtStartOfLine() && NextStart++ > 0) |
165 | continue; |
166 | LineBuff << std::string(SM.getCharacterData(SL: 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(Str: Content); |
179 | Hash.final(Result&: MD5Res); |
180 | llvm::MD5::stringifyResult(Result&: MD5Res, Str&: 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(D: IssueDecl) + Delimiter + |
194 | Twine(IssueLoc.getExpansionColumnNumber()) + Delimiter + |
195 | NormalizeLine(SM: IssueLoc.getManager(), L: 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(Content: getIssueString( |
207 | IssueLoc, CheckerName, WarningMessage, IssueDecl, LangOpts)); |
208 | } |
209 | |