Index: include/clang/Driver/CC1Options.td =================================================================== --- include/clang/Driver/CC1Options.td +++ include/clang/Driver/CC1Options.td @@ -54,12 +54,16 @@ def analyzer_output_EQ : Joined<["-"], "analyzer-output=">, Alias; +def analyzer_opt_stable_report_filename : Flag<["-"], "analyzer-stable-report-filename">, + HelpText<"Force the generation of 'stable' filename for the HTML report">; + def analyzer_purge : Separate<["-"], "analyzer-purge">, HelpText<"Source Code Analysis - Dead Symbol Removal Frequency">; def analyzer_purge_EQ : Joined<["-"], "analyzer-purge=">, Alias; def analyzer_opt_analyze_headers : Flag<["-"], "analyzer-opt-analyze-headers">, HelpText<"Force the static analyzer to analyze functions defined in header files">; + def analyzer_opt_analyze_nested_blocks : Flag<["-"], "analyzer-opt-analyze-nested-blocks">, HelpText<"Analyze the definitions of blocks in addition to functions">; def analyzer_display_progress : Flag<["-"], "analyzer-display-progress">, Index: include/clang/StaticAnalyzer/Core/AnalyzerOptions.h =================================================================== --- include/clang/StaticAnalyzer/Core/AnalyzerOptions.h +++ include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -141,7 +141,8 @@ unsigned AnalyzeAll : 1; unsigned AnalyzerDisplayProgress : 1; unsigned AnalyzeNestedBlocks : 1; - + unsigned StableReportFilename : 1; + /// \brief The flag regulates if we should eagerly assume evaluations of /// conditionals, thus, bifurcating the path. /// @@ -415,6 +416,7 @@ AnalyzeAll(0), AnalyzerDisplayProgress(0), AnalyzeNestedBlocks(0), + StableReportFilename(0), eagerlyAssumeBinOpBifurcation(0), TrimGraph(0), visualizeExplodedGraphWithGraphViz(0), Index: include/clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h +++ include/clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h @@ -28,7 +28,7 @@ typedef std::vector PathDiagnosticConsumers; #define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN)\ -void CREATEFN(AnalyzerOptions &AnalyzerOpts,\ +void CREATEFN(const AnalyzerOptions &AnalyzerOpts,\ PathDiagnosticConsumers &C,\ const std::string &Prefix,\ const Preprocessor &PP); Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -237,6 +237,7 @@ Opts.InlineMaxStackDepth = getLastArgIntValue(Args, OPT_analyzer_inline_max_stack_depth, Opts.InlineMaxStackDepth, Diags); + Opts.StableReportFilename = Args.hasArg(OPT_analyzer_opt_stable_report_filename); Opts.CheckersControlList.clear(); for (arg_iterator it = Args.filtered_begin(OPT_analyzer_checker, Index: lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp =================================================================== --- lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp +++ lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp @@ -20,6 +20,7 @@ #include "clang/Lex/Preprocessor.h" #include "clang/Rewrite/Core/HTMLRewrite.h" #include "clang/Rewrite/Core/Rewriter.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" @@ -39,8 +40,9 @@ std::string Directory; bool createdDir, noDir; const Preprocessor &PP; + const AnalyzerOptions &AnalyzerOpts; public: - HTMLDiagnostics(const std::string& prefix, const Preprocessor &pp); + HTMLDiagnostics(const AnalyzerOptions &AnalyzerOpts, const std::string& prefix, const Preprocessor &pp); virtual ~HTMLDiagnostics() { FlushDiagnostics(NULL); } @@ -68,16 +70,17 @@ } // end anonymous namespace -HTMLDiagnostics::HTMLDiagnostics(const std::string& prefix, +HTMLDiagnostics::HTMLDiagnostics(const AnalyzerOptions &AnalyzerOpts_, + const std::string& prefix, const Preprocessor &pp) - : Directory(prefix), createdDir(false), noDir(false), PP(pp) { + : Directory(prefix), createdDir(false), noDir(false), PP(pp), AnalyzerOpts(AnalyzerOpts_) { } -void ento::createHTMLDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, +void ento::createHTMLDiagnosticConsumer(const AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, const std::string& prefix, const Preprocessor &PP) { - C.push_back(new HTMLDiagnostics(prefix, PP)); + C.push_back(new HTMLDiagnostics(AnalyzerOpts, prefix, PP)); } //===----------------------------------------------------------------------===// @@ -95,7 +98,7 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, FilesMade *filesMade) { - + // Create the HTML directory if it is missing. if (!createdDir) { createdDir = true; @@ -126,11 +129,30 @@ // Create a new rewriter to generate HTML. Rewriter R(const_cast(SMgr), PP.getLangOpts()); +// Get the function/method name + std::string declName="unknown"; + int offsetDecl = 0; + if (const Decl *DeclWithIssue = D.getDeclWithIssue()) { + if (const NamedDecl *ND = dyn_cast(DeclWithIssue)) { + declName = ND->getDeclName().getAsString(); + } + + if (const Stmt *Body = DeclWithIssue->getBody()) { + // Retrieve the relative position of the declaration which will be used + // for the file name + FullSourceLoc L( + SMgr.getExpansionLoc((*path.rbegin())->getLocation().asLocation()), + SMgr); + FullSourceLoc FunL(SMgr.getExpansionLoc(Body->getLocStart()), SMgr); + offsetDecl = L.getExpansionLineNumber() - FunL.getExpansionLineNumber(); + } + } + // Process the path. unsigned n = path.size(); unsigned max = n; - for (PathPieces::const_reverse_iterator I = path.rbegin(), + for (PathPieces::const_reverse_iterator I = path.rbegin(), E = path.rend(); I != E; ++I, --n) HandlePiece(R, FID, **I, n, max); @@ -163,6 +185,9 @@ DirName += '/'; } + int LineNumber = (*path.rbegin())->getLocation().asLocation().getExpansionLineNumber(); + int ColumnNumber = (*path.rbegin())->getLocation().asLocation().getExpansionColumnNumber(); + // Add the name of the file as an

tag. { @@ -176,9 +201,9 @@ << html::EscapeText(Entry->getName()) << "\nLocation:" "line " - << (*path.rbegin())->getLocation().asLocation().getExpansionLineNumber() + << LineNumber << ", column " - << (*path.rbegin())->getLocation().asLocation().getExpansionColumnNumber() + << ColumnNumber << "\n" "Description:" << D.getVerboseDescription() << "\n"; @@ -216,11 +241,11 @@ os << "\n\n"; os << "\n\n"; os << "\n\n"; os << "\n\n"; @@ -247,13 +272,33 @@ // Create a path for the target HTML file. int FD; SmallString<128> Model, ResultPath; - llvm::sys::path::append(Model, Directory, "report-%%%%%%.html"); - if (llvm::error_code EC = + if (!AnalyzerOpts.StableReportFilename) { + llvm::sys::path::append(Model, Directory, "report-%%%%%%.html"); + + + if (llvm::error_code EC = llvm::sys::fs::createUniqueFile(Model.str(), FD, ResultPath)) { - llvm::errs() << "warning: could not create file in '" << Directory - << "': " << EC.message() << '\n'; - return; + llvm::errs() << "warning: could not create file in '" << Directory + << "': " << EC.message() << '\n'; + return; + } + + } else { + + int i = 1; + do { + Model=""; + llvm::sys::path::append(Model, Directory, "report-" + llvm::sys::path::filename(Entry->getName()) + "-" + declName + "-" + std::to_string(offsetDecl) + "-" + std::to_string(i) + ".html") ; + i++; + } while (llvm::sys::fs::exists(Model.str())); + + if (llvm::error_code EC = + llvm::sys::fs::openFileForWrite(Model.str(), FD, llvm::sys::fs::OpenFlags::F_RW)) { + llvm::errs() << "warning: could not create file '" << Model.str() + << "': " << EC.message() << '\n'; + return; + } } llvm::raw_fd_ostream os(FD, true); Index: lib/StaticAnalyzer/Core/PlistDiagnostics.cpp =================================================================== --- lib/StaticAnalyzer/Core/PlistDiagnostics.cpp +++ lib/StaticAnalyzer/Core/PlistDiagnostics.cpp @@ -32,7 +32,7 @@ const LangOptions &LangOpts; const bool SupportsCrossFileDiagnostics; public: - PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, + PlistDiagnostics(const AnalyzerOptions &AnalyzerOpts, const std::string& prefix, const LangOptions &LangOpts, bool supportsMultipleFiles); @@ -56,7 +56,7 @@ }; } // end anonymous namespace -PlistDiagnostics::PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, +PlistDiagnostics::PlistDiagnostics(const AnalyzerOptions &AnalyzerOpts, const std::string& output, const LangOptions &LO, bool supportsMultipleFiles) @@ -64,7 +64,7 @@ LangOpts(LO), SupportsCrossFileDiagnostics(supportsMultipleFiles) {} -void ento::createPlistDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, +void ento::createPlistDiagnosticConsumer(const AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, const std::string& s, const Preprocessor &PP) { @@ -72,7 +72,7 @@ PP.getLangOpts(), false)); } -void ento::createPlistMultiFileDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, +void ento::createPlistMultiFileDiagnosticConsumer(const AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, const std::string &s, const Preprocessor &PP) { Index: lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp =================================================================== --- lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -66,7 +66,7 @@ // Special PathDiagnosticConsumers. //===----------------------------------------------------------------------===// -void ento::createPlistHTMLDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, +void ento::createPlistHTMLDiagnosticConsumer(const AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, const std::string &prefix, const Preprocessor &PP) { @@ -75,7 +75,7 @@ createPlistDiagnosticConsumer(AnalyzerOpts, C, prefix, PP); } -void ento::createTextPathDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, +void ento::createTextPathDiagnosticConsumer(const AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, const std::string &Prefix, const clang::Preprocessor &PP) { Index: tools/scan-build/scan-build =================================================================== --- tools/scan-build/scan-build +++ tools/scan-build/scan-build @@ -498,6 +498,8 @@ sub FileWanted { my $baseDirRegEx = quotemeta $baseDir; my $file = $File::Find::name; + + # The name of the file is generated by clang binary (HTMLDiagnostics.cpp) if ($file =~ /report-.*\.html$/) { my $relative_file = $file; $relative_file =~ s/$baseDirRegEx//g; @@ -1001,6 +1003,7 @@ my $CCAnalyzer = shift; my $CXXAnalyzer = shift; my $Options = shift; + my $StableFilename = shift; if ($Cmd =~ /\bxcodebuild$/) { return RunXcodebuild($Args, $IgnoreErrors, $CCAnalyzer, $CXXAnalyzer, $Options); @@ -1176,6 +1179,11 @@ Provide options to pass through to the analyzer's -analyzer-config flag. + --stable-report-filename + + Switch the page naming to report---.html + instead of report-XXXXXX.html. Added in clang 3.5 + CONTROLLING CHECKERS: A default group of checkers are always run unless explicitly disabled. @@ -1343,6 +1351,7 @@ my $ViewResults = 0; # View results when the build terminates. my $ExitStatusFoundBugs = 0; # Exit status reflects whether bugs were found my $KeepEmpty = 0; # Don't remove output directory even with 0 results. +my $StableFilename = 0; # Stable page name my @AnalysesToRun; my $StoreModel; my $ConstraintsModel; @@ -1377,6 +1386,12 @@ next; } + if ($arg eq "--stable-report-filename") { + shift @ARGV; + $StableFilename = 1; + next; + } + if ($arg eq "-o") { shift @ARGV; @@ -1649,6 +1664,7 @@ if ($AnalyzeHeaders) { push @AnalysesToRun,"-analyzer-opt-analyze-headers"; } if ($AnalyzerStats) { push @AnalysesToRun, '-analyzer-checker=debug.Stats'; } if ($MaxLoop > 0) { push @AnalysesToRun, "-analyzer-max-loop $MaxLoop"; } +if ($StableFilename) { push @AnalysesToRun,"-analyzer-stable-report-filename"; } # Delay setting up other environment variables in case we can do true # interposition.