clang 20.0.0git
IssueHash.cpp
Go to the documentation of this file.
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
11#include "clang/AST/Decl.h"
12#include "clang/AST/DeclCXX.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
28using namespace clang;
29
30// Get a string representation of the parts of the signature that can be
31// overloaded on.
32static 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 =
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
89static 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
125static 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
137static 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),
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
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
185std::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
201 StringRef CheckerName,
202 StringRef WarningMessage,
203 const Decl *IssueDecl,
204 const LangOptions &LangOpts) {
205
207 IssueLoc, CheckerName, WarningMessage, IssueDecl, LangOpts));
208}
Defines the clang::ASTContext interface.
#define SM(sm)
Definition: Cuda.cpp:83
const Decl * D
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
static std::string NormalizeLine(const SourceManager &SM, const FullSourceLoc &L, const LangOptions &LangOpts)
Definition: IssueHash.cpp:137
static llvm::SmallString< 32 > GetMD5HashOfContent(StringRef Content)
Definition: IssueHash.cpp:173
static std::string GetSignature(const FunctionDecl *Target)
Definition: IssueHash.cpp:32
static StringRef GetNthLineOfFile(std::optional< llvm::MemoryBufferRef > Buffer, int Line)
Definition: IssueHash.cpp:125
static std::string GetEnclosingDeclContextSignature(const Decl *D)
Definition: IssueHash.cpp:89
llvm::MachO::Target Target
Definition: MachO.h:51
Defines the SourceManager interface.
Defines various enumerations that describe declaration and type specifiers.
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:86
A SourceLocation and its associated SourceManager.
FileID getFileID() const
unsigned getExpansionColumnNumber(bool *Invalid=nullptr) const
const SourceManager & getManager() const
unsigned getExpansionLineNumber(bool *Invalid=nullptr) const
Represents a function declaration or definition.
Definition: Decl.h:1932
FunctionDecl * getTemplateInstantiationPattern(bool ForDefinition=true) const
Retrieve the function declaration from which this function could be instantiated, if it is an instant...
Definition: Decl.cpp:4099
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:476
Lexer - This provides a simple interface that turns a text buffer into a stream of tokens.
Definition: Lexer.h:78
bool LexFromRawLexer(Token &Result)
LexFromRawLexer - Lex a token from a designated raw lexer (one with no associated preprocessor object...
Definition: Lexer.h:236
Encodes a location in the source.
This class handles loading and caching of source files into memory.
Token - This structure provides full information about a lexed token.
Definition: Token.h:36
SourceLocation getLocation() const
Return a source location identifier for the specified offset in the current file.
Definition: Token.h:132
unsigned getLength() const
Definition: Token.h:135
bool isAtStartOfLine() const
isAtStartOfLine - Return true if this token is at the start of a line.
Definition: Token.h:276
The JSON file list parser is used to communicate input to InstallAPI.
@ RQ_LValue
An lvalue ref-qualifier was provided (&).
Definition: Type.h:1781
@ RQ_RValue
An rvalue ref-qualifier was provided (&&).
Definition: Type.h:1784
llvm::SmallString< 32 > getIssueHash(const FullSourceLoc &IssueLoc, llvm::StringRef CheckerName, llvm::StringRef WarningMessage, const Decl *IssueDecl, const LangOptions &LangOpts)
Returns an opaque identifier for a diagnostic.
std::string getIssueString(const FullSourceLoc &IssueLoc, llvm::StringRef CheckerName, llvm::StringRef WarningMessage, const Decl *IssueDecl, const LangOptions &LangOpts)
Get the unhashed string representation of the V1 issue hash.