clang 22.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"
14#include "clang/Lex/Lexer.h"
15#include "llvm/ADT/StringRef.h"
16#include "llvm/ADT/Twine.h"
17#include "llvm/Support/LineIterator.h"
18#include "llvm/Support/MD5.h"
19
20#include <optional>
21#include <sstream>
22#include <string>
23
24using namespace clang;
25
26// Get a string representation of the parts of the signature that can be
27// overloaded on.
28static std::string GetSignature(const FunctionDecl *Target) {
29 if (!Target)
30 return "";
31 std::string Signature;
32
33 // When a flow sensitive bug happens in templated code we should not generate
34 // distinct hash value for every instantiation. Use the signature from the
35 // primary template.
36 if (const FunctionDecl *InstantiatedFrom =
37 Target->getTemplateInstantiationPattern())
38 Target = InstantiatedFrom;
39
42 Signature.append(Target->getReturnType().getAsString()).append(" ");
43 Signature.append(Target->getQualifiedNameAsString()).append("(");
44
45 for (int i = 0, paramsCount = Target->getNumParams(); i < paramsCount; ++i) {
46 if (i)
47 Signature.append(", ");
48 Signature.append(Target->getParamDecl(i)->getType().getAsString());
49 }
50
51 if (Target->isVariadic())
52 Signature.append(", ...");
53 Signature.append(")");
54
55 const auto *TargetT =
56 llvm::dyn_cast_or_null<FunctionType>(Target->getType().getTypePtr());
57
58 if (!TargetT || !isa<CXXMethodDecl>(Target))
59 return Signature;
60
61 if (TargetT->isConst())
62 Signature.append(" const");
63 if (TargetT->isVolatile())
64 Signature.append(" volatile");
65 if (TargetT->isRestrict())
66 Signature.append(" restrict");
67
68 if (const auto *TargetPT =
69 dyn_cast_or_null<FunctionProtoType>(Target->getType().getTypePtr())) {
70 switch (TargetPT->getRefQualifier()) {
71 case RQ_LValue:
72 Signature.append(" &");
73 break;
74 case RQ_RValue:
75 Signature.append(" &&");
76 break;
77 default:
78 break;
79 }
80 }
81
82 return Signature;
83}
84
85static std::string GetEnclosingDeclContextSignature(const Decl *D) {
86 if (!D)
87 return "";
88
89 if (const auto *ND = dyn_cast<NamedDecl>(D)) {
90 std::string DeclName;
91
92 switch (ND->getKind()) {
93 case Decl::Namespace:
94 case Decl::Record:
95 case Decl::CXXRecord:
96 case Decl::Enum:
97 DeclName = ND->getQualifiedNameAsString();
98 break;
99 case Decl::CXXConstructor:
100 case Decl::CXXDestructor:
101 case Decl::CXXConversion:
102 case Decl::CXXMethod:
103 case Decl::Function:
104 DeclName = GetSignature(dyn_cast_or_null<FunctionDecl>(ND));
105 break;
106 case Decl::ObjCMethod:
107 // ObjC Methods can not be overloaded, qualified name uniquely identifies
108 // the method.
109 DeclName = ND->getQualifiedNameAsString();
110 break;
111 default:
112 break;
113 }
114
115 return DeclName;
116 }
117
118 return "";
119}
120
121static StringRef GetNthLineOfFile(std::optional<llvm::MemoryBufferRef> Buffer,
122 int Line) {
123 if (!Buffer)
124 return "";
125
126 llvm::line_iterator LI(*Buffer, false);
127 for (; !LI.is_at_eof() && LI.line_number() != Line; ++LI)
128 ;
129
130 return *LI;
131}
132
133static std::string NormalizeLine(const SourceManager &SM, const FullSourceLoc &L,
134 const LangOptions &LangOpts) {
135 static StringRef Whitespaces = " \t\n";
136
137 StringRef Str = GetNthLineOfFile(SM.getBufferOrNone(L.getFileID(), L),
139 StringRef::size_type col = Str.find_first_not_of(Whitespaces);
140 if (col == StringRef::npos)
141 col = 1; // The line only contains whitespace.
142 else
143 col++;
144 SourceLocation StartOfLine =
145 SM.translateLineCol(SM.getFileID(L), L.getExpansionLineNumber(), col);
146 std::optional<llvm::MemoryBufferRef> Buffer =
147 SM.getBufferOrNone(SM.getFileID(StartOfLine), StartOfLine);
148 if (!Buffer)
149 return {};
150
151 const char *BufferPos = SM.getCharacterData(StartOfLine);
152
153 Token Token;
154 Lexer Lexer(SM.getLocForStartOfFile(SM.getFileID(StartOfLine)), LangOpts,
155 Buffer->getBufferStart(), BufferPos, Buffer->getBufferEnd());
156
157 size_t NextStart = 0;
158 std::ostringstream LineBuff;
159 while (!Lexer.LexFromRawLexer(Token) && NextStart < 2) {
160 if (Token.isAtStartOfLine() && NextStart++ > 0)
161 continue;
162 LineBuff << std::string(SM.getCharacterData(Token.getLocation()),
163 Token.getLength());
164 }
165
166 return LineBuff.str();
167}
168
170 llvm::MD5 Hash;
171 llvm::MD5::MD5Result MD5Res;
172 SmallString<32> Res;
173
174 Hash.update(Content);
175 Hash.final(MD5Res);
176 llvm::MD5::stringifyResult(MD5Res, Res);
177
178 return Res;
179}
180
181std::string clang::getIssueString(const FullSourceLoc &IssueLoc,
182 StringRef CheckerName,
183 StringRef WarningMessage,
184 const Decl *IssueDecl,
185 const LangOptions &LangOpts) {
186 static StringRef Delimiter = "$";
187
188 return (llvm::Twine(CheckerName) + Delimiter +
189 GetEnclosingDeclContextSignature(IssueDecl) + Delimiter +
190 Twine(IssueLoc.getExpansionColumnNumber()) + Delimiter +
191 NormalizeLine(IssueLoc.getManager(), IssueLoc, LangOpts) +
192 Delimiter + WarningMessage)
193 .str();
194}
195
197 StringRef CheckerName,
198 StringRef WarningMessage,
199 const Decl *IssueDecl,
200 const LangOptions &LangOpts) {
201
203 IssueLoc, CheckerName, WarningMessage, IssueDecl, LangOpts));
204}
Defines the clang::ASTContext interface.
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)
static llvm::SmallString< 32 > GetMD5HashOfContent(StringRef Content)
static std::string GetSignature(const FunctionDecl *Target)
Definition IssueHash.cpp:28
static StringRef GetNthLineOfFile(std::optional< llvm::MemoryBufferRef > Buffer, int Line)
static std::string GetEnclosingDeclContextSignature(const Decl *D)
Definition IssueHash.cpp:85
llvm::MachO::Target Target
Definition MachO.h:51
#define SM(sm)
Defines the SourceManager interface.
Decl - This represents one declaration (or definition), e.g.
Definition DeclBase.h:86
A SourceLocation and its associated SourceManager.
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:1999
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
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:134
unsigned getLength() const
Definition Token.h:137
bool isAtStartOfLine() const
isAtStartOfLine - Return true if this token is at the start of a line.
Definition Token.h:278
The JSON file list parser is used to communicate input to InstallAPI.
bool isa(CodeGen::Address addr)
Definition Address.h:330
@ RQ_LValue
An lvalue ref-qualifier was provided (&).
Definition TypeBase.h:1785
@ RQ_RValue
An rvalue ref-qualifier was provided (&&).
Definition TypeBase.h:1788
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.