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