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