1e8d8bef9SDimitry Andric //===---------- IssueHash.cpp - Generate identification hashes --*- C++ -*-===//
2e8d8bef9SDimitry Andric //
3e8d8bef9SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e8d8bef9SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5e8d8bef9SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e8d8bef9SDimitry Andric //
7e8d8bef9SDimitry Andric //===----------------------------------------------------------------------===//
8e8d8bef9SDimitry Andric
9e8d8bef9SDimitry Andric #include "clang/Analysis/IssueHash.h"
10e8d8bef9SDimitry Andric #include "clang/AST/ASTContext.h"
11e8d8bef9SDimitry Andric #include "clang/AST/Decl.h"
12e8d8bef9SDimitry Andric #include "clang/AST/DeclCXX.h"
13e8d8bef9SDimitry Andric #include "clang/Basic/SourceManager.h"
14e8d8bef9SDimitry Andric #include "clang/Basic/Specifiers.h"
15e8d8bef9SDimitry Andric #include "clang/Lex/Lexer.h"
16e8d8bef9SDimitry Andric #include "llvm/ADT/StringExtras.h"
17e8d8bef9SDimitry Andric #include "llvm/ADT/StringRef.h"
18e8d8bef9SDimitry Andric #include "llvm/ADT/Twine.h"
19e8d8bef9SDimitry Andric #include "llvm/Support/LineIterator.h"
20e8d8bef9SDimitry Andric #include "llvm/Support/MD5.h"
21e8d8bef9SDimitry Andric #include "llvm/Support/Path.h"
22e8d8bef9SDimitry Andric
23e8d8bef9SDimitry Andric #include <functional>
24*bdd1243dSDimitry Andric #include <optional>
25e8d8bef9SDimitry Andric #include <sstream>
26e8d8bef9SDimitry Andric #include <string>
27e8d8bef9SDimitry Andric
28e8d8bef9SDimitry Andric using namespace clang;
29e8d8bef9SDimitry Andric
30e8d8bef9SDimitry Andric // Get a string representation of the parts of the signature that can be
31e8d8bef9SDimitry Andric // overloaded on.
GetSignature(const FunctionDecl * Target)32e8d8bef9SDimitry Andric static std::string GetSignature(const FunctionDecl *Target) {
33e8d8bef9SDimitry Andric if (!Target)
34e8d8bef9SDimitry Andric return "";
35e8d8bef9SDimitry Andric std::string Signature;
36e8d8bef9SDimitry Andric
37e8d8bef9SDimitry Andric // When a flow sensitive bug happens in templated code we should not generate
38e8d8bef9SDimitry Andric // distinct hash value for every instantiation. Use the signature from the
39e8d8bef9SDimitry Andric // primary template.
40e8d8bef9SDimitry Andric if (const FunctionDecl *InstantiatedFrom =
41e8d8bef9SDimitry Andric Target->getTemplateInstantiationPattern())
42e8d8bef9SDimitry Andric Target = InstantiatedFrom;
43e8d8bef9SDimitry Andric
44e8d8bef9SDimitry Andric if (!isa<CXXConstructorDecl>(Target) && !isa<CXXDestructorDecl>(Target) &&
45e8d8bef9SDimitry Andric !isa<CXXConversionDecl>(Target))
46e8d8bef9SDimitry Andric Signature.append(Target->getReturnType().getAsString()).append(" ");
47e8d8bef9SDimitry Andric Signature.append(Target->getQualifiedNameAsString()).append("(");
48e8d8bef9SDimitry Andric
49e8d8bef9SDimitry Andric for (int i = 0, paramsCount = Target->getNumParams(); i < paramsCount; ++i) {
50e8d8bef9SDimitry Andric if (i)
51e8d8bef9SDimitry Andric Signature.append(", ");
52e8d8bef9SDimitry Andric Signature.append(Target->getParamDecl(i)->getType().getAsString());
53e8d8bef9SDimitry Andric }
54e8d8bef9SDimitry Andric
55e8d8bef9SDimitry Andric if (Target->isVariadic())
56e8d8bef9SDimitry Andric Signature.append(", ...");
57e8d8bef9SDimitry Andric Signature.append(")");
58e8d8bef9SDimitry Andric
59e8d8bef9SDimitry Andric const auto *TargetT =
60e8d8bef9SDimitry Andric llvm::dyn_cast_or_null<FunctionType>(Target->getType().getTypePtr());
61e8d8bef9SDimitry Andric
62e8d8bef9SDimitry Andric if (!TargetT || !isa<CXXMethodDecl>(Target))
63e8d8bef9SDimitry Andric return Signature;
64e8d8bef9SDimitry Andric
65e8d8bef9SDimitry Andric if (TargetT->isConst())
66e8d8bef9SDimitry Andric Signature.append(" const");
67e8d8bef9SDimitry Andric if (TargetT->isVolatile())
68e8d8bef9SDimitry Andric Signature.append(" volatile");
69e8d8bef9SDimitry Andric if (TargetT->isRestrict())
70e8d8bef9SDimitry Andric Signature.append(" restrict");
71e8d8bef9SDimitry Andric
72e8d8bef9SDimitry Andric if (const auto *TargetPT =
73e8d8bef9SDimitry Andric dyn_cast_or_null<FunctionProtoType>(Target->getType().getTypePtr())) {
74e8d8bef9SDimitry Andric switch (TargetPT->getRefQualifier()) {
75e8d8bef9SDimitry Andric case RQ_LValue:
76e8d8bef9SDimitry Andric Signature.append(" &");
77e8d8bef9SDimitry Andric break;
78e8d8bef9SDimitry Andric case RQ_RValue:
79e8d8bef9SDimitry Andric Signature.append(" &&");
80e8d8bef9SDimitry Andric break;
81e8d8bef9SDimitry Andric default:
82e8d8bef9SDimitry Andric break;
83e8d8bef9SDimitry Andric }
84e8d8bef9SDimitry Andric }
85e8d8bef9SDimitry Andric
86e8d8bef9SDimitry Andric return Signature;
87e8d8bef9SDimitry Andric }
88e8d8bef9SDimitry Andric
GetEnclosingDeclContextSignature(const Decl * D)89e8d8bef9SDimitry Andric static std::string GetEnclosingDeclContextSignature(const Decl *D) {
90e8d8bef9SDimitry Andric if (!D)
91e8d8bef9SDimitry Andric return "";
92e8d8bef9SDimitry Andric
93e8d8bef9SDimitry Andric if (const auto *ND = dyn_cast<NamedDecl>(D)) {
94e8d8bef9SDimitry Andric std::string DeclName;
95e8d8bef9SDimitry Andric
96e8d8bef9SDimitry Andric switch (ND->getKind()) {
97e8d8bef9SDimitry Andric case Decl::Namespace:
98e8d8bef9SDimitry Andric case Decl::Record:
99e8d8bef9SDimitry Andric case Decl::CXXRecord:
100e8d8bef9SDimitry Andric case Decl::Enum:
101e8d8bef9SDimitry Andric DeclName = ND->getQualifiedNameAsString();
102e8d8bef9SDimitry Andric break;
103e8d8bef9SDimitry Andric case Decl::CXXConstructor:
104e8d8bef9SDimitry Andric case Decl::CXXDestructor:
105e8d8bef9SDimitry Andric case Decl::CXXConversion:
106e8d8bef9SDimitry Andric case Decl::CXXMethod:
107e8d8bef9SDimitry Andric case Decl::Function:
108e8d8bef9SDimitry Andric DeclName = GetSignature(dyn_cast_or_null<FunctionDecl>(ND));
109e8d8bef9SDimitry Andric break;
110e8d8bef9SDimitry Andric case Decl::ObjCMethod:
111e8d8bef9SDimitry Andric // ObjC Methods can not be overloaded, qualified name uniquely identifies
112e8d8bef9SDimitry Andric // the method.
113e8d8bef9SDimitry Andric DeclName = ND->getQualifiedNameAsString();
114e8d8bef9SDimitry Andric break;
115e8d8bef9SDimitry Andric default:
116e8d8bef9SDimitry Andric break;
117e8d8bef9SDimitry Andric }
118e8d8bef9SDimitry Andric
119e8d8bef9SDimitry Andric return DeclName;
120e8d8bef9SDimitry Andric }
121e8d8bef9SDimitry Andric
122e8d8bef9SDimitry Andric return "";
123e8d8bef9SDimitry Andric }
124e8d8bef9SDimitry Andric
GetNthLineOfFile(std::optional<llvm::MemoryBufferRef> Buffer,int Line)125*bdd1243dSDimitry Andric static StringRef GetNthLineOfFile(std::optional<llvm::MemoryBufferRef> Buffer,
126e8d8bef9SDimitry Andric int Line) {
127e8d8bef9SDimitry Andric if (!Buffer)
128e8d8bef9SDimitry Andric return "";
129e8d8bef9SDimitry Andric
130e8d8bef9SDimitry Andric llvm::line_iterator LI(*Buffer, false);
131e8d8bef9SDimitry Andric for (; !LI.is_at_eof() && LI.line_number() != Line; ++LI)
132e8d8bef9SDimitry Andric ;
133e8d8bef9SDimitry Andric
134e8d8bef9SDimitry Andric return *LI;
135e8d8bef9SDimitry Andric }
136e8d8bef9SDimitry Andric
NormalizeLine(const SourceManager & SM,const FullSourceLoc & L,const LangOptions & LangOpts)137e8d8bef9SDimitry Andric static std::string NormalizeLine(const SourceManager &SM, const FullSourceLoc &L,
138e8d8bef9SDimitry Andric const LangOptions &LangOpts) {
139e8d8bef9SDimitry Andric static StringRef Whitespaces = " \t\n";
140e8d8bef9SDimitry Andric
141e8d8bef9SDimitry Andric StringRef Str = GetNthLineOfFile(SM.getBufferOrNone(L.getFileID(), L),
142e8d8bef9SDimitry Andric L.getExpansionLineNumber());
143e8d8bef9SDimitry Andric StringRef::size_type col = Str.find_first_not_of(Whitespaces);
144e8d8bef9SDimitry Andric if (col == StringRef::npos)
145e8d8bef9SDimitry Andric col = 1; // The line only contains whitespace.
146e8d8bef9SDimitry Andric else
147e8d8bef9SDimitry Andric col++;
148e8d8bef9SDimitry Andric SourceLocation StartOfLine =
149e8d8bef9SDimitry Andric SM.translateLineCol(SM.getFileID(L), L.getExpansionLineNumber(), col);
150*bdd1243dSDimitry Andric std::optional<llvm::MemoryBufferRef> Buffer =
151e8d8bef9SDimitry Andric SM.getBufferOrNone(SM.getFileID(StartOfLine), StartOfLine);
152e8d8bef9SDimitry Andric if (!Buffer)
153e8d8bef9SDimitry Andric return {};
154e8d8bef9SDimitry Andric
155e8d8bef9SDimitry Andric const char *BufferPos = SM.getCharacterData(StartOfLine);
156e8d8bef9SDimitry Andric
157e8d8bef9SDimitry Andric Token Token;
158e8d8bef9SDimitry Andric Lexer Lexer(SM.getLocForStartOfFile(SM.getFileID(StartOfLine)), LangOpts,
159e8d8bef9SDimitry Andric Buffer->getBufferStart(), BufferPos, Buffer->getBufferEnd());
160e8d8bef9SDimitry Andric
161e8d8bef9SDimitry Andric size_t NextStart = 0;
162e8d8bef9SDimitry Andric std::ostringstream LineBuff;
163e8d8bef9SDimitry Andric while (!Lexer.LexFromRawLexer(Token) && NextStart < 2) {
164e8d8bef9SDimitry Andric if (Token.isAtStartOfLine() && NextStart++ > 0)
165e8d8bef9SDimitry Andric continue;
166e8d8bef9SDimitry Andric LineBuff << std::string(SM.getCharacterData(Token.getLocation()),
167e8d8bef9SDimitry Andric Token.getLength());
168e8d8bef9SDimitry Andric }
169e8d8bef9SDimitry Andric
170e8d8bef9SDimitry Andric return LineBuff.str();
171e8d8bef9SDimitry Andric }
172e8d8bef9SDimitry Andric
GetMD5HashOfContent(StringRef Content)173e8d8bef9SDimitry Andric static llvm::SmallString<32> GetMD5HashOfContent(StringRef Content) {
174e8d8bef9SDimitry Andric llvm::MD5 Hash;
175e8d8bef9SDimitry Andric llvm::MD5::MD5Result MD5Res;
176e8d8bef9SDimitry Andric SmallString<32> Res;
177e8d8bef9SDimitry Andric
178e8d8bef9SDimitry Andric Hash.update(Content);
179e8d8bef9SDimitry Andric Hash.final(MD5Res);
180e8d8bef9SDimitry Andric llvm::MD5::stringifyResult(MD5Res, Res);
181e8d8bef9SDimitry Andric
182e8d8bef9SDimitry Andric return Res;
183e8d8bef9SDimitry Andric }
184e8d8bef9SDimitry Andric
getIssueString(const FullSourceLoc & IssueLoc,StringRef CheckerName,StringRef WarningMessage,const Decl * IssueDecl,const LangOptions & LangOpts)185e8d8bef9SDimitry Andric std::string clang::getIssueString(const FullSourceLoc &IssueLoc,
186e8d8bef9SDimitry Andric StringRef CheckerName,
187e8d8bef9SDimitry Andric StringRef WarningMessage,
188e8d8bef9SDimitry Andric const Decl *IssueDecl,
189e8d8bef9SDimitry Andric const LangOptions &LangOpts) {
190e8d8bef9SDimitry Andric static StringRef Delimiter = "$";
191e8d8bef9SDimitry Andric
192e8d8bef9SDimitry Andric return (llvm::Twine(CheckerName) + Delimiter +
193e8d8bef9SDimitry Andric GetEnclosingDeclContextSignature(IssueDecl) + Delimiter +
194e8d8bef9SDimitry Andric Twine(IssueLoc.getExpansionColumnNumber()) + Delimiter +
195e8d8bef9SDimitry Andric NormalizeLine(IssueLoc.getManager(), IssueLoc, LangOpts) +
196e8d8bef9SDimitry Andric Delimiter + WarningMessage)
197e8d8bef9SDimitry Andric .str();
198e8d8bef9SDimitry Andric }
199e8d8bef9SDimitry Andric
getIssueHash(const FullSourceLoc & IssueLoc,StringRef CheckerName,StringRef WarningMessage,const Decl * IssueDecl,const LangOptions & LangOpts)200e8d8bef9SDimitry Andric SmallString<32> clang::getIssueHash(const FullSourceLoc &IssueLoc,
201e8d8bef9SDimitry Andric StringRef CheckerName,
202e8d8bef9SDimitry Andric StringRef WarningMessage,
203e8d8bef9SDimitry Andric const Decl *IssueDecl,
204e8d8bef9SDimitry Andric const LangOptions &LangOpts) {
205e8d8bef9SDimitry Andric
206e8d8bef9SDimitry Andric return GetMD5HashOfContent(getIssueString(
207e8d8bef9SDimitry Andric IssueLoc, CheckerName, WarningMessage, IssueDecl, LangOpts));
208e8d8bef9SDimitry Andric }
209