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: lib/StaticAnalyzer/Core/BugId.cpp =================================================================== --- lib/StaticAnalyzer/Core/BugId.cpp +++ lib/StaticAnalyzer/Core/BugId.cpp @@ -0,0 +1,198 @@ +//===---------- 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" +#include "clang/AST/ASTContext.h" + +#include +#include +#include + +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 std::string NormalizeLine(const SourceManager *SM, FullSourceLoc &L, + const Decl *D) { + static const std::string whitespaces = " \t\n"; + + const LangOptions &Opts = D->getASTContext().getLangOpts(); + std::string str = GetNthLineOfFile(SM->getBuffer(L.getFileID(), L), L.getExpansionLineNumber()); + unsigned col = str.find_first_not_of(whitespaces); + + SourceLocation StartOfLine = SM->translateLineCol(SM->getFileID(L), L.getExpansionLineNumber(), col); + llvm::MemoryBuffer *Buffer = SM->getBuffer(SM->getFileID(StartOfLine), StartOfLine); + if (!Buffer) return {}; + + const char *BufferPos = SM->getCharacterData(StartOfLine); + + Token Token; + Lexer Lexer(SM->getLocForStartOfFile(SM->getFileID(StartOfLine)), Opts, + Buffer->getBufferStart(), BufferPos, Buffer->getBufferEnd()); + + size_t nextStart = 0; + std::ostringstream lineBuff; + while (!Lexer.LexFromRawLexer(Token) && nextStart < 2) { + if (Token.isAtStartOfLine() && nextStart++ > 0) continue; + lineBuff << std::string(SM->getCharacterData(Token.getLocation()), Token.getLength()); + } + + return lineBuff.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(CheckerName) + Delimiter + GetEnclosingDeclContextSignature(D) + + Delimiter + std::to_string(L.getExpansionColumnNumber()) + Delimiter + + NormalizeLine(SM, L, D) + + Delimiter + HashField.str()).str()); +} 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,11 @@ if (!BugType.empty()) os << "\n\n"; + PathDiagnosticLocation UPDLoc = D.getUniqueingLoc(); + FullSourceLoc UL(SMgr.getExpansionLoc(UPDLoc.asLocation()), SMgr); + FullSourceLoc L(SMgr.getExpansionLoc(D.getLocation().asLocation()), SMgr); + const Decl *DeclWithIssue = D.getDeclWithIssue(); + StringRef BugCategory = D.getCategory(); if (!BugCategory.empty()) os << "\n\n"; @@ -246,6 +252,10 @@ os << "\n\n"; + os << "\n\n"; + os << "\n\n"; Index: lib/StaticAnalyzer/Core/PlistDiagnostics.cpp =================================================================== --- lib/StaticAnalyzer/Core/PlistDiagnostics.cpp +++ lib/StaticAnalyzer/Core/PlistDiagnostics.cpp @@ -11,12 +11,13 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" +#include "clang/StaticAnalyzer/Core/BugId.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/PlistSupport.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/Version.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/DenseMap.h" @@ -210,7 +211,7 @@ unsigned depth) { IntrusiveRefCntPtr callEnter = - P.getCallEnterEvent(); + P.getCallEnterEvent(); if (callEnter) ReportPiece(o, *callEnter, FM, SM, LangOpts, indent, depth, true, @@ -224,7 +225,7 @@ if (callEnterWithinCaller) ReportPiece(o, *callEnterWithinCaller, FM, SM, LangOpts, indent, depth, true); - + for (PathPieces::const_iterator I = P.path.begin(), E = P.path.end();I!=E;++I) ReportPiece(o, **I, FM, SM, LangOpts, indent, depth, true); @@ -296,8 +297,8 @@ if (!Diags.empty()) SM = &(*(*Diags.begin())->path.begin())->getLocation().getManager(); - + for (std::vector::iterator DI = Diags.begin(), DE = Diags.end(); DI != DE; ++DI) { @@ -390,6 +391,14 @@ o << " check_name"; EmitString(o, D->getCheckName()) << '\n'; + o << " bug_id_1"; + PathDiagnosticLocation UPDLoc = D->getUniqueingLoc(); + FullSourceLoc UL(SM->getExpansionLoc(UPDLoc.asLocation()), *SM); + FullSourceLoc L(SM->getExpansionLoc(D->getLocation().asLocation()), *SM); + const Decl *DeclWithIssue = D->getDeclWithIssue(); + EmitString(o, GetIssueHash(SM, UPDLoc.isValid() ? UL : L, D->getCheckName(), + D->getBugType(), DeclWithIssue).str()) << '\n'; + // Output information about the semantic context where // the issue occurred. if (const Decl *DeclWithIssue = D->getDeclWithIssue()) {