Index: include/clang/StaticAnalyzer/Core/BugId.h =================================================================== --- include/clang/StaticAnalyzer/Core/BugId.h +++ include/clang/StaticAnalyzer/Core/BugId.h @@ -0,0 +1,24 @@ +//===---------- BugId.h - Unique bugid utility ------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_STATICANALYZER_CORE_BUGID_H +#define LLVM_CLANG_STATICANALYZER_CORE_BUGID_H + +#include "llvm/ADT/SmallString.h" + +namespace clang { +class Decl; +class SourceManager; +class FullSourceLoc; + +llvm::SmallString<32> GetIssueHash(const SourceManager *SM, FullSourceLoc &L, + llvm::StringRef CheckerName, + llvm::StringRef HashField, const Decl *D); +} + +#endif Index: include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h =================================================================== --- include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h +++ include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h @@ -75,6 +75,7 @@ const Decl *DeclWithIssue; std::string ShortDescription; std::string Description; + std::string HashField; PathDiagnosticLocation Location; PathDiagnosticLocation UniqueingLocation; const Decl *UniqueingDecl; @@ -143,19 +144,23 @@ void popInterestingSymbolsAndRegions(); public: - BugReport(BugType& bt, StringRef desc, const ExplodedNode *errornode) - : BT(bt), DeclWithIssue(nullptr), Description(desc), ErrorNode(errornode), - ConfigurationChangeToken(0), DoNotPrunePath(false) {} - - BugReport(BugType& bt, StringRef shortDesc, StringRef desc, - const ExplodedNode *errornode) - : BT(bt), DeclWithIssue(nullptr), ShortDescription(shortDesc), - Description(desc), ErrorNode(errornode), ConfigurationChangeToken(0), - DoNotPrunePath(false) {} - - BugReport(BugType &bt, StringRef desc, PathDiagnosticLocation l) - : BT(bt), DeclWithIssue(nullptr), Description(desc), Location(l), - ErrorNode(nullptr), ConfigurationChangeToken(0), DoNotPrunePath(false) {} + BugReport(BugType &bt, StringRef desc, const ExplodedNode *errornode, + StringRef hashfield = "") + : BT(bt), DeclWithIssue(nullptr), Description(desc), HashField(hashfield), + ErrorNode(errornode), ConfigurationChangeToken(0), + DoNotPrunePath(false) {} + + BugReport(BugType &bt, StringRef shortDesc, StringRef desc, + const ExplodedNode *errornode, StringRef hashfield = "") + : BT(bt), DeclWithIssue(nullptr), ShortDescription(shortDesc), + Description(desc), HashField(hashfield), ErrorNode(errornode), + ConfigurationChangeToken(0), DoNotPrunePath(false) {} + + BugReport(BugType &bt, StringRef desc, PathDiagnosticLocation l, + StringRef hashfield = "") + : BT(bt), DeclWithIssue(nullptr), Description(desc), HashField(hashfield), + Location(l), ErrorNode(nullptr), ConfigurationChangeToken(0), + DoNotPrunePath(false) {} /// \brief Create a BugReport with a custom uniqueing location. /// @@ -165,8 +170,9 @@ /// for uniquing reports. For example, memory leaks checker, could set this to /// the allocation site, rather then the location where the bug is reported. BugReport(BugType& bt, StringRef desc, const ExplodedNode *errornode, - PathDiagnosticLocation LocationToUnique, const Decl *DeclToUnique) - : BT(bt), DeclWithIssue(nullptr), Description(desc), + PathDiagnosticLocation LocationToUnique, const Decl *DeclToUnique, + StringRef hashfield = "") + : BT(bt), DeclWithIssue(nullptr), Description(desc), HashField(hashfield), UniqueingLocation(LocationToUnique), UniqueingDecl(DeclToUnique), ErrorNode(errornode), ConfigurationChangeToken(0), @@ -181,6 +187,8 @@ StringRef getDescription() const { return Description; } + StringRef getHashField() const { return HashField; } + StringRef getShortDescription(bool UseFallback = true) const { if (ShortDescription.empty() && UseFallback) return Description; Index: include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h =================================================================== --- include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h +++ include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h @@ -720,6 +720,7 @@ std::string BugType; std::string VerboseDesc; std::string ShortDesc; + std::string HashField; std::string Category; std::deque OtherDesc; @@ -739,7 +740,7 @@ PathDiagnostic(StringRef CheckName, const Decl *DeclWithIssue, StringRef bugtype, StringRef verboseDesc, StringRef shortDesc, StringRef category, PathDiagnosticLocation LocationToUnique, - const Decl *DeclToUnique); + const Decl *DeclToUnique, StringRef hashfield); ~PathDiagnostic(); @@ -794,6 +795,7 @@ StringRef getShortDescription() const { return ShortDesc.empty() ? VerboseDesc : ShortDesc; } + StringRef getHashField() const { return HashField; } StringRef getCheckName() const { return CheckName; } StringRef getBugType() const { return BugType; } StringRef getCategory() const { return Category; } Index: lib/StaticAnalyzer/Core/BugId.cpp =================================================================== --- lib/StaticAnalyzer/Core/BugId.cpp +++ lib/StaticAnalyzer/Core/BugId.cpp @@ -0,0 +1,167 @@ +//===---------- BugId.cpp - Unique bugid utility ------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "clang/StaticAnalyzer/Core/BugId.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/Lex/Lexer.h" +#include "clang/Basic/Specifiers.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/Support/LineIterator.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/MD5.h" +#include "llvm/ADT/Twine.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" + +using namespace clang; + +static std::string GetSignature(const FunctionDecl *Target) { + if (!Target) + return ""; + + std::string Signature; + const CXXConstructorDecl *CDecl = dyn_cast(Target); + const CXXDestructorDecl *DDecl = dyn_cast(Target); + const CXXConversionDecl *ConversionDecl = dyn_cast(Target); + + switch (Target->getStorageClass()) { + case SC_Extern: + Signature.append("extern "); + break; + case SC_Static: + Signature.append("static "); + break; + case SC_PrivateExtern: + Signature.append("__private_extern__ "); + break; + default: + break; + } + + if (Target->isInlineSpecified()) + Signature.append("inline "); + if (Target->isVirtualAsWritten()) + Signature.append("virtual "); + if (Target->isModulePrivate()) + Signature.append("__module_private__ "); + if (Target->isConstexpr() && !Target->isExplicitlyDefaulted()) + Signature.append("constexpr "); + if ((CDecl && CDecl->isExplicitSpecified()) || + (ConversionDecl && ConversionDecl->isExplicit())) + Signature.append("explicit "); + + if (!CDecl && !ConversionDecl && !DDecl) + Signature.append(Target->getReturnType().getAsString()).append(" "); + Signature.append(Target->getQualifiedNameAsString()).append("("); + + const FunctionProtoType *TargetPT = + llvm::dyn_cast_or_null(Target->getType().getTypePtr()); + + for (int i = 0, paramsCount = Target->getNumParams(); i < paramsCount; ++i) { + if (i) + Signature.append(", "); + Signature.append(Target->getParamDecl(i)->getType().getAsString()); + } + + if (TargetPT && TargetPT->isVariadic()) + Signature.append(", ..."); + Signature.append(")"); + + if (TargetPT) { + if (TargetPT->isConst()) + Signature.append(" const"); + if (TargetPT->isVolatile()) + Signature.append(" volatile"); + if (TargetPT->isRestrict()) + Signature.append(" restrict"); + + switch (TargetPT->getRefQualifier()) { + case RQ_LValue: + Signature.append(" &"); + break; + case RQ_RValue: + Signature.append(" &&"); + break; + default: + break; + } + } + + return Signature; +} + +static std::string GetEnclosingDeclContextSignature(const Decl *D) { + if (!D) + return ""; + + if (const NamedDecl *ND = dyn_cast(D)) { + std::string DeclName; + + switch (ND->getKind()) { + case Decl::Namespace: + case Decl::Record: + case Decl::CXXRecord: + case Decl::Enum: + DeclName = ND->getQualifiedNameAsString(); + break; + case Decl::CXXConstructor: + case Decl::CXXDestructor: + case Decl::CXXConversion: + case Decl::CXXMethod: + case Decl::ObjCMethod: + case Decl::Function: + DeclName = GetSignature(dyn_cast_or_null(ND)); + break; + default: + break; + } + + return DeclName; + } + + return ""; +} + +static std::string GetNthLineOfFile(llvm::MemoryBuffer *Buffer, int Line) { + if (!Buffer) + return ""; + + llvm::line_iterator LI(*Buffer, false); + for (; !LI.is_at_eof() && LI.line_number() != Line; ++LI) + ; + + return LI->str(); +} + +static llvm::SmallString<32> GetHashOfContent(StringRef Content) { + llvm::MD5 Hash; + llvm::MD5::MD5Result MD5Res; + llvm::SmallString<32> Res; + + Hash.update(Content); + Hash.final(MD5Res); + llvm::MD5::stringifyResult(MD5Res, Res); + + return Res; +} + +llvm::SmallString<32> clang::GetIssueHash(const SourceManager *SM, + FullSourceLoc &L, + StringRef CheckerName, + StringRef HashField, const Decl *D) { + static llvm::StringRef Delimiter = "$"; + + return GetHashOfContent( + (llvm::Twine(llvm::sys::path::filename(SM->getFilename(L))) + Delimiter + + CheckerName + Delimiter + GetEnclosingDeclContextSignature(D) + + Delimiter + std::to_string(L.getExpansionColumnNumber()) + Delimiter + + GetNthLineOfFile(SM->getBuffer(L.getFileID(), L), + L.getExpansionLineNumber()) + + Delimiter + HashField.str()).str()); +} Index: lib/StaticAnalyzer/Core/BugReporter.cpp =================================================================== --- lib/StaticAnalyzer/Core/BugReporter.cpp +++ lib/StaticAnalyzer/Core/BugReporter.cpp @@ -3403,7 +3403,8 @@ exampleReport->getDescription(), exampleReport->getShortDescription(/*Fallback=*/false), BT.getCategory(), exampleReport->getUniqueingLocation(), - exampleReport->getUniqueingDecl())); + exampleReport->getUniqueingDecl(), + exampleReport->getHashField())); MaxBugClassSize = std::max(bugReports.size(), static_cast(MaxBugClassSize)); Index: lib/StaticAnalyzer/Core/CMakeLists.txt =================================================================== --- lib/StaticAnalyzer/Core/CMakeLists.txt +++ lib/StaticAnalyzer/Core/CMakeLists.txt @@ -6,6 +6,7 @@ AnalyzerOptions.cpp BasicValueFactory.cpp BlockCounter.cpp + BugId.cpp BugReporter.cpp BugReporterVisitors.cpp CallEvent.cpp Index: lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp =================================================================== --- lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp +++ lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/StaticAnalyzer/Core/BugId.h" #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" @@ -236,6 +237,9 @@ if (!BugType.empty()) os << "\n\n"; + FullSourceLoc L(SMgr.getExpansionLoc(D.getLocation().asLocation()), SMgr); + const Decl *DeclWithIssue = D.getDeclWithIssue(); + StringRef BugCategory = D.getCategory(); if (!BugCategory.empty()) os << "\n\n"; @@ -245,6 +249,10 @@ os << "\n\n"; os << "\n\n"; + + os << "\n\n"; os << "\n