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 
10 #include "clang/AST/ASTContext.h"
11 #include "clang/AST/Decl.h"
12 #include "clang/AST/DeclCXX.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 =
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 
89 static 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 
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(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 
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(Content);
179  Hash.final(MD5Res);
180  llvm::MD5::stringifyResult(MD5Res, 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(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 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
static llvm::SmallString< 32 > GetMD5HashOfContent(StringRef Content)
Definition: IssueHash.cpp:173
llvm::MachO::Target Target
Definition: MachO.h:51
llvm::MachO::Record Record
Definition: MachO.h:31
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
unsigned getExpansionLineNumber(bool *Invalid=nullptr) const
const SourceManager & getManager() const
Represents a function declaration or definition.
Definition: Decl.h:1933
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:4103
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:480
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.