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