Home | History | Annotate | Download | only in Core
      1 //===---------- IssueHash.cpp - Generate identification hashes --*- C++ -*-===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 #include "clang/StaticAnalyzer/Core/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/SmallVector.h"
     17 #include "llvm/ADT/StringExtras.h"
     18 #include "llvm/ADT/StringRef.h"
     19 #include "llvm/ADT/Twine.h"
     20 #include "llvm/Support/LineIterator.h"
     21 #include "llvm/Support/MD5.h"
     22 #include "llvm/Support/Path.h"
     23 
     24 #include <functional>
     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.
     32 static std::string GetSignature(const FunctionDecl *Target) {
     33   if (!Target)
     34     return "";
     35   std::string Signature;
     36 
     37   if (!isa<CXXConstructorDecl>(Target) && !isa<CXXDestructorDecl>(Target) &&
     38       !isa<CXXConversionDecl>(Target))
     39     Signature.append(Target->getReturnType().getAsString()).append(" ");
     40   Signature.append(Target->getQualifiedNameAsString()).append("(");
     41 
     42   for (int i = 0, paramsCount = Target->getNumParams(); i < paramsCount; ++i) {
     43     if (i)
     44       Signature.append(", ");
     45     Signature.append(Target->getParamDecl(i)->getType().getAsString());
     46   }
     47 
     48   if (Target->isVariadic())
     49     Signature.append(", ...");
     50   Signature.append(")");
     51 
     52   const auto *TargetT =
     53       llvm::dyn_cast_or_null<FunctionType>(Target->getType().getTypePtr());
     54 
     55   if (!TargetT || !isa<CXXMethodDecl>(Target))
     56     return Signature;
     57 
     58   if (TargetT->isConst())
     59     Signature.append(" const");
     60   if (TargetT->isVolatile())
     61     Signature.append(" volatile");
     62   if (TargetT->isRestrict())
     63     Signature.append(" restrict");
     64 
     65   if (const auto *TargetPT =
     66           dyn_cast_or_null<FunctionProtoType>(Target->getType().getTypePtr())) {
     67     switch (TargetPT->getRefQualifier()) {
     68     case RQ_LValue:
     69       Signature.append(" &");
     70       break;
     71     case RQ_RValue:
     72       Signature.append(" &&");
     73       break;
     74     default:
     75       break;
     76     }
     77   }
     78 
     79   return Signature;
     80 }
     81 
     82 static std::string GetEnclosingDeclContextSignature(const Decl *D) {
     83   if (!D)
     84     return "";
     85 
     86   if (const auto *ND = dyn_cast<NamedDecl>(D)) {
     87     std::string DeclName;
     88 
     89     switch (ND->getKind()) {
     90     case Decl::Namespace:
     91     case Decl::Record:
     92     case Decl::CXXRecord:
     93     case Decl::Enum:
     94       DeclName = ND->getQualifiedNameAsString();
     95       break;
     96     case Decl::CXXConstructor:
     97     case Decl::CXXDestructor:
     98     case Decl::CXXConversion:
     99     case Decl::CXXMethod:
    100     case Decl::Function:
    101       DeclName = GetSignature(dyn_cast_or_null<FunctionDecl>(ND));
    102       break;
    103     case Decl::ObjCMethod:
    104       // ObjC Methods can not be overloaded, qualified name uniquely identifies
    105       // the method.
    106       DeclName = ND->getQualifiedNameAsString();
    107       break;
    108     default:
    109       break;
    110     }
    111 
    112     return DeclName;
    113   }
    114 
    115   return "";
    116 }
    117 
    118 static StringRef GetNthLineOfFile(llvm::MemoryBuffer *Buffer, int Line) {
    119   if (!Buffer)
    120     return "";
    121 
    122   llvm::line_iterator LI(*Buffer, false);
    123   for (; !LI.is_at_eof() && LI.line_number() != Line; ++LI)
    124     ;
    125 
    126   return *LI;
    127 }
    128 
    129 static std::string NormalizeLine(const SourceManager &SM, FullSourceLoc &L,
    130                                  const LangOptions &LangOpts) {
    131   static StringRef Whitespaces = " \t\n";
    132 
    133   StringRef Str = GetNthLineOfFile(SM.getBuffer(L.getFileID(), L),
    134                                    L.getExpansionLineNumber());
    135   StringRef::size_type col = Str.find_first_not_of(Whitespaces);
    136   if (col == StringRef::npos)
    137     col = 1; // The line only contains whitespace.
    138   else
    139     col++;
    140   SourceLocation StartOfLine =
    141       SM.translateLineCol(SM.getFileID(L), L.getExpansionLineNumber(), col);
    142   llvm::MemoryBuffer *Buffer =
    143       SM.getBuffer(SM.getFileID(StartOfLine), StartOfLine);
    144   if (!Buffer)
    145     return {};
    146 
    147   const char *BufferPos = SM.getCharacterData(StartOfLine);
    148 
    149   Token Token;
    150   Lexer Lexer(SM.getLocForStartOfFile(SM.getFileID(StartOfLine)), LangOpts,
    151               Buffer->getBufferStart(), BufferPos, Buffer->getBufferEnd());
    152 
    153   size_t NextStart = 0;
    154   std::ostringstream LineBuff;
    155   while (!Lexer.LexFromRawLexer(Token) && NextStart < 2) {
    156     if (Token.isAtStartOfLine() && NextStart++ > 0)
    157       continue;
    158     LineBuff << std::string(SM.getCharacterData(Token.getLocation()),
    159                             Token.getLength());
    160   }
    161 
    162   return LineBuff.str();
    163 }
    164 
    165 static llvm::SmallString<32> GetHashOfContent(StringRef Content) {
    166   llvm::MD5 Hash;
    167   llvm::MD5::MD5Result MD5Res;
    168   SmallString<32> Res;
    169 
    170   Hash.update(Content);
    171   Hash.final(MD5Res);
    172   llvm::MD5::stringifyResult(MD5Res, Res);
    173 
    174   return Res;
    175 }
    176 
    177 std::string clang::GetIssueString(const SourceManager &SM,
    178                                   FullSourceLoc &IssueLoc,
    179                                   StringRef CheckerName, StringRef BugType,
    180                                   const Decl *D,
    181                                   const LangOptions &LangOpts) {
    182   static StringRef Delimiter = "$";
    183 
    184   return (llvm::Twine(CheckerName) + Delimiter +
    185           GetEnclosingDeclContextSignature(D) + Delimiter +
    186           Twine(IssueLoc.getExpansionColumnNumber()) + Delimiter +
    187           NormalizeLine(SM, IssueLoc, LangOpts) + Delimiter + BugType)
    188       .str();
    189 }
    190 
    191 SmallString<32> clang::GetIssueHash(const SourceManager &SM,
    192                                     FullSourceLoc &IssueLoc,
    193                                     StringRef CheckerName, StringRef BugType,
    194                                     const Decl *D,
    195                                     const LangOptions &LangOpts) {
    196 
    197   return GetHashOfContent(
    198       GetIssueString(SM, IssueLoc, CheckerName, BugType, D, LangOpts));
    199 }
    200