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 //===----------------------------------------------------------------------===//
8 #include "clang/StaticAnalyzer/Core/IssueHash.h"
9 #include "clang/AST/ASTContext.h"
10 #include "clang/AST/Decl.h"
11 #include "clang/AST/DeclCXX.h"
12 #include "clang/Basic/SourceManager.h"
13 #include "clang/Basic/Specifiers.h"
14 #include "clang/Lex/Lexer.h"
15 #include "llvm/ADT/StringExtras.h"
16 #include "llvm/ADT/StringRef.h"
17 #include "llvm/ADT/Twine.h"
18 #include "llvm/Support/LineIterator.h"
19 #include "llvm/Support/MD5.h"
20 #include "llvm/Support/Path.h"
26 using namespace clang;
28 // Get a string representation of the parts of the signature that can be
30 static std::string GetSignature(const FunctionDecl *Target) {
33 std::string Signature;
35 // When a flow sensitive bug happens in templated code we should not generate
36 // distinct hash value for every instantiation. Use the signature from the
38 if (const FunctionDecl *InstantiatedFrom =
39 Target->getTemplateInstantiationPattern())
40 Target = InstantiatedFrom;
42 if (!isa<CXXConstructorDecl>(Target) && !isa<CXXDestructorDecl>(Target) &&
43 !isa<CXXConversionDecl>(Target))
44 Signature.append(Target->getReturnType().getAsString()).append(" ");
45 Signature.append(Target->getQualifiedNameAsString()).append("(");
47 for (int i = 0, paramsCount = Target->getNumParams(); i < paramsCount; ++i) {
49 Signature.append(", ");
50 Signature.append(Target->getParamDecl(i)->getType().getAsString());
53 if (Target->isVariadic())
54 Signature.append(", ...");
55 Signature.append(")");
58 llvm::dyn_cast_or_null<FunctionType>(Target->getType().getTypePtr());
60 if (!TargetT || !isa<CXXMethodDecl>(Target))
63 if (TargetT->isConst())
64 Signature.append(" const");
65 if (TargetT->isVolatile())
66 Signature.append(" volatile");
67 if (TargetT->isRestrict())
68 Signature.append(" restrict");
70 if (const auto *TargetPT =
71 dyn_cast_or_null<FunctionProtoType>(Target->getType().getTypePtr())) {
72 switch (TargetPT->getRefQualifier()) {
74 Signature.append(" &");
77 Signature.append(" &&");
87 static std::string GetEnclosingDeclContextSignature(const Decl *D) {
91 if (const auto *ND = dyn_cast<NamedDecl>(D)) {
94 switch (ND->getKind()) {
99 DeclName = ND->getQualifiedNameAsString();
101 case Decl::CXXConstructor:
102 case Decl::CXXDestructor:
103 case Decl::CXXConversion:
104 case Decl::CXXMethod:
106 DeclName = GetSignature(dyn_cast_or_null<FunctionDecl>(ND));
108 case Decl::ObjCMethod:
109 // ObjC Methods can not be overloaded, qualified name uniquely identifies
111 DeclName = ND->getQualifiedNameAsString();
123 static StringRef GetNthLineOfFile(const llvm::MemoryBuffer *Buffer, int Line) {
127 llvm::line_iterator LI(*Buffer, false);
128 for (; !LI.is_at_eof() && LI.line_number() != Line; ++LI)
134 static std::string NormalizeLine(const SourceManager &SM, FullSourceLoc &L,
135 const LangOptions &LangOpts) {
136 static StringRef Whitespaces = " \t\n";
138 StringRef Str = GetNthLineOfFile(SM.getBuffer(L.getFileID(), L),
139 L.getExpansionLineNumber());
140 StringRef::size_type col = Str.find_first_not_of(Whitespaces);
141 if (col == StringRef::npos)
142 col = 1; // The line only contains whitespace.
145 SourceLocation StartOfLine =
146 SM.translateLineCol(SM.getFileID(L), L.getExpansionLineNumber(), col);
147 const llvm::MemoryBuffer *Buffer =
148 SM.getBuffer(SM.getFileID(StartOfLine), StartOfLine);
152 const char *BufferPos = SM.getCharacterData(StartOfLine);
155 Lexer Lexer(SM.getLocForStartOfFile(SM.getFileID(StartOfLine)), LangOpts,
156 Buffer->getBufferStart(), BufferPos, Buffer->getBufferEnd());
158 size_t NextStart = 0;
159 std::ostringstream LineBuff;
160 while (!Lexer.LexFromRawLexer(Token) && NextStart < 2) {
161 if (Token.isAtStartOfLine() && NextStart++ > 0)
163 LineBuff << std::string(SM.getCharacterData(Token.getLocation()),
167 return LineBuff.str();
170 static llvm::SmallString<32> GetHashOfContent(StringRef Content) {
172 llvm::MD5::MD5Result MD5Res;
175 Hash.update(Content);
177 llvm::MD5::stringifyResult(MD5Res, Res);
182 std::string clang::GetIssueString(const SourceManager &SM,
183 FullSourceLoc &IssueLoc,
184 StringRef CheckerName, StringRef BugType,
186 const LangOptions &LangOpts) {
187 static StringRef Delimiter = "$";
189 return (llvm::Twine(CheckerName) + Delimiter +
190 GetEnclosingDeclContextSignature(D) + Delimiter +
191 Twine(IssueLoc.getExpansionColumnNumber()) + Delimiter +
192 NormalizeLine(SM, IssueLoc, LangOpts) + Delimiter + BugType)
196 SmallString<32> clang::GetIssueHash(const SourceManager &SM,
197 FullSourceLoc &IssueLoc,
198 StringRef CheckerName, StringRef BugType,
200 const LangOptions &LangOpts) {
202 return GetHashOfContent(
203 GetIssueString(SM, IssueLoc, CheckerName, BugType, D, LangOpts));