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/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/PathDiagnostic.cpp =================================================================== --- lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -111,12 +111,13 @@ StringRef bugtype, StringRef verboseDesc, StringRef shortDesc, StringRef category, PathDiagnosticLocation LocationToUnique, - const Decl *DeclToUnique) + const Decl *DeclToUnique, StringRef hashfield) : CheckName(CheckName), DeclWithIssue(declWithIssue), BugType(StripTrailingDots(bugtype)), VerboseDesc(StripTrailingDots(verboseDesc)), ShortDesc(StripTrailingDots(shortDesc)), + HashField(hashfield), Category(StripTrailingDots(category)), UniqueingLoc(LocationToUnique), UniqueingDecl(DeclToUnique), Index: lib/StaticAnalyzer/Core/PlistDiagnostics.cpp =================================================================== --- lib/StaticAnalyzer/Core/PlistDiagnostics.cpp +++ lib/StaticAnalyzer/Core/PlistDiagnostics.cpp @@ -11,17 +11,25 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/PlistSupport.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/Version.h" +#include "clang/Basic/Specifiers.h" +#include "clang/Lex/Lexer.h" #include "clang/Lex/Preprocessor.h" +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" +#include "llvm/ADT/Twine.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" -#include "llvm/Support/Casting.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/LineIterator.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/MD5.h" using namespace clang; using namespace ento; using namespace markup; @@ -285,6 +293,151 @@ } } +static std::string GetSignature(const clang::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; +} + +static llvm::SmallString<32> 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()); +} + void PlistDiagnostics::FlushDiagnosticsImpl( std::vector &Diags, FilesMade *filesMade) { @@ -390,6 +543,12 @@ o << " check_name"; EmitString(o, D->getCheckName()) << '\n'; + o << " bug_id_1"; + FullSourceLoc L(SM->getExpansionLoc(D->getLocation().asLocation()), *SM); + const Decl *DeclWithIssue = D->getDeclWithIssue(); + EmitString(o, GetIssueHash(SM, L, D->getCheckName(), D->getHashField(), + DeclWithIssue).str()) << '\n'; + // Output information about the semantic context where // the issue occurred. if (const Decl *DeclWithIssue = D->getDeclWithIssue()) {