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