diff options
Diffstat (limited to 'clang/tools/scan-build')
| l--------- | clang/tools/scan-build/c++-analyzer | 1 | ||||
| -rwxr-xr-x | clang/tools/scan-build/ccc-analyzer | 686 | ||||
| -rwxr-xr-x | clang/tools/scan-build/scan-build | 1436 | ||||
| -rw-r--r-- | clang/tools/scan-build/scanview.css | 62 | ||||
| -rwxr-xr-x | clang/tools/scan-build/set-xcode-analyzer | 89 | ||||
| -rw-r--r-- | clang/tools/scan-build/sorttable.js | 493 | 
6 files changed, 2767 insertions, 0 deletions
| diff --git a/clang/tools/scan-build/c++-analyzer b/clang/tools/scan-build/c++-analyzer new file mode 120000 index 0000000..ca10bf5 --- /dev/null +++ b/clang/tools/scan-build/c++-analyzer @@ -0,0 +1 @@ +ccc-analyzer
\ No newline at end of file diff --git a/clang/tools/scan-build/ccc-analyzer b/clang/tools/scan-build/ccc-analyzer new file mode 100755 index 0000000..d01bd0f --- /dev/null +++ b/clang/tools/scan-build/ccc-analyzer @@ -0,0 +1,686 @@ +#!/usr/bin/env perl +# +#                     The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +# +#  A script designed to interpose between the build system and gcc.  It invokes +#  both gcc and the static analyzer. +# +##===----------------------------------------------------------------------===## + +use strict; +use warnings; +use FindBin; +use Cwd qw/ getcwd abs_path /; +use File::Temp qw/ tempfile /; +use File::Path qw / mkpath /; +use File::Basename; +use Text::ParseWords; + +##===----------------------------------------------------------------------===## +# Compiler command setup. +##===----------------------------------------------------------------------===## + +my $Compiler; +my $Clang; +my $DefaultCCompiler; +my $DefaultCXXCompiler; + +if (`uname -a` =~ m/Darwin/) {  +	$DefaultCCompiler = 'clang'; +	$DefaultCXXCompiler = 'clang++';  +} else { +    $DefaultCCompiler = 'gcc'; +    $DefaultCXXCompiler = 'g++'; 	 +} + +if ($FindBin::Script =~ /c\+\+-analyzer/) { +  $Compiler = $ENV{'CCC_CXX'}; +  if (!defined $Compiler) { $Compiler = $DefaultCXXCompiler; } +   +  $Clang = $ENV{'CLANG_CXX'}; +  if (!defined $Clang) { $Clang = 'clang++'; } +} +else { +  $Compiler = $ENV{'CCC_CC'}; +  if (!defined $Compiler) { $Compiler = $DefaultCCompiler; } + +  $Clang = $ENV{'CLANG'}; +  if (!defined $Clang) { $Clang = 'clang'; } +} + +##===----------------------------------------------------------------------===## +# Cleanup. +##===----------------------------------------------------------------------===## + +my $ReportFailures = $ENV{'CCC_REPORT_FAILURES'}; +if (!defined $ReportFailures) { $ReportFailures = 1; } + +my $CleanupFile; +my $ResultFile; + +# Remove any stale files at exit. +END {  +  if (defined $ResultFile && -z $ResultFile) { +    `rm -f $ResultFile`; +  } +  if (defined $CleanupFile) { +    `rm -f $CleanupFile`; +  } +} + +##----------------------------------------------------------------------------## +#  Process Clang Crashes. +##----------------------------------------------------------------------------## + +sub GetPPExt { +  my $Lang = shift; +  if ($Lang =~ /objective-c\+\+/) { return ".mii" }; +  if ($Lang =~ /objective-c/) { return ".mi"; } +  if ($Lang =~ /c\+\+/) { return ".ii"; } +  return ".i"; +} + +# Set this to 1 if we want to include 'parser rejects' files. +my $IncludeParserRejects = 0; +my $ParserRejects = "Parser Rejects"; +my $AttributeIgnored = "Attribute Ignored"; +my $OtherError = "Other Error"; + +sub ProcessClangFailure { +  my ($Clang, $Lang, $file, $Args, $HtmlDir, $ErrorType, $ofile) = @_; +  my $Dir = "$HtmlDir/failures"; +  mkpath $Dir; +   +  my $prefix = "clang_crash"; +  if ($ErrorType eq $ParserRejects) { +    $prefix = "clang_parser_rejects"; +  } +  elsif ($ErrorType eq $AttributeIgnored) { +    $prefix = "clang_attribute_ignored"; +  } +  elsif ($ErrorType eq $OtherError) { +    $prefix = "clang_other_error"; +  } + +  # Generate the preprocessed file with Clang. +  my ($PPH, $PPFile) = tempfile( $prefix . "_XXXXXX", +                                 SUFFIX => GetPPExt($Lang), +                                 DIR => $Dir); +  system $Clang, @$Args, "-E", "-o", $PPFile; +  close ($PPH); +   +  # Create the info file. +  open (OUT, ">", "$PPFile.info.txt") or die "Cannot open $PPFile.info.txt\n"; +  print OUT abs_path($file), "\n"; +  print OUT "$ErrorType\n"; +  print OUT "@$Args\n"; +  close OUT; +  `uname -a >> $PPFile.info.txt 2>&1`; +  `$Compiler -v >> $PPFile.info.txt 2>&1`; +  system 'mv',$ofile,"$PPFile.stderr.txt"; +  return (basename $PPFile); +} + +##----------------------------------------------------------------------------## +#  Running the analyzer. +##----------------------------------------------------------------------------## + +sub GetCCArgs { +  my $mode = shift; +  my $Args = shift; +   +  pipe (FROM_CHILD, TO_PARENT); +  my $pid = fork(); +  if ($pid == 0) { +    close FROM_CHILD; +    open(STDOUT,">&", \*TO_PARENT); +    open(STDERR,">&", \*TO_PARENT); +    exec $Clang, "-###", $mode, @$Args; +  }   +  close(TO_PARENT); +  my $line; +  while (<FROM_CHILD>) { +    next if (!/-cc1/); +    $line = $_; +  } + +  waitpid($pid,0); +  close(FROM_CHILD); +   +  die "could not find clang line\n" if (!defined $line); +  # Strip the newline and initial whitspace +  chomp $line; +  $line =~ s/^\s+//; +  my @items = quotewords('\s+', 0, $line); +  my $cmd = shift @items; +  die "cannot find 'clang' in 'clang' command\n" if (!($cmd =~ /clang/)); +  return \@items; +} + +sub Analyze { +  my ($Clang, $OriginalArgs, $AnalyzeArgs, $Lang, $Output, $Verbose, $HtmlDir, +      $file) = @_; + +  my @Args = @$OriginalArgs; +  my $Cmd; +  my @CmdArgs; +  my @CmdArgsSansAnalyses; + +  if ($Lang =~ /header/) { +    exit 0 if (!defined ($Output)); +    $Cmd = 'cp'; +    push @CmdArgs, $file; +    # Remove the PCH extension. +    $Output =~ s/[.]gch$//; +    push @CmdArgs, $Output; +    @CmdArgsSansAnalyses = @CmdArgs; +  } +  else { +    $Cmd = $Clang; +    if ($Lang eq "objective-c" || $Lang eq "objective-c++") { +      push @Args,'-DIBOutlet=__attribute__((iboutlet))'; +      push @Args,'-DIBOutletCollection(ClassName)=__attribute__((iboutletcollection)))'; +      push @Args,'-DIBAction=void)__attribute__((ibaction)'; +    } + +    # Create arguments for doing regular parsing. +    my $SyntaxArgs = GetCCArgs("-fsyntax-only", \@Args); +    @CmdArgsSansAnalyses = @$SyntaxArgs; + +    # Create arguments for doing static analysis. +    if (defined $ResultFile) { +      push @Args, '-o', $ResultFile; +    } +    elsif (defined $HtmlDir) { +      push @Args, '-o', $HtmlDir; +    } +    if ($Verbose) { +      push @Args, "-Xclang", "-analyzer-display-progress"; +    } + +    foreach my $arg (@$AnalyzeArgs) { +      push @Args, "-Xclang", $arg; +    } + +    # Display Ubiviz graph? +    if (defined $ENV{'CCC_UBI'}) {    +      push @Args, "-Xclang", "-analyzer-viz-egraph-ubigraph"; +    } + +    my $AnalysisArgs = GetCCArgs("--analyze", \@Args); +    @CmdArgs = @$AnalysisArgs; +  } + +  my @PrintArgs; +  my $dir; + +  if ($Verbose) { +    $dir = getcwd(); +    print STDERR "\n[LOCATION]: $dir\n"; +    push @PrintArgs,"'$Cmd'"; +    foreach my $arg (@CmdArgs) { +        push @PrintArgs,"\'$arg\'"; +    } +  } +  if ($Verbose == 1) { +    # We MUST print to stderr.  Some clients use the stdout output of +    # gcc for various purposes.  +    print STDERR join(' ', @PrintArgs); +    print STDERR "\n"; +  } +  elsif ($Verbose == 2) { +    print STDERR "#SHELL (cd '$dir' && @PrintArgs)\n"; +  } + +  # Capture the STDERR of clang and send it to a temporary file. +  # Capture the STDOUT of clang and reroute it to ccc-analyzer's STDERR. +  # We save the output file in the 'crashes' directory if clang encounters +  # any problems with the file.   +  pipe (FROM_CHILD, TO_PARENT); +  my $pid = fork(); +  if ($pid == 0) { +    close FROM_CHILD; +    open(STDOUT,">&", \*TO_PARENT); +    open(STDERR,">&", \*TO_PARENT); +    exec $Cmd, @CmdArgs; +  } + +  close TO_PARENT; +  my ($ofh, $ofile) = tempfile("clang_output_XXXXXX", DIR => $HtmlDir); +   +  while (<FROM_CHILD>) { +    print $ofh $_; +    print STDERR $_; +  } + +  waitpid($pid,0); +  close(FROM_CHILD); +  my $Result = $?; + +  # Did the command die because of a signal? +  if ($ReportFailures) { +    if ($Result & 127 and $Cmd eq $Clang and defined $HtmlDir) { +      ProcessClangFailure($Clang, $Lang, $file, \@CmdArgsSansAnalyses, +                          $HtmlDir, "Crash", $ofile); +    } +    elsif ($Result) { +      if ($IncludeParserRejects && !($file =~/conftest/)) { +        ProcessClangFailure($Clang, $Lang, $file, \@CmdArgsSansAnalyses, +                            $HtmlDir, $ParserRejects, $ofile); +      } else { +        ProcessClangFailure($Clang, $Lang, $file, \@CmdArgsSansAnalyses, +                            $HtmlDir, $OtherError, $ofile);      	 +      } +    } +    else { +      # Check if there were any unhandled attributes. +      if (open(CHILD, $ofile)) { +        my %attributes_not_handled; + +        # Don't flag warnings about the following attributes that we +        # know are currently not supported by Clang. +        $attributes_not_handled{"cdecl"} = 1; + +        my $ppfile; +        while (<CHILD>) { +          next if (! /warning: '([^\']+)' attribute ignored/); + +          # Have we already spotted this unhandled attribute? +          next if (defined $attributes_not_handled{$1}); +          $attributes_not_handled{$1} = 1; +         +          # Get the name of the attribute file. +          my $dir = "$HtmlDir/failures"; +          my $afile = "$dir/attribute_ignored_$1.txt"; +         +          # Only create another preprocessed file if the attribute file +          # doesn't exist yet. +          next if (-e $afile); +         +          # Add this file to the list of files that contained this attribute. +          # Generate a preprocessed file if we haven't already. +          if (!(defined $ppfile)) { +            $ppfile = ProcessClangFailure($Clang, $Lang, $file, +                                          \@CmdArgsSansAnalyses, +                                          $HtmlDir, $AttributeIgnored, $ofile); +          } + +          mkpath $dir; +          open(AFILE, ">$afile"); +          print AFILE "$ppfile\n"; +          close(AFILE); +        } +        close CHILD; +      } +    } +  } +   +  unlink($ofile); +} + +##----------------------------------------------------------------------------## +#  Lookup tables. +##----------------------------------------------------------------------------## + +my %CompileOptionMap = ( +  '-nostdinc' => 0, +  '-fblocks' => 0, +  '-fno-builtin' => 0, +  '-fobjc-gc-only' => 0, +  '-fobjc-gc' => 0, +  '-ffreestanding' => 0, +  '-include' => 1, +  '-idirafter' => 1, +  '-imacros' => 1, +  '-iprefix' => 1, +  '-iquote' => 1, +  '-isystem' => 1, +  '-iwithprefix' => 1, +  '-iwithprefixbefore' => 1 +); + +my %LinkerOptionMap = ( +  '-framework' => 1, +  '-fobjc-link-runtime' => 0 +); + +my %CompilerLinkerOptionMap = ( +  '-fobjc-arc' => 0, +  '-fobjc-abi-version' => 0, # This is really a 1 argument, but always has '=' +  '-isysroot' => 1, +  '-arch' => 1, +  '-m32' => 0, +  '-m64' => 0, +  '-v' => 0, +  '-fpascal-strings' => 0, +  '-mmacosx-version-min' => 0, # This is really a 1 argument, but always has '=' +  '-miphoneos-version-min' => 0 # This is really a 1 argument, but always has '=' +); + +my %IgnoredOptionMap = ( +  '-MT' => 1,  # Ignore these preprocessor options. +  '-MF' => 1, + +  '-fsyntax-only' => 0, +  '-save-temps' => 0, +  '-install_name' => 1, +  '-exported_symbols_list' => 1, +  '-current_version' => 1, +  '-compatibility_version' => 1, +  '-init' => 1, +  '-e' => 1, +  '-seg1addr' => 1, +  '-bundle_loader' => 1, +  '-multiply_defined' => 1, +  '-sectorder' => 3, +  '--param' => 1, +  '-u' => 1, +  '--serialize-diagnostics' => 1 +); + +my %LangMap = ( +  'c'   => 'c', +  'cp'  => 'c++', +  'cpp' => 'c++', +  'cxx' => 'c++', +  'txx' => 'c++', +  'cc'  => 'c++', +  'ii'  => 'c++', +  'i'   => 'c-cpp-output', +  'm'   => 'objective-c', +  'mi'  => 'objective-c-cpp-output', +  'mm'  => 'objective-c++' +); + +my %UniqueOptions = ( +  '-isysroot' => 0   +); + +##----------------------------------------------------------------------------## +# Languages accepted. +##----------------------------------------------------------------------------## + +my %LangsAccepted = ( +  "objective-c" => 1, +  "c" => 1, +  "c++" => 1, +  "objective-c++" => 1 +); + +##----------------------------------------------------------------------------## +#  Main Logic. +##----------------------------------------------------------------------------## + +my $Action = 'link'; +my @CompileOpts; +my @LinkOpts; +my @Files; +my $Lang; +my $Output; +my %Uniqued; + +# Forward arguments to gcc. +my $Status = system($Compiler,@ARGV); +if  (defined $ENV{'CCC_ANALYZER_LOG'}) { +  print "$Compiler @ARGV\n"; +} +if ($Status) { exit($Status >> 8); } + +# Get the analysis options. +my $Analyses = $ENV{'CCC_ANALYZER_ANALYSIS'}; + +# Get the store model. +my $StoreModel = $ENV{'CCC_ANALYZER_STORE_MODEL'}; + +# Get the constraints engine. +my $ConstraintsModel = $ENV{'CCC_ANALYZER_CONSTRAINTS_MODEL'}; + +# Get the output format. +my $OutputFormat = $ENV{'CCC_ANALYZER_OUTPUT_FORMAT'}; +if (!defined $OutputFormat) { $OutputFormat = "html"; } + +# Determine the level of verbosity. +my $Verbose = 0; +if (defined $ENV{CCC_ANALYZER_VERBOSE}) { $Verbose = 1; } +if (defined $ENV{CCC_ANALYZER_LOG}) { $Verbose = 2; } + +# Get the HTML output directory. +my $HtmlDir = $ENV{'CCC_ANALYZER_HTML'}; + +my %DisabledArchs = ('ppc' => 1, 'ppc64' => 1); +my %ArchsSeen; +my $HadArch = 0; + +# Process the arguments. +foreach (my $i = 0; $i < scalar(@ARGV); ++$i) { +  my $Arg = $ARGV[$i];   +  my ($ArgKey) = split /=/,$Arg,2; + +  # Modes ccc-analyzer supports +  if ($Arg =~ /^-(E|MM?)$/) { $Action = 'preprocess'; } +  elsif ($Arg eq '-c') { $Action = 'compile'; } +  elsif ($Arg =~ /^-print-prog-name/) { exit 0; } + +  # Specially handle duplicate cases of -arch +  if ($Arg eq "-arch") { +    my $arch = $ARGV[$i+1]; +    # We don't want to process 'ppc' because of Clang's lack of support +    # for Altivec (also some #defines won't likely be defined correctly, etc.) +    if (!(defined $DisabledArchs{$arch})) { $ArchsSeen{$arch} = 1; } +    $HadArch = 1; +    ++$i; +    next; +  } + +  # Options with possible arguments that should pass through to compiler. +  if (defined $CompileOptionMap{$ArgKey}) { +    my $Cnt = $CompileOptionMap{$ArgKey}; +    push @CompileOpts,$Arg; +    while ($Cnt > 0) { ++$i; --$Cnt; push @CompileOpts, $ARGV[$i]; } +    next; +  } + +  # Options with possible arguments that should pass through to linker. +  if (defined $LinkerOptionMap{$ArgKey}) { +    my $Cnt = $LinkerOptionMap{$ArgKey}; +    push @LinkOpts,$Arg; +    while ($Cnt > 0) { ++$i; --$Cnt; push @LinkOpts, $ARGV[$i]; } +    next; +  } + +  # Options with possible arguments that should pass through to both compiler +  # and the linker. +  if (defined $CompilerLinkerOptionMap{$ArgKey}) { +    my $Cnt = $CompilerLinkerOptionMap{$ArgKey}; +     +    # Check if this is an option that should have a unique value, and if so +    # determine if the value was checked before. +    if ($UniqueOptions{$Arg}) { +      if (defined $Uniqued{$Arg}) { +        $i += $Cnt; +        next; +      } +      $Uniqued{$Arg} = 1; +    } +     +    push @CompileOpts,$Arg;     +    push @LinkOpts,$Arg; + +    while ($Cnt > 0) { +      ++$i; --$Cnt; +      push @CompileOpts, $ARGV[$i]; +      push @LinkOpts, $ARGV[$i]; +    } +    next; +  } +   +  # Ignored options. +  if (defined $IgnoredOptionMap{$ArgKey}) { +    my $Cnt = $IgnoredOptionMap{$ArgKey}; +    while ($Cnt > 0) { +      ++$i; --$Cnt; +    } +    next; +  } +   +  # Compile mode flags. +  if ($Arg =~ /^-[D,I,U](.*)$/) { +    my $Tmp = $Arg;     +    if ($1 eq '') { +      # FIXME: Check if we are going off the end. +      ++$i; +      $Tmp = $Arg . $ARGV[$i]; +    } +    push @CompileOpts,$Tmp; +    next; +  } +   +  # Language. +  if ($Arg eq '-x') { +    $Lang = $ARGV[$i+1]; +    ++$i; next; +  } + +  # Output file. +  if ($Arg eq '-o') { +    ++$i; +    $Output = $ARGV[$i]; +    next; +  } +   +  # Get the link mode. +  if ($Arg =~ /^-[l,L,O]/) { +    if ($Arg eq '-O') { push @LinkOpts,'-O1'; } +    elsif ($Arg eq '-Os') { push @LinkOpts,'-O2'; } +    else { push @LinkOpts,$Arg; } +    next; +  } +   +  if ($Arg =~ /^-std=/) { +    push @CompileOpts,$Arg; +    next; +  } +   +#  if ($Arg =~ /^-f/) { +#    # FIXME: Not sure if the remaining -fxxxx options have no arguments. +#    push @CompileOpts,$Arg; +#    push @LinkOpts,$Arg;  # FIXME: Not sure if these are link opts. +#  } +   +  # Get the compiler/link mode. +  if ($Arg =~ /^-F(.+)$/) { +    my $Tmp = $Arg; +    if ($1 eq '') { +      # FIXME: Check if we are going off the end. +      ++$i; +      $Tmp = $Arg . $ARGV[$i]; +    } +    push @CompileOpts,$Tmp; +    push @LinkOpts,$Tmp; +    next; +  } + +  # Input files. +  if ($Arg eq '-filelist') { +    # FIXME: Make sure we aren't walking off the end. +    open(IN, $ARGV[$i+1]); +    while (<IN>) { s/\015?\012//; push @Files,$_; } +    close(IN); +    ++$i; +    next; +  } +   +  # Handle -Wno-.  We don't care about extra warnings, but +  # we should suppress ones that we don't want to see. +  if ($Arg =~ /^-Wno-/) { +    push @CompileOpts, $Arg; +    next; +  } + +  if (!($Arg =~ /^-/)) { +    push @Files, $Arg; +    next; +  } +} + +if ($Action eq 'compile' or $Action eq 'link') { +  my @Archs = keys %ArchsSeen; +  # Skip the file if we don't support the architectures specified. +  exit 0 if ($HadArch && scalar(@Archs) == 0); +   +  foreach my $file (@Files) { +    # Determine the language for the file. +    my $FileLang = $Lang; + +    if (!defined($FileLang)) { +      # Infer the language from the extension. +      if ($file =~ /[.]([^.]+)$/) { +        $FileLang = $LangMap{$1}; +      } +    } +     +    # FileLang still not defined?  Skip the file. +    next if (!defined $FileLang); + +    # Language not accepted? +    next if (!defined $LangsAccepted{$FileLang}); + +    my @CmdArgs; +    my @AnalyzeArgs;     +     +    if ($FileLang ne 'unknown') { +      push @CmdArgs, '-x', $FileLang; +    } + +    if (defined $StoreModel) { +      push @AnalyzeArgs, "-analyzer-store=$StoreModel"; +    } + +    if (defined $ConstraintsModel) { +      push @AnalyzeArgs, "-analyzer-constraints=$ConstraintsModel"; +    } +     +    if (defined $Analyses) { +      push @AnalyzeArgs, split '\s+', $Analyses; +    } + +    if (defined $OutputFormat) { +      push @AnalyzeArgs, "-analyzer-output=" . $OutputFormat; +      if ($OutputFormat =~ /plist/) { +        # Change "Output" to be a file. +        my ($h, $f) = tempfile("report-XXXXXX", SUFFIX => ".plist", +                               DIR => $HtmlDir); +        $ResultFile = $f; +        # If the HtmlDir is not set, we sould clean up the plist files. +        if (!defined $HtmlDir || -z $HtmlDir) { +        	$CleanupFile = $f;  +        } +      } +    } + +    push @CmdArgs, @CompileOpts; +    push @CmdArgs, $file; + +    if (scalar @Archs) { +      foreach my $arch (@Archs) { +        my @NewArgs; +        push @NewArgs, '-arch', $arch; +        push @NewArgs, @CmdArgs; +        Analyze($Clang, \@NewArgs, \@AnalyzeArgs, $FileLang, $Output, +                $Verbose, $HtmlDir, $file); +      } +    } +    else { +      Analyze($Clang, \@CmdArgs, \@AnalyzeArgs, $FileLang, $Output, +              $Verbose, $HtmlDir, $file); +    } +  } +} + +exit($Status >> 8); + diff --git a/clang/tools/scan-build/scan-build b/clang/tools/scan-build/scan-build new file mode 100755 index 0000000..59b0baf --- /dev/null +++ b/clang/tools/scan-build/scan-build @@ -0,0 +1,1436 @@ +#!/usr/bin/env perl +# +#                     The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +# +# A script designed to wrap a build so that all calls to gcc are intercepted +# and piped to the static analyzer. +# +##===----------------------------------------------------------------------===## + +use strict; +use warnings; +use FindBin qw($RealBin); +use Digest::MD5; +use File::Basename; +use Term::ANSIColor; +use Term::ANSIColor qw(:constants); +use Cwd qw/ getcwd abs_path /; +use Sys::Hostname; + +my $Verbose = 0;       # Verbose output from this script. +my $Prog = "scan-build"; +my $BuildName; +my $BuildDate; + +my $TERM = $ENV{'TERM'}; +my $UseColor = (defined $TERM and $TERM eq 'xterm-color' and -t STDOUT +                and defined $ENV{'SCAN_BUILD_COLOR'}); + +my $UserName = HtmlEscape(getpwuid($<) || 'unknown'); +my $HostName = HtmlEscape(hostname() || 'unknown'); +my $CurrentDir = HtmlEscape(getcwd()); +my $CurrentDirSuffix = basename($CurrentDir); + +my $CmdArgs; + +my $HtmlTitle; + +my $Date = localtime(); + +##----------------------------------------------------------------------------## +# Diagnostics +##----------------------------------------------------------------------------## + +sub Diag { +  if ($UseColor) { +    print BOLD, MAGENTA "$Prog: @_"; +    print RESET; +  } +  else { +    print "$Prog: @_"; +  }   +} + +sub DiagCrashes { +  my $Dir = shift; +  Diag ("The analyzer encountered problems on some source files.\n"); +  Diag ("Preprocessed versions of these sources were deposited in '$Dir/failures'.\n"); +  Diag ("Please consider submitting a bug report using these files:\n"); +  Diag ("  http://clang-analyzer.llvm.org/filing_bugs.html\n") +} + +sub DieDiag { +  if ($UseColor) { +    print BOLD, RED "$Prog: "; +    print RESET, RED @_; +    print RESET; +  } +  else { +    print "$Prog: ", @_; +  } +  exit(0); +} + +##----------------------------------------------------------------------------## +# Some initial preprocessing of Clang options. +##----------------------------------------------------------------------------## + +# Find 'clang' +my $ClangSB = Cwd::realpath("$RealBin/bin/clang"); +if (!defined $ClangSB || ! -x $ClangSB) { +  $ClangSB = Cwd::realpath("$RealBin/clang"); +} +my $Clang; +if (!defined $ClangSB || ! -x $ClangSB) { +  # Default to looking for 'clang' in the path. +  $Clang = `which clang`; +  chomp $Clang; +  if ($Clang eq "") { +    DieDiag("No 'clang' executable found in path."); +  } +} +else { +  $Clang = $ClangSB; +} +my $ClangCXX = $Clang . "++"; + +##----------------------------------------------------------------------------## +# GetHTMLRunDir - Construct an HTML directory name for the current sub-run. +##----------------------------------------------------------------------------## + +sub GetHTMLRunDir {   +  die "Not enough arguments." if (@_ == 0);   +  my $Dir = shift @_;     +  my $TmpMode = 0; +  if (!defined $Dir) { +    if (`uname` =~ /Darwin/) { +      $Dir = $ENV{'TMPDIR'}; +      if (!defined $Dir) { $Dir = "/tmp"; } +    } +    else { +      $Dir = "/tmp"; +    }     +    $TmpMode = 1; +  } +   +  # Chop off any trailing '/' characters. +  while ($Dir =~ /\/$/) { chop $Dir; } + +  # Get current date and time. +  my @CurrentTime = localtime();   +  my $year  = $CurrentTime[5] + 1900; +  my $day   = $CurrentTime[3]; +  my $month = $CurrentTime[4] + 1; +  my $DateString = sprintf("%d-%02d-%02d", $year, $month, $day); +   +  # Determine the run number.   +  my $RunNumber; +   +  if (-d $Dir) {     +    if (! -r $Dir) { +      DieDiag("directory '$Dir' exists but is not readable.\n"); +    }     +    # Iterate over all files in the specified directory.     +    my $max = 0;     +    opendir(DIR, $Dir); +    my @FILES = grep { -d "$Dir/$_" } readdir(DIR); +    closedir(DIR); + +    foreach my $f (@FILES) { +      # Strip the prefix '$Prog-' if we are dumping files to /tmp. +      if ($TmpMode) { +        next if (!($f =~ /^$Prog-(.+)/)); +        $f = $1; +      } + +      my @x = split/-/, $f; +      next if (scalar(@x) != 4); +      next if ($x[0] != $year); +      next if ($x[1] != $month); +      next if ($x[2] != $day); +       +      if ($x[3] > $max) { +        $max = $x[3]; +      }       +    } +     +    $RunNumber = $max + 1; +  } +  else { +     +    if (-x $Dir) { +      DieDiag("'$Dir' exists but is not a directory.\n"); +    } + +    if ($TmpMode) { +      DieDiag("The directory '/tmp' does not exist or cannot be accessed.\n"); +    } + +    # $Dir does not exist.  It will be automatically created by the  +    # clang driver.  Set the run number to 1.   + +    $RunNumber = 1; +  } +   +  die "RunNumber must be defined!" if (!defined $RunNumber); +   +  # Append the run number. +  my $NewDir; +  if ($TmpMode) { +    $NewDir = "$Dir/$Prog-$DateString-$RunNumber"; +  } +  else { +    $NewDir = "$Dir/$DateString-$RunNumber"; +  } +  system 'mkdir','-p',$NewDir; +  return $NewDir; +} + +sub SetHtmlEnv { +   +  die "Wrong number of arguments." if (scalar(@_) != 2); +   +  my $Args = shift; +  my $Dir = shift; +   +  die "No build command." if (scalar(@$Args) == 0); +   +  my $Cmd = $$Args[0]; + +  if ($Cmd =~ /configure/ || $Cmd =~ /autogen/) { +    return; +  } +   +  if ($Verbose) { +    Diag("Emitting reports for this run to '$Dir'.\n"); +  } +   +  $ENV{'CCC_ANALYZER_HTML'} = $Dir; +} + +##----------------------------------------------------------------------------## +# ComputeDigest - Compute a digest of the specified file. +##----------------------------------------------------------------------------## + +sub ComputeDigest { +  my $FName = shift; +  DieDiag("Cannot read $FName to compute Digest.\n") if (! -r $FName);   +   +  # Use Digest::MD5.  We don't have to be cryptographically secure.  We're +  # just looking for duplicate files that come from a non-malicious source. +  # We use Digest::MD5 because it is a standard Perl module that should +  # come bundled on most systems.   +  open(FILE, $FName) or DieDiag("Cannot open $FName when computing Digest.\n"); +  binmode FILE; +  my $Result = Digest::MD5->new->addfile(*FILE)->hexdigest; +  close(FILE); +   +  # Return the digest.   +  return $Result; +} + +##----------------------------------------------------------------------------## +#  UpdatePrefix - Compute the common prefix of files. +##----------------------------------------------------------------------------## + +my $Prefix; + +sub UpdatePrefix { +  my $x = shift; +  my $y = basename($x); +  $x =~ s/\Q$y\E$//; + +  if (!defined $Prefix) { +    $Prefix = $x; +    return; +  } +   +  chop $Prefix while (!($x =~ /^\Q$Prefix/)); +} + +sub GetPrefix { +  return $Prefix; +} + +##----------------------------------------------------------------------------## +#  UpdateInFilePath - Update the path in the report file. +##----------------------------------------------------------------------------## + +sub UpdateInFilePath { +  my $fname = shift; +  my $regex = shift; +  my $newtext = shift; + +  open (RIN, $fname) or die "cannot open $fname"; +  open (ROUT, ">", "$fname.tmp") or die "cannot open $fname.tmp"; + +  while (<RIN>) { +    s/$regex/$newtext/; +    print ROUT $_; +  } + +  close (ROUT); +  close (RIN); +  system("mv", "$fname.tmp", $fname); +} + +##----------------------------------------------------------------------------## +# AddStatLine - Decode and insert a statistics line into the database. +##----------------------------------------------------------------------------## + +sub AddStatLine { +  my $Line  = shift; +  my $Stats = shift; + +  print $Line . "\n"; + +  my $Regex = qr/(.*?)\ :\ (.*?)\ ->\ Total\ CFGBlocks:\ (\d+)\ \|\ Unreachable +      \ CFGBlocks:\ (\d+)\ \|\ Exhausted\ Block:\ (yes|no)\ \|\ Empty\ WorkList: +      \ (yes|no)/x; + +  if ($Line !~ $Regex) { +    return; +  } + +  # Create a hash of the interesting fields +  my $Row = { +    Filename    => $1, +    Function    => $2, +    Total       => $3, +    Unreachable => $4, +    Aborted     => $5, +    Empty       => $6 +  }; + +  # Add them to the stats array +  push @$Stats, $Row; +} + +##----------------------------------------------------------------------------## +# ScanFile - Scan a report file for various identifying attributes. +##----------------------------------------------------------------------------## + +# Sometimes a source file is scanned more than once, and thus produces +# multiple error reports.  We use a cache to solve this problem. + +my %AlreadyScanned; + +sub ScanFile { +   +  my $Index = shift; +  my $Dir = shift; +  my $FName = shift; +  my $Stats = shift; +   +  # Compute a digest for the report file.  Determine if we have already +  # scanned a file that looks just like it. +   +  my $digest = ComputeDigest("$Dir/$FName"); + +  if (defined $AlreadyScanned{$digest}) { +    # Redundant file.  Remove it. +    system ("rm", "-f", "$Dir/$FName"); +    return; +  } +   +  $AlreadyScanned{$digest} = 1; +   +  # At this point the report file is not world readable.  Make it happen. +  system ("chmod", "644", "$Dir/$FName"); +   +  # Scan the report file for tags. +  open(IN, "$Dir/$FName") or DieDiag("Cannot open '$Dir/$FName'\n"); + +  my $BugType        = ""; +  my $BugFile        = ""; +  my $BugCategory    = ""; +  my $BugDescription = ""; +  my $BugPathLength  = 1; +  my $BugLine        = 0; + +  while (<IN>) { +    last if (/<!-- BUGMETAEND -->/); + +    if (/<!-- BUGTYPE (.*) -->$/) { +      $BugType = $1; +    } +    elsif (/<!-- BUGFILE (.*) -->$/) { +      $BugFile = abs_path($1); +      UpdatePrefix($BugFile); +    } +    elsif (/<!-- BUGPATHLENGTH (.*) -->$/) { +      $BugPathLength = $1; +    } +    elsif (/<!-- BUGLINE (.*) -->$/) { +      $BugLine = $1;     +    } +    elsif (/<!-- BUGCATEGORY (.*) -->$/) { +      $BugCategory = $1; +    } +    elsif (/<!-- BUGDESC (.*) -->$/) { +      $BugDescription = $1; +    } +  } + +  close(IN); +   +  if (!defined $BugCategory) { +    $BugCategory = "Other"; +  } + +  # Don't add internal statistics to the bug reports +  if ($BugCategory =~ /statistics/i) { +    AddStatLine($BugDescription, $Stats); +    return; +  } +   +  push @$Index,[ $FName, $BugCategory, $BugType, $BugFile, $BugLine, +                 $BugPathLength ]; +} + +##----------------------------------------------------------------------------## +# CopyFiles - Copy resource files to target directory. +##----------------------------------------------------------------------------## + +sub CopyFiles { + +  my $Dir = shift; + +  my $JS = Cwd::realpath("$RealBin/sorttable.js"); +   +  DieDiag("Cannot find 'sorttable.js'.\n") +    if (! -r $JS);   + +  system ("cp", $JS, "$Dir"); + +  DieDiag("Could not copy 'sorttable.js' to '$Dir'.\n") +    if (! -r "$Dir/sorttable.js"); +     +  my $CSS = Cwd::realpath("$RealBin/scanview.css"); +   +  DieDiag("Cannot find 'scanview.css'.\n") +    if (! -r $CSS); + +  system ("cp", $CSS, "$Dir"); + +  DieDiag("Could not copy 'scanview.css' to '$Dir'.\n") +    if (! -r $CSS); +} + +##----------------------------------------------------------------------------## +# CalcStats - Calculates visitation statistics and returns the string. +##----------------------------------------------------------------------------## + +sub CalcStats { +  my $Stats = shift; + +  my $TotalBlocks = 0; +  my $UnreachedBlocks = 0; +  my $TotalFunctions = scalar(@$Stats); +  my $BlockAborted = 0; +  my $WorkListAborted = 0; +  my $Aborted = 0; + +  # Calculate the unique files +  my $FilesHash = {}; + +  foreach my $Row (@$Stats) { +    $FilesHash->{$Row->{Filename}} = 1; +    $TotalBlocks += $Row->{Total}; +    $UnreachedBlocks += $Row->{Unreachable}; +    $BlockAborted++ if $Row->{Aborted} eq 'yes'; +    $WorkListAborted++ if $Row->{Empty} eq 'no'; +    $Aborted++ if $Row->{Aborted} eq 'yes' || $Row->{Empty} eq 'no'; +  } + +  my $TotalFiles = scalar(keys(%$FilesHash)); + +  # Calculations +  my $PercentAborted = sprintf("%.2f", $Aborted / $TotalFunctions * 100); +  my $PercentBlockAborted = sprintf("%.2f", $BlockAborted / $TotalFunctions +      * 100); +  my $PercentWorkListAborted = sprintf("%.2f", $WorkListAborted / +      $TotalFunctions * 100); +  my $PercentBlocksUnreached = sprintf("%.2f", $UnreachedBlocks / $TotalBlocks +      * 100); + +  my $StatsString = "Analyzed $TotalBlocks blocks in $TotalFunctions functions" +    . " in $TotalFiles files\n" +    . "$Aborted functions aborted early ($PercentAborted%)\n" +    . "$BlockAborted had aborted blocks ($PercentBlockAborted%)\n" +    . "$WorkListAborted had unfinished worklists ($PercentWorkListAborted%)\n" +    . "$UnreachedBlocks blocks were never reached ($PercentBlocksUnreached%)\n"; + +  return $StatsString; +} + +##----------------------------------------------------------------------------## +# Postprocess - Postprocess the results of an analysis scan. +##----------------------------------------------------------------------------## + +sub Postprocess { +   +  my $Dir           = shift; +  my $BaseDir       = shift; +  my $AnalyzerStats = shift; +   +  die "No directory specified." if (!defined $Dir); +   +  if (! -d $Dir) { +    Diag("No bugs found.\n"); +    return 0; +  } +   +  opendir(DIR, $Dir); +  my @files = grep { /^report-.*\.html$/ } readdir(DIR); +  closedir(DIR); + +  if (scalar(@files) == 0 and ! -e "$Dir/failures") { +    Diag("Removing directory '$Dir' because it contains no reports.\n"); +    system ("rm", "-fR", $Dir); +    return 0; +  } +   +  # Scan each report file and build an index.   +  my @Index; +  my @Stats; +  foreach my $file (@files) { ScanFile(\@Index, $Dir, $file, \@Stats); } +   +  # Scan the failures directory and use the information in the .info files +  # to update the common prefix directory. +  my @failures; +  my @attributes_ignored; +  if (-d "$Dir/failures") { +    opendir(DIR, "$Dir/failures"); +    @failures = grep { /[.]info.txt$/ && !/attribute_ignored/; } readdir(DIR); +    closedir(DIR); +    opendir(DIR, "$Dir/failures");         +    @attributes_ignored = grep { /^attribute_ignored/; } readdir(DIR); +    closedir(DIR); +    foreach my $file (@failures) { +      open IN, "$Dir/failures/$file" or DieDiag("cannot open $file\n"); +      my $Path = <IN>; +      if (defined $Path) { UpdatePrefix($Path); } +      close IN; +    }     +  } +   +  # Generate an index.html file.   +  my $FName = "$Dir/index.html";   +  open(OUT, ">", $FName) or DieDiag("Cannot create file '$FName'\n"); +   +  # Print out the header. +   +print OUT <<ENDTEXT; +<html> +<head> +<title>${HtmlTitle}</title> +<link type="text/css" rel="stylesheet" href="scanview.css"/> +<script src="sorttable.js"></script> +<script language='javascript' type="text/javascript"> +function SetDisplay(RowClass, DisplayVal) +{ +  var Rows = document.getElementsByTagName("tr"); +  for ( var i = 0 ; i < Rows.length; ++i ) { +    if (Rows[i].className == RowClass) { +      Rows[i].style.display = DisplayVal; +    } +  } +} + +function CopyCheckedStateToCheckButtons(SummaryCheckButton) { +  var Inputs = document.getElementsByTagName("input"); +  for ( var i = 0 ; i < Inputs.length; ++i ) { +    if (Inputs[i].type == "checkbox") { +      if(Inputs[i] != SummaryCheckButton) { +        Inputs[i].checked = SummaryCheckButton.checked; +        Inputs[i].onclick(); +	  } +    } +  } +} + +function returnObjById( id ) { +    if (document.getElementById)  +        var returnVar = document.getElementById(id); +    else if (document.all) +        var returnVar = document.all[id]; +    else if (document.layers)  +        var returnVar = document.layers[id]; +    return returnVar;  +} + +var NumUnchecked = 0; + +function ToggleDisplay(CheckButton, ClassName) { +  if (CheckButton.checked) { +    SetDisplay(ClassName, ""); +    if (--NumUnchecked == 0) { +      returnObjById("AllBugsCheck").checked = true; +    } +  } +  else { +    SetDisplay(ClassName, "none"); +    NumUnchecked++; +    returnObjById("AllBugsCheck").checked = false; +  } +} +</script> +<!-- SUMMARYENDHEAD --> +</head> +<body> +<h1>${HtmlTitle}</h1> + +<table> +<tr><th>User:</th><td>${UserName}\@${HostName}</td></tr> +<tr><th>Working Directory:</th><td>${CurrentDir}</td></tr> +<tr><th>Command Line:</th><td>${CmdArgs}</td></tr> +<tr><th>Date:</th><td>${Date}</td></tr> +ENDTEXT + +print OUT "<tr><th>Version:</th><td>${BuildName} (${BuildDate})</td></tr>\n" +  if (defined($BuildName) && defined($BuildDate)); + +print OUT <<ENDTEXT; +</table> +ENDTEXT + +  if (scalar(@files)) { +    # Print out the summary table. +    my %Totals; + +    for my $row ( @Index ) { +      my $bug_type = ($row->[2]); +      my $bug_category = ($row->[1]); +      my $key = "$bug_category:$bug_type"; + +      if (!defined $Totals{$key}) { $Totals{$key} = [1,$bug_category,$bug_type]; } +      else { $Totals{$key}->[0]++; } +    } + +    print OUT "<h2>Bug Summary</h2>"; + +    if (defined $BuildName) { +      print OUT "\n<p>Results in this analysis run are based on analyzer build <b>$BuildName</b>.</p>\n" +    } +   +  my $TotalBugs = scalar(@Index); +print OUT <<ENDTEXT; +<table> +<thead><tr><td>Bug Type</td><td>Quantity</td><td class="sorttable_nosort">Display?</td></tr></thead> +<tr style="font-weight:bold"><td class="SUMM_DESC">All Bugs</td><td class="Q">$TotalBugs</td><td><center><input type="checkbox" id="AllBugsCheck" onClick="CopyCheckedStateToCheckButtons(this);" checked/></center></td></tr> +ENDTEXT +   +    my $last_category; + +    for my $key ( +      sort { +        my $x = $Totals{$a}; +        my $y = $Totals{$b}; +        my $res = $x->[1] cmp $y->[1]; +        $res = $x->[2] cmp $y->[2] if ($res == 0); +        $res +      } keys %Totals )  +    { +      my $val = $Totals{$key}; +      my $category = $val->[1]; +      if (!defined $last_category or $last_category ne $category) { +        $last_category = $category; +        print OUT "<tr><th>$category</th><th colspan=2></th></tr>\n"; +      }       +      my $x = lc $key; +      $x =~ s/[ ,'":\/()]+/_/g; +      print OUT "<tr><td class=\"SUMM_DESC\">"; +      print OUT $val->[2]; +      print OUT "</td><td class=\"Q\">"; +      print OUT $val->[0]; +      print OUT "</td><td><center><input type=\"checkbox\" onClick=\"ToggleDisplay(this,'bt_$x');\" checked/></center></td></tr>\n"; +    } + +  # Print out the table of errors. + +print OUT <<ENDTEXT; +</table> +<h2>Reports</h2> + +<table class="sortable" style="table-layout:automatic"> +<thead><tr> +  <td>Bug Group</td> +  <td class="sorttable_sorted">Bug Type<span id="sorttable_sortfwdind"> ▾</span></td> +  <td>File</td> +  <td class="Q">Line</td> +  <td class="Q">Path Length</td> +  <td class="sorttable_nosort"></td> +  <!-- REPORTBUGCOL --> +</tr></thead> +<tbody> +ENDTEXT + +    my $prefix = GetPrefix(); +    my $regex; +    my $InFileRegex; +    my $InFilePrefix = "File:</td><td>"; +   +    if (defined $prefix) {  +      $regex = qr/^\Q$prefix\E/is;     +      $InFileRegex = qr/\Q$InFilePrefix$prefix\E/is; +    }     + +    for my $row ( sort { $a->[2] cmp $b->[2] } @Index ) { +      my $x = "$row->[1]:$row->[2]"; +      $x = lc $x; +      $x =~ s/[ ,'":\/()]+/_/g; +     +      my $ReportFile = $row->[0]; +           +      print OUT "<tr class=\"bt_$x\">"; +      print OUT "<td class=\"DESC\">"; +      print OUT $row->[1]; +      print OUT "</td>"; +      print OUT "<td class=\"DESC\">"; +      print OUT $row->[2]; +      print OUT "</td>"; +       +      # Update the file prefix.       +      my $fname = $row->[3]; + +      if (defined $regex) { +        $fname =~ s/$regex//; +        UpdateInFilePath("$Dir/$ReportFile", $InFileRegex, $InFilePrefix) +      } +       +      print OUT "<td>";       +      my @fname = split /\//,$fname; +      if ($#fname > 0) { +        while ($#fname >= 0) { +          my $x = shift @fname; +          print OUT $x; +          if ($#fname >= 0) { +            print OUT "<span class=\"W\"> </span>/"; +          } +        } +      } +      else { +        print OUT $fname; +      }       +      print OUT "</td>"; +       +      # Print out the quantities. +      for my $j ( 4 .. 5 ) { +        print OUT "<td class=\"Q\">$row->[$j]</td>";         +      } +       +      # Print the rest of the columns. +      for (my $j = 6; $j <= $#{$row}; ++$j) { +        print OUT "<td>$row->[$j]</td>" +      } + +      # Emit the "View" link. +      print OUT "<td><a href=\"$ReportFile#EndPath\">View Report</a></td>"; +         +      # Emit REPORTBUG markers. +      print OUT "\n<!-- REPORTBUG id=\"$ReportFile\" -->\n"; +         +      # End the row. +      print OUT "</tr>\n"; +    } +   +    print OUT "</tbody>\n</table>\n\n"; +  } + +  if (scalar (@failures) || scalar(@attributes_ignored)) { +    print OUT "<h2>Analyzer Failures</h2>\n"; +     +    if (scalar @attributes_ignored) { +      print OUT "The analyzer's parser ignored the following attributes:<p>\n"; +      print OUT "<table>\n"; +      print OUT "<thead><tr><td>Attribute</td><td>Source File</td><td>Preprocessed File</td><td>STDERR Output</td></tr></thead>\n"; +      foreach my $file (sort @attributes_ignored) { +        die "cannot demangle attribute name\n" if (! ($file =~ /^attribute_ignored_(.+).txt/)); +        my $attribute = $1; +        # Open the attribute file to get the first file that failed. +        next if (!open (ATTR, "$Dir/failures/$file")); +        my $ppfile = <ATTR>; +        chomp $ppfile; +        close ATTR; +        next if (! -e "$Dir/failures/$ppfile"); +        # Open the info file and get the name of the source file. +        open (INFO, "$Dir/failures/$ppfile.info.txt") or +          die "Cannot open $Dir/failures/$ppfile.info.txt\n"; +        my $srcfile = <INFO>; +        chomp $srcfile; +        close (INFO); +        # Print the information in the table. +        my $prefix = GetPrefix(); +        if (defined $prefix) { $srcfile =~ s/^\Q$prefix//; } +        print OUT "<tr><td>$attribute</td><td>$srcfile</td><td><a href=\"failures/$ppfile\">$ppfile</a></td><td><a href=\"failures/$ppfile.stderr.txt\">$ppfile.stderr.txt</a></td></tr>\n"; +        my $ppfile_clang = $ppfile; +        $ppfile_clang =~ s/[.](.+)$/.clang.$1/; +        print OUT "  <!-- REPORTPROBLEM src=\"$srcfile\" file=\"failures/$ppfile\" clangfile=\"failures/$ppfile_clang\" stderr=\"failures/$ppfile.stderr.txt\" info=\"failures/$ppfile.info.txt\" -->\n"; +      } +      print OUT "</table>\n"; +    } +     +    if (scalar @failures) { +      print OUT "<p>The analyzer had problems processing the following files:</p>\n"; +      print OUT "<table>\n"; +      print OUT "<thead><tr><td>Problem</td><td>Source File</td><td>Preprocessed File</td><td>STDERR Output</td></tr></thead>\n"; +      foreach my $file (sort @failures) { +        $file =~ /(.+).info.txt$/; +        # Get the preprocessed file. +        my $ppfile = $1; +        # Open the info file and get the name of the source file. +        open (INFO, "$Dir/failures/$file") or +          die "Cannot open $Dir/failures/$file\n"; +        my $srcfile = <INFO>; +        chomp $srcfile; +        my $problem = <INFO>; +        chomp $problem; +        close (INFO); +        # Print the information in the table. +        my $prefix = GetPrefix(); +        if (defined $prefix) { $srcfile =~ s/^\Q$prefix//; } +        print OUT "<tr><td>$problem</td><td>$srcfile</td><td><a href=\"failures/$ppfile\">$ppfile</a></td><td><a href=\"failures/$ppfile.stderr.txt\">$ppfile.stderr.txt</a></td></tr>\n"; +        my $ppfile_clang = $ppfile; +        $ppfile_clang =~ s/[.](.+)$/.clang.$1/; +        print OUT "  <!-- REPORTPROBLEM src=\"$srcfile\" file=\"failures/$ppfile\" clangfile=\"failures/$ppfile_clang\" stderr=\"failures/$ppfile.stderr.txt\" info=\"failures/$ppfile.info.txt\" -->\n"; +      } +      print OUT "</table>\n"; +    }     +    print OUT "<p>Please consider submitting preprocessed files as <a href=\"http://clang-analyzer.llvm.org/filing_bugs.html\">bug reports</a>. <!-- REPORTCRASHES --> </p>\n"; +  } +   +  print OUT "</body></html>\n";   +  close(OUT); +  CopyFiles($Dir); + +  # Make sure $Dir and $BaseDir are world readable/executable. +  system("chmod", "755", $Dir); +  if (defined $BaseDir) { system("chmod", "755", $BaseDir); } + +  # Print statistics +  print CalcStats(\@Stats) if $AnalyzerStats; + +  my $Num = scalar(@Index); +  Diag("$Num bugs found.\n"); +  if ($Num > 0 && -r "$Dir/index.html") { +    Diag("Run 'scan-view $Dir' to examine bug reports.\n"); +  } +   +  DiagCrashes($Dir) if (scalar @failures || scalar @attributes_ignored); +   +  return $Num; +} + +##----------------------------------------------------------------------------## +# RunBuildCommand - Run the build command. +##----------------------------------------------------------------------------## + +sub AddIfNotPresent { +  my $Args = shift; +  my $Arg = shift;   +  my $found = 0; +   +  foreach my $k (@$Args) { +    if ($k eq $Arg) { +      $found = 1; +      last; +    } +  } +   +  if ($found == 0) { +    push @$Args, $Arg; +  } +} + +sub RunBuildCommand { +   +  my $Args = shift; +  my $IgnoreErrors = shift; +  my $Cmd = $Args->[0]; +  my $CCAnalyzer = shift; +  my $CXXAnalyzer = shift; +   +  # Get only the part of the command after the last '/'. +  if ($Cmd =~ /\/([^\/]+)$/) { +    $Cmd = $1; +  } +   +  if ($Cmd =~ /(.*\/?gcc[^\/]*$)/ or  +      $Cmd =~ /(.*\/?cc[^\/]*$)/ or +      $Cmd =~ /(.*\/?llvm-gcc[^\/]*$)/ or +      $Cmd =~ /(.*\/?clang$)/ or  +      $Cmd =~ /(.*\/?ccc-analyzer[^\/]*$)/) { + +    if (!($Cmd =~ /ccc-analyzer/) and !defined $ENV{"CCC_CC"}) { +      $ENV{"CCC_CC"} = $1;       +    } +         +    shift @$Args; +    unshift @$Args, $CCAnalyzer; +  } +  elsif ($Cmd =~ /(.*\/?g\+\+[^\/]*$)/ or  +        $Cmd =~ /(.*\/?c\+\+[^\/]*$)/ or +        $Cmd =~ /(.*\/?llvm-g\+\+[^\/]*$)/ or +        $Cmd =~ /(.*\/?clang\+\+$)/ or +        $Cmd =~ /(.*\/?c\+\+-analyzer[^\/]*$)/) { +    if (!($Cmd =~ /c\+\+-analyzer/) and !defined $ENV{"CCC_CXX"}) { +      $ENV{"CCC_CXX"} = $1;       +    }         +    shift @$Args; +    unshift @$Args, $CXXAnalyzer; +  } +  elsif ($IgnoreErrors) { +    if ($Cmd eq "make" or $Cmd eq "gmake") { +      AddIfNotPresent($Args, "CC=$CCAnalyzer"); +      AddIfNotPresent($Args, "CXX=$CXXAnalyzer"); +      AddIfNotPresent($Args,"-k"); +      AddIfNotPresent($Args,"-i"); +    } +    elsif ($Cmd eq "xcodebuild") { +      AddIfNotPresent($Args,"-PBXBuildsContinueAfterErrors=YES"); +    } +  }  +   +  if ($Cmd eq "xcodebuild") { +    # Check if using iPhone SDK 3.0 (simulator).  If so the compiler being +    # used should be gcc-4.2. +    if (!defined $ENV{"CCC_CC"}) { +      for (my $i = 0 ; $i < scalar(@$Args); ++$i) { +        if ($Args->[$i] eq "-sdk" && $i + 1 < scalar(@$Args)) { +          if (@$Args[$i+1] =~ /^iphonesimulator3/) { +            $ENV{"CCC_CC"} = "gcc-4.2"; +            $ENV{"CCC_CXX"} = "g++-4.2";             +          } +        } +      } +    } + +    # Disable PCH files until clang supports them. +    AddIfNotPresent($Args,"GCC_PRECOMPILE_PREFIX_HEADER=NO"); +     +    # When 'CC' is set, xcodebuild uses it to do all linking, even if we are +    # linking C++ object files.  Set 'LDPLUSPLUS' so that xcodebuild uses 'g++' +    # (via c++-analyzer) when linking such files. +    $ENV{"LDPLUSPLUS"} = $CXXAnalyzer; +  } +   +  return (system(@$Args) >> 8); +} + +##----------------------------------------------------------------------------## +# DisplayHelp - Utility function to display all help options. +##----------------------------------------------------------------------------## + +sub DisplayHelp { +   +print <<ENDTEXT; +USAGE: $Prog [options] <build command> [build options] + +ENDTEXT + +  if (defined $BuildName) { +    print "ANALYZER BUILD: $BuildName ($BuildDate)\n\n"; +  } + +print <<ENDTEXT; +OPTIONS: + + -analyze-headers - Also analyze functions in #included files. +  + -o             - Target directory for HTML report files.  Subdirectories +                  will be created as needed to represent separate "runs" of +                  the analyzer.  If this option is not specified, a directory +                  is created in /tmp (TMPDIR on Mac OS X) to store the reports. +                   + -h             - Display this message. + --help + + -k             - Add a "keep on going" option to the specified build command. + --keep-going     This option currently supports make and xcodebuild. +                  This is a convenience option; one can specify this +                  behavior directly using build options. + + --html-title [title]       - Specify the title used on generated HTML pages. + --html-title=[title]         If not specified, a default title will be used. + + -plist         - By default the output of scan-build is a set of HTML files. +                  This option outputs the results as a set of .plist files. +  + -plist-html    - By default the output of scan-build is a set of HTML files. +                  This option outputs the results as a set of HTML  +                  and .plist files. +  + --status-bugs  - By default, the exit status of $Prog is the same as the +                  executed build command.  Specifying this option causes the +                  exit status of $Prog to be 1 if it found potential bugs +                  and 0 otherwise. + + --use-cc [compiler path]   - $Prog attempts to guess the default compiler for + --use-cc=[compiler path]     your C and Objective-C code. Use this option +                              to specify an alternate compiler. + + --use-c++ [compiler path]  - $Prog attempts to guess the default compiler for + --use-c++=[compiler path]    your C++ and Objective-C++ code. Use this option +                              to specify an alternate compiler. + + -v             - Verbose output from $Prog and the analyzer. +                  A second and third '-v' increases verbosity. + + -V             - View analysis results in a web browser when the build + --view           completes. + +ADVANCED OPTIONS: + + -constraints [model] - Specify the contraint engine used by the analyzer. +                        By default the 'range' model is used.  Specifying  +                        'basic' uses a simpler, less powerful constraint model +                        used by checker-0.160 and earlier. + + -store [model] - Specify the store model used by the analyzer. By default, +                  the 'region' store model is used. 'region' specifies a field- +                  sensitive store model. Users can also specify 'basic', which +                  is far less precise but can more quickly analyze code. +                  'basic' was the default store model for checker-0.221 and +                  earlier. + + -no-failure-reports - Do not create a 'failures' subdirectory that includes +                       analyzer crash reports and preprocessed source files. + + -stats - Generates visitation statistics for the project being analyzed. + + -maxloop N - specifiy the number of times a block can be visited before giving +              up. Default is 4. Increase for more comprehensive coverage at a +              cost of speed. + +CONTROLLING CHECKERS: + + A default group of checkers are always run unless explicitly disabled. + Checkers may be enabled/disabled using the following options: + + -enable-checker [checker name] + -disable-checker [checker name] +ENDTEXT + +# Query clang for list of checkers that are enabled. +my %EnabledCheckers; +foreach my $lang ("c", "objective-c", "objective-c++", "c++") { +  pipe(FROM_CHILD, TO_PARENT); +  my $pid = fork(); +  if ($pid == 0) { +    close FROM_CHILD; +    open(STDOUT,">&", \*TO_PARENT); +    open(STDERR,">&", \*TO_PARENT); +    exec $Clang, ('--analyze', '-x', $lang, '-', '-###'); +  } +  close(TO_PARENT); +  while(<FROM_CHILD>) { +    foreach my $val (split /\s+/) { +      $val =~ s/\"//g; +      if ($val =~ /-analyzer-checker\=([^\s]+)/) { +        $EnabledCheckers{$1} = 1; +      } +    } +  } +  waitpid($pid,0); +  close(FROM_CHILD); +} + +# Query clang for complete list of checkers. +pipe(FROM_CHILD, TO_PARENT); +my $pid = fork(); +if ($pid == 0) { +  close FROM_CHILD; +  open(STDOUT,">&", \*TO_PARENT); +  open(STDERR,">&", \*TO_PARENT); +  exec $Clang, ('-cc1', '-analyzer-checker-help'); +} +close(TO_PARENT); +my $foundCheckers = 0; +while(<FROM_CHILD>) { +  if (/CHECKERS:/) { +    $foundCheckers = 1; +    last; +  } +} +if (!$foundCheckers) { +  print "  *** Could not query Clang for the list of available checkers."; +} +else { +  print("\nAVAILABLE CHECKERS:\n\n"); +  my $skip = 0; +  while(<FROM_CHILD>) { +    if (/experimental/) { +      $skip = 1; +      next; +    } +    if ($skip) { +      next if (!/^\s\s[^\s]/); +      $skip = 0; +    } +    s/^\s\s//; +    if (/^([^\s]+)/) { +      # Is the checker enabled? +      my $checker = $1; +      my $enabled = 0; +      my $aggregate = ""; +      foreach my $domain (split /\./, $checker) { +        $aggregate .= $domain; +        if ($EnabledCheckers{$aggregate}) { +          $enabled =1; +          last; +        }         +      } +       +      if ($enabled) { +        print " + "; +      } +      else { +        print "   "; +      } +    } +    else { +      print "   "; +    } +    print $_; +  }  +} +waitpid($pid,0); +close(FROM_CHILD); + +print <<ENDTEXT + + NOTE: "+" indicates that an analysis is enabled by default. + +BUILD OPTIONS + + You can specify any build option acceptable to the build command. + +EXAMPLE + + $Prog -o /tmp/myhtmldir make -j4 +      + The above example causes analysis reports to be deposited into + a subdirectory of "/tmp/myhtmldir" and to run "make" with the "-j4" option. + A different subdirectory is created each time $Prog analyzes a project. + The analyzer should support most parallel builds, but not distributed builds. + +ENDTEXT +} + +##----------------------------------------------------------------------------## +# HtmlEscape - HTML entity encode characters that are special in HTML +##----------------------------------------------------------------------------## + +sub HtmlEscape { +  # copy argument to new variable so we don't clobber the original +  my $arg = shift || ''; +  my $tmp = $arg; +  $tmp =~ s/&/&/g; +  $tmp =~ s/</</g; +  $tmp =~ s/>/>/g; +  return $tmp; +} + +##----------------------------------------------------------------------------## +# ShellEscape - backslash escape characters that are special to the shell +##----------------------------------------------------------------------------## + +sub ShellEscape { +  # copy argument to new variable so we don't clobber the original +  my $arg = shift || ''; +  if ($arg =~ /["\s]/) { return "'" . $arg . "'"; } +  return $arg; +} + +##----------------------------------------------------------------------------## +# Process command-line arguments. +##----------------------------------------------------------------------------## + +my $AnalyzeHeaders = 0; +my $HtmlDir;           # Parent directory to store HTML files. +my $IgnoreErrors = 0;  # Ignore build errors. +my $ViewResults  = 0;  # View results when the build terminates. +my $ExitStatusFoundBugs = 0; # Exit status reflects whether bugs were found +my @AnalysesToRun; +my $StoreModel; +my $ConstraintsModel; +my $OutputFormat = "html"; +my $AnalyzerStats = 0; +my $MaxLoop = 0; + +if (!@ARGV) { +  DisplayHelp(); +  exit 1; +} + + +my $displayHelp = 0; + +while (@ARGV) { +   +  # Scan for options we recognize. +   +  my $arg = $ARGV[0]; + +  if ($arg eq "-h" or $arg eq "--help") { +    $displayHelp = 1; +    shift @ARGV; +    next; +  } +   +  if ($arg eq '-analyze-headers') { +    shift @ARGV;     +    $AnalyzeHeaders = 1; +    next; +  } +   +  if ($arg eq "-o") { +    shift @ARGV; +         +    if (!@ARGV) { +      DieDiag("'-o' option requires a target directory name.\n"); +    } +     +    # Construct an absolute path.  Uses the current working directory +    # as a base if the original path was not absolute. +    $HtmlDir = abs_path(shift @ARGV); +     +    next; +  } + +  if ($arg =~ /^--html-title(=(.+))?$/) { +    shift @ARGV; + +    if (!defined $2 || $2 eq '') { +      if (!@ARGV) { +        DieDiag("'--html-title' option requires a string.\n"); +      } + +      $HtmlTitle = shift @ARGV; +    } else { +      $HtmlTitle = $2; +    } + +    next; +  } +   +  if ($arg eq "-k" or $arg eq "--keep-going") { +    shift @ARGV; +    $IgnoreErrors = 1; +    next; +  } + +  if ($arg =~ /^--use-cc(=(.+))?$/) { +    shift @ARGV; +    my $cc; +     +    if (!defined $2 || $2 eq "") { +      if (!@ARGV) { +        DieDiag("'--use-cc' option requires a compiler executable name.\n"); +      } +      $cc = shift @ARGV; +    } +    else { +      $cc = $2; +    } +     +    $ENV{"CCC_CC"} = $cc; +    next; +  } +   +  if ($arg =~ /^--use-c\+\+(=(.+))?$/) { +    shift @ARGV; +    my $cxx;     +     +    if (!defined $2 || $2 eq "") { +      if (!@ARGV) { +        DieDiag("'--use-c++' option requires a compiler executable name.\n"); +      } +      $cxx = shift @ARGV; +    } +    else { +      $cxx = $2; +    } +     +    $ENV{"CCC_CXX"} = $cxx; +    next; +  } +   +  if ($arg eq "-v") { +    shift @ARGV; +    $Verbose++; +    next; +  } +   +  if ($arg eq "-V" or $arg eq "--view") { +    shift @ARGV; +    $ViewResults = 1;     +    next; +  } +   +  if ($arg eq "--status-bugs") { +    shift @ARGV; +    $ExitStatusFoundBugs = 1; +    next; +  } + +  if ($arg eq "-store") { +    shift @ARGV; +    $StoreModel = shift @ARGV; +    next; +  } +   +  if ($arg eq "-constraints") { +    shift @ARGV; +    $ConstraintsModel = shift @ARGV; +    next; +  } +   +  if ($arg eq "-plist") { +    shift @ARGV; +    $OutputFormat = "plist"; +    next; +  } +  if ($arg eq "-plist-html") { +    shift @ARGV; +    $OutputFormat = "plist-html"; +    next; +  } +   +  if ($arg eq "-no-failure-reports") { +    $ENV{"CCC_REPORT_FAILURES"} = 0; +    next; +  } +  if ($arg eq "-stats") { +    shift @ARGV; +    $AnalyzerStats = 1; +    next; +  } +  if ($arg eq "-maxloop") { +    shift @ARGV; +    $MaxLoop = shift @ARGV; +    next; +  } +  if ($arg eq "-enable-checker") { +    shift @ARGV; +    push @AnalysesToRun, "-analyzer-checker", shift @ARGV; +    next; +  } +  if ($arg eq "-disable-checker") { +    shift @ARGV; +    push @AnalysesToRun, "-analyzer-disable-checker", shift @ARGV; +    next; +  } + +  DieDiag("unrecognized option '$arg'\n") if ($arg =~ /^-/); +   +  last; +} + +if (!@ARGV and $displayHelp == 0) { +  Diag("No build command specified.\n\n"); +  $displayHelp = 1; +} + +if ($displayHelp) { +  DisplayHelp(); +  exit 1; +} + +# Determine where results go. +$CmdArgs = HtmlEscape(join(' ', map(ShellEscape($_), @ARGV))); +$HtmlTitle = "${CurrentDirSuffix} - scan-build results" +  unless (defined($HtmlTitle)); + +# Determine the output directory for the HTML reports. +my $BaseDir = $HtmlDir; +$HtmlDir = GetHTMLRunDir($HtmlDir); + +# Determine the location of ccc-analyzer. +my $AbsRealBin = Cwd::realpath($RealBin); +my $Cmd = "$AbsRealBin/libexec/ccc-analyzer"; +my $CmdCXX = "$AbsRealBin/libexec/c++-analyzer"; + +if (!defined $Cmd || ! -x $Cmd) { +  $Cmd = "$AbsRealBin/ccc-analyzer"; +  DieDiag("Executable 'ccc-analyzer' does not exist at '$Cmd'\n") if(! -x $Cmd); +} +if (!defined $CmdCXX || ! -x $CmdCXX) { +  $CmdCXX = "$AbsRealBin/c++-analyzer"; +  DieDiag("Executable 'c++-analyzer' does not exist at '$CmdCXX'\n") if(! -x $CmdCXX); +} + +if (!defined $ClangSB || ! -x $ClangSB) { +  Diag("'clang' executable not found in '$RealBin/bin'.\n"); +  Diag("Using 'clang' from path: $Clang\n"); +} + +# Set the appropriate environment variables. +SetHtmlEnv(\@ARGV, $HtmlDir); +$ENV{'CC'} = $Cmd; +$ENV{'CXX'} = $CmdCXX; +$ENV{'CLANG'} = $Clang; +$ENV{'CLANG_CXX'} = $ClangCXX; +if ($Verbose >= 2) { +  $ENV{'CCC_ANALYZER_VERBOSE'} = 1; +} +if ($Verbose >= 3) { +  $ENV{'CCC_ANALYZER_LOG'} = 1; +} +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; +} + +$ENV{'CCC_ANALYZER_ANALYSIS'} = join ' ',@AnalysesToRun; + +if (defined $StoreModel) { +  $ENV{'CCC_ANALYZER_STORE_MODEL'} = $StoreModel; +} +if (defined $ConstraintsModel) { +  $ENV{'CCC_ANALYZER_CONSTRAINTS_MODEL'} = $ConstraintsModel; +} +if (defined $OutputFormat) { +  $ENV{'CCC_ANALYZER_OUTPUT_FORMAT'} = $OutputFormat; +} + +# Run the build. +my $ExitStatus = RunBuildCommand(\@ARGV, $IgnoreErrors, $Cmd, $CmdCXX); + +if (defined $OutputFormat) { +  if ($OutputFormat =~ /plist/) { +    Diag "Analysis run complete.\n"; +    Diag "Analysis results (plist files) deposited in '$HtmlDir'\n"; +  } +  elsif ($OutputFormat =~ /html/) { +    # Postprocess the HTML directory. +    my $NumBugs = Postprocess($HtmlDir, $BaseDir, $AnalyzerStats); + +    if ($ViewResults and -r "$HtmlDir/index.html") { +      Diag "Analysis run complete.\n"; +      Diag "Viewing analysis results in '$HtmlDir' using scan-view.\n"; +      my $ScanView = Cwd::realpath("$RealBin/scan-view"); +      if (! -x $ScanView) { $ScanView = "scan-view"; } +      exec $ScanView, "$HtmlDir"; +    } + +    if ($ExitStatusFoundBugs) { +      exit 1 if ($NumBugs > 0); +      exit 0; +    } +  } +} + +exit $ExitStatus; + diff --git a/clang/tools/scan-build/scanview.css b/clang/tools/scan-build/scanview.css new file mode 100644 index 0000000..a0406f3 --- /dev/null +++ b/clang/tools/scan-build/scanview.css @@ -0,0 +1,62 @@ +body { color:#000000; background-color:#ffffff } +body { font-family: Helvetica, sans-serif; font-size:9pt } +h1 { font-size: 14pt; } +h2 { font-size: 12pt; } +table { font-size:9pt } +table { border-spacing: 0px; border: 1px solid black } +th, table thead { +  background-color:#eee; color:#666666; +  font-weight: bold; cursor: default; +  text-align:center; +  font-weight: bold; font-family: Verdana; +  white-space:nowrap; +}  +.W { font-size:0px } +th, td { padding:5px; padding-left:8px; text-align:left } +td.SUMM_DESC { padding-left:12px } +td.DESC { white-space:pre } +td.Q { text-align:right } +td { text-align:left } +tbody.scrollContent { overflow:auto } + +table.form_group { +    background-color: #ccc; +    border: 1px solid #333;  +    padding: 2px; +} + +table.form_inner_group { +    background-color: #ccc; +    border: 1px solid #333; +    padding: 0px; +} + +table.form { +    background-color: #999; +    border: 1px solid #333;  +    padding: 2px; +} + +td.form_label { +    text-align: right; +    vertical-align: top; +} +/* For one line entires */ +td.form_clabel { +    text-align: right; +    vertical-align: center; +} +td.form_value { +    text-align: left; +    vertical-align: top; +} +td.form_submit { +    text-align: right; +    vertical-align: top; +} + +h1.SubmitFail { +    color: #f00; +} +h1.SubmitOk { +} diff --git a/clang/tools/scan-build/set-xcode-analyzer b/clang/tools/scan-build/set-xcode-analyzer new file mode 100755 index 0000000..06e1d85 --- /dev/null +++ b/clang/tools/scan-build/set-xcode-analyzer @@ -0,0 +1,89 @@ +#!/usr/bin/python + +# [PR 11661] Note that we hardwire to /usr/bin/python because we +# want to the use the system version of Python on Mac OS X. +# This one has the scripting bridge enabled. + +import os +import subprocess +import sys +import re +import tempfile +import shutil +import stat +from AppKit import * + +def FindClangSpecs(path): +  print "(+) Searching for xcspec file in: ", path +  for root, dirs, files in os.walk(path): +    for f in files: +      if f.endswith(".xcspec") and f.startswith("Clang LLVM"): +        yield os.path.join(root, f) + +def ModifySpec(path, pathToChecker): +  t = tempfile.NamedTemporaryFile(delete=False) +  foundAnalyzer = False +  with open(path) as f: +    for line in f: +      if not foundAnalyzer: +        if line.find("Static Analyzer") >= 0: +          foundAnalyzer = True +      else: +        m = re.search('^(\s*ExecPath\s*=\s*")', line) +        if m: +          line = "".join([m.group(0), pathToChecker, '";\n']) +      t.write(line) +  t.close() +  print "(+) processing:", path +  try: +    shutil.copy(t.name, path) +    os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH) +  except IOError, why: +    print "    (-) Cannot update file:", why, "\n" +  except OSError, why: +    print "    (-) Cannot update file:", why, "\n" +  os.unlink(t.name) + +def main(): +  from optparse import OptionParser +  parser = OptionParser('usage: %prog [options]') +  parser.set_description(__doc__) +  parser.add_option("--use-checker-build", dest="path", +                    help="Use the Clang located at the provided absolute path, e.g. /Users/foo/checker-1") +  parser.add_option("--use-xcode-clang", action="store_const",  +                    const="$(CLANG)", dest="default", +                    help="Use the Clang bundled with Xcode") +  (options, args) = parser.parse_args() +  if options.path is None and options.default is None: +    parser.error("You must specify a version of Clang to use for static analysis.  Specify '-h' for details") + +  # determine if Xcode is running +  for x in NSWorkspace.sharedWorkspace().runningApplications(): +    if x.localizedName().find("Xcode") >= 0: +      print "(-) You must quit Xcode first before modifying its configuration files." +      return + +  if options.path: +    # Expand tildes. +    path = os.path.expanduser(options.path) +    if not path.endswith("clang"): +      print "(+) Using Clang bundled with checker build:", path +      path = os.path.join(path, "bin", "clang"); +    else: +      print "(+) Using Clang located at:", path +  else: +    print "(+) Using the Clang bundled with Xcode" +    path = options.default +   +  xcode_path = subprocess.check_output(["xcode-select", "-print-path"]) +  if (re.search("Xcode.app", xcode_path)): +    # Cut off the 'Developer' dir, as the xcspec lies in another part +    # of the Xcode.app subtree. +    xcode_path = os.path.dirname(xcode_path) +   +  for x in FindClangSpecs(xcode_path): +    ModifySpec(x, path) + +if __name__ == '__main__': +  main() + diff --git a/clang/tools/scan-build/sorttable.js b/clang/tools/scan-build/sorttable.js new file mode 100644 index 0000000..4352d3b --- /dev/null +++ b/clang/tools/scan-build/sorttable.js @@ -0,0 +1,493 @@ +/* +  SortTable +  version 2 +  7th April 2007 +  Stuart Langridge, http://www.kryogenix.org/code/browser/sorttable/ +   +  Instructions: +  Download this file +  Add <script src="sorttable.js"></script> to your HTML +  Add class="sortable" to any table you'd like to make sortable +  Click on the headers to sort +   +  Thanks to many, many people for contributions and suggestions. +  Licenced as X11: http://www.kryogenix.org/code/browser/licence.html +  This basically means: do what you want with it. +*/ + +  +var stIsIE = /*@cc_on!@*/false; + +sorttable = { +  init: function() { +    // quit if this function has already been called +    if (arguments.callee.done) return; +    // flag this function so we don't do the same thing twice +    arguments.callee.done = true; +    // kill the timer +    if (_timer) clearInterval(_timer); +     +    if (!document.createElement || !document.getElementsByTagName) return; +     +    sorttable.DATE_RE = /^(\d\d?)[\/\.-](\d\d?)[\/\.-]((\d\d)?\d\d)$/; +     +    forEach(document.getElementsByTagName('table'), function(table) { +      if (table.className.search(/\bsortable\b/) != -1) { +        sorttable.makeSortable(table); +      } +    }); +     +  }, +   +  makeSortable: function(table) { +    if (table.getElementsByTagName('thead').length == 0) { +      // table doesn't have a tHead. Since it should have, create one and +      // put the first table row in it. +      the = document.createElement('thead'); +      the.appendChild(table.rows[0]); +      table.insertBefore(the,table.firstChild); +    } +    // Safari doesn't support table.tHead, sigh +    if (table.tHead == null) table.tHead = table.getElementsByTagName('thead')[0]; +     +    if (table.tHead.rows.length != 1) return; // can't cope with two header rows +     +    // Sorttable v1 put rows with a class of "sortbottom" at the bottom (as +    // "total" rows, for example). This is B&R, since what you're supposed +    // to do is put them in a tfoot. So, if there are sortbottom rows, +    // for backwards compatibility, move them to tfoot (creating it if needed). +    sortbottomrows = []; +    for (var i=0; i<table.rows.length; i++) { +      if (table.rows[i].className.search(/\bsortbottom\b/) != -1) { +        sortbottomrows[sortbottomrows.length] = table.rows[i]; +      } +    } +    if (sortbottomrows) { +      if (table.tFoot == null) { +        // table doesn't have a tfoot. Create one. +        tfo = document.createElement('tfoot'); +        table.appendChild(tfo); +      } +      for (var i=0; i<sortbottomrows.length; i++) { +        tfo.appendChild(sortbottomrows[i]); +      } +      delete sortbottomrows; +    } +     +    // work through each column and calculate its type +    headrow = table.tHead.rows[0].cells; +    for (var i=0; i<headrow.length; i++) { +      // manually override the type with a sorttable_type attribute +      if (!headrow[i].className.match(/\bsorttable_nosort\b/)) { // skip this col +        mtch = headrow[i].className.match(/\bsorttable_([a-z0-9]+)\b/); +        if (mtch) { override = mtch[1]; } +	      if (mtch && typeof sorttable["sort_"+override] == 'function') { +	        headrow[i].sorttable_sortfunction = sorttable["sort_"+override]; +	      } else { +	        headrow[i].sorttable_sortfunction = sorttable.guessType(table,i); +	      } +	      // make it clickable to sort +	      headrow[i].sorttable_columnindex = i; +	      headrow[i].sorttable_tbody = table.tBodies[0]; +	      dean_addEvent(headrow[i],"click", function(e) { + +          if (this.className.search(/\bsorttable_sorted\b/) != -1) { +            // if we're already sorted by this column, just  +            // reverse the table, which is quicker +            sorttable.reverse(this.sorttable_tbody); +            this.className = this.className.replace('sorttable_sorted', +                                                    'sorttable_sorted_reverse'); +            this.removeChild(document.getElementById('sorttable_sortfwdind')); +            sortrevind = document.createElement('span'); +            sortrevind.id = "sorttable_sortrevind"; +            sortrevind.innerHTML = stIsIE ? ' <font face="webdings">5</font>' : ' ▴'; +            this.appendChild(sortrevind); +            return; +          } +          if (this.className.search(/\bsorttable_sorted_reverse\b/) != -1) { +            // if we're already sorted by this column in reverse, just  +            // re-reverse the table, which is quicker +            sorttable.reverse(this.sorttable_tbody); +            this.className = this.className.replace('sorttable_sorted_reverse', +                                                    'sorttable_sorted'); +            this.removeChild(document.getElementById('sorttable_sortrevind')); +            sortfwdind = document.createElement('span'); +            sortfwdind.id = "sorttable_sortfwdind"; +            sortfwdind.innerHTML = stIsIE ? ' <font face="webdings">6</font>' : ' ▾'; +            this.appendChild(sortfwdind); +            return; +          } +           +          // remove sorttable_sorted classes +          theadrow = this.parentNode; +          forEach(theadrow.childNodes, function(cell) { +            if (cell.nodeType == 1) { // an element +              cell.className = cell.className.replace('sorttable_sorted_reverse',''); +              cell.className = cell.className.replace('sorttable_sorted',''); +            } +          }); +          sortfwdind = document.getElementById('sorttable_sortfwdind'); +          if (sortfwdind) { sortfwdind.parentNode.removeChild(sortfwdind); } +          sortrevind = document.getElementById('sorttable_sortrevind'); +          if (sortrevind) { sortrevind.parentNode.removeChild(sortrevind); } +           +          this.className += ' sorttable_sorted'; +          sortfwdind = document.createElement('span'); +          sortfwdind.id = "sorttable_sortfwdind"; +          sortfwdind.innerHTML = stIsIE ? ' <font face="webdings">6</font>' : ' ▾'; +          this.appendChild(sortfwdind); + +	        // build an array to sort. This is a Schwartzian transform thing, +	        // i.e., we "decorate" each row with the actual sort key, +	        // sort based on the sort keys, and then put the rows back in order +	        // which is a lot faster because you only do getInnerText once per row +	        row_array = []; +	        col = this.sorttable_columnindex; +	        rows = this.sorttable_tbody.rows; +	        for (var j=0; j<rows.length; j++) { +	          row_array[row_array.length] = [sorttable.getInnerText(rows[j].cells[col]), rows[j]]; +	        } +	        /* If you want a stable sort, uncomment the following line */ +	        sorttable.shaker_sort(row_array, this.sorttable_sortfunction); +	        /* and comment out this one */ +	        //row_array.sort(this.sorttable_sortfunction); +	         +	        tb = this.sorttable_tbody; +	        for (var j=0; j<row_array.length; j++) { +	          tb.appendChild(row_array[j][1]); +	        } +	         +	        delete row_array; +	      }); +	    } +    } +  }, +   +  guessType: function(table, column) { +    // guess the type of a column based on its first non-blank row +    sortfn = sorttable.sort_alpha; +    for (var i=0; i<table.tBodies[0].rows.length; i++) { +      text = sorttable.getInnerText(table.tBodies[0].rows[i].cells[column]); +      if (text != '') { +        if (text.match(/^-?[£$¤]?[\d,.]+%?$/)) { +          return sorttable.sort_numeric; +        } +        // check for a date: dd/mm/yyyy or dd/mm/yy  +        // can have / or . or - as separator +        // can be mm/dd as well +        possdate = text.match(sorttable.DATE_RE) +        if (possdate) { +          // looks like a date +          first = parseInt(possdate[1]); +          second = parseInt(possdate[2]); +          if (first > 12) { +            // definitely dd/mm +            return sorttable.sort_ddmm; +          } else if (second > 12) { +            return sorttable.sort_mmdd; +          } else { +            // looks like a date, but we can't tell which, so assume +            // that it's dd/mm (English imperialism!) and keep looking +            sortfn = sorttable.sort_ddmm; +          } +        } +      } +    } +    return sortfn; +  }, +   +  getInnerText: function(node) { +    // gets the text we want to use for sorting for a cell. +    // strips leading and trailing whitespace. +    // this is *not* a generic getInnerText function; it's special to sorttable. +    // for example, you can override the cell text with a customkey attribute. +    // it also gets .value for <input> fields. +     +    hasInputs = (typeof node.getElementsByTagName == 'function') && +                 node.getElementsByTagName('input').length; +     +    if (node.getAttribute("sorttable_customkey") != null) { +      return node.getAttribute("sorttable_customkey"); +    } +    else if (typeof node.textContent != 'undefined' && !hasInputs) { +      return node.textContent.replace(/^\s+|\s+$/g, ''); +    } +    else if (typeof node.innerText != 'undefined' && !hasInputs) { +      return node.innerText.replace(/^\s+|\s+$/g, ''); +    } +    else if (typeof node.text != 'undefined' && !hasInputs) { +      return node.text.replace(/^\s+|\s+$/g, ''); +    } +    else { +      switch (node.nodeType) { +        case 3: +          if (node.nodeName.toLowerCase() == 'input') { +            return node.value.replace(/^\s+|\s+$/g, ''); +          } +        case 4: +          return node.nodeValue.replace(/^\s+|\s+$/g, ''); +          break; +        case 1: +        case 11: +          var innerText = ''; +          for (var i = 0; i < node.childNodes.length; i++) { +            innerText += sorttable.getInnerText(node.childNodes[i]); +          } +          return innerText.replace(/^\s+|\s+$/g, ''); +          break; +        default: +          return ''; +      } +    } +  }, +   +  reverse: function(tbody) { +    // reverse the rows in a tbody +    newrows = []; +    for (var i=0; i<tbody.rows.length; i++) { +      newrows[newrows.length] = tbody.rows[i]; +    } +    for (var i=newrows.length-1; i>=0; i--) { +       tbody.appendChild(newrows[i]); +    } +    delete newrows; +  }, +   +  /* sort functions +     each sort function takes two parameters, a and b +     you are comparing a[0] and b[0] */ +  sort_numeric: function(a,b) { +    aa = parseFloat(a[0].replace(/[^0-9.-]/g,'')); +    if (isNaN(aa)) aa = 0; +    bb = parseFloat(b[0].replace(/[^0-9.-]/g,''));  +    if (isNaN(bb)) bb = 0; +    return aa-bb; +  }, +  sort_alpha: function(a,b) { +    if (a[0]==b[0]) return 0; +    if (a[0]<b[0]) return -1; +    return 1; +  }, +  sort_ddmm: function(a,b) { +    mtch = a[0].match(sorttable.DATE_RE); +    y = mtch[3]; m = mtch[2]; d = mtch[1]; +    if (m.length == 1) m = '0'+m; +    if (d.length == 1) d = '0'+d; +    dt1 = y+m+d; +    mtch = b[0].match(sorttable.DATE_RE); +    y = mtch[3]; m = mtch[2]; d = mtch[1]; +    if (m.length == 1) m = '0'+m; +    if (d.length == 1) d = '0'+d; +    dt2 = y+m+d; +    if (dt1==dt2) return 0; +    if (dt1<dt2) return -1; +    return 1; +  }, +  sort_mmdd: function(a,b) { +    mtch = a[0].match(sorttable.DATE_RE); +    y = mtch[3]; d = mtch[2]; m = mtch[1]; +    if (m.length == 1) m = '0'+m; +    if (d.length == 1) d = '0'+d; +    dt1 = y+m+d; +    mtch = b[0].match(sorttable.DATE_RE); +    y = mtch[3]; d = mtch[2]; m = mtch[1]; +    if (m.length == 1) m = '0'+m; +    if (d.length == 1) d = '0'+d; +    dt2 = y+m+d; +    if (dt1==dt2) return 0; +    if (dt1<dt2) return -1; +    return 1; +  }, +   +  shaker_sort: function(list, comp_func) { +    // A stable sort function to allow multi-level sorting of data +    // see: http://en.wikipedia.org/wiki/Cocktail_sort +    // thanks to Joseph Nahmias +    var b = 0; +    var t = list.length - 1; +    var swap = true; + +    while(swap) { +        swap = false; +        for(var i = b; i < t; ++i) { +            if ( comp_func(list[i], list[i+1]) > 0 ) { +                var q = list[i]; list[i] = list[i+1]; list[i+1] = q; +                swap = true; +            } +        } // for +        t--; + +        if (!swap) break; + +        for(var i = t; i > b; --i) { +            if ( comp_func(list[i], list[i-1]) < 0 ) { +                var q = list[i]; list[i] = list[i-1]; list[i-1] = q; +                swap = true; +            } +        } // for +        b++; + +    } // while(swap) +  }   +} + +/* ****************************************************************** +   Supporting functions: bundled here to avoid depending on a library +   ****************************************************************** */ + +// Dean Edwards/Matthias Miller/John Resig + +/* for Mozilla/Opera9 */ +if (document.addEventListener) { +    document.addEventListener("DOMContentLoaded", sorttable.init, false); +} + +/* for Internet Explorer */ +/*@cc_on @*/ +/*@if (@_win32) +    document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>"); +    var script = document.getElementById("__ie_onload"); +    script.onreadystatechange = function() { +        if (this.readyState == "complete") { +            sorttable.init(); // call the onload handler +        } +    }; +/*@end @*/ + +/* for Safari */ +if (/WebKit/i.test(navigator.userAgent)) { // sniff +    var _timer = setInterval(function() { +        if (/loaded|complete/.test(document.readyState)) { +            sorttable.init(); // call the onload handler +        } +    }, 10); +} + +/* for other browsers */ +window.onload = sorttable.init; + +// written by Dean Edwards, 2005 +// with input from Tino Zijdel, Matthias Miller, Diego Perini + +// http://dean.edwards.name/weblog/2005/10/add-event/ + +function dean_addEvent(element, type, handler) { +	if (element.addEventListener) { +		element.addEventListener(type, handler, false); +	} else { +		// assign each event handler a unique ID +		if (!handler.$$guid) handler.$$guid = dean_addEvent.guid++; +		// create a hash table of event types for the element +		if (!element.events) element.events = {}; +		// create a hash table of event handlers for each element/event pair +		var handlers = element.events[type]; +		if (!handlers) { +			handlers = element.events[type] = {}; +			// store the existing event handler (if there is one) +			if (element["on" + type]) { +				handlers[0] = element["on" + type]; +			} +		} +		// store the event handler in the hash table +		handlers[handler.$$guid] = handler; +		// assign a global event handler to do all the work +		element["on" + type] = handleEvent; +	} +}; +// a counter used to create unique IDs +dean_addEvent.guid = 1; + +function removeEvent(element, type, handler) { +	if (element.removeEventListener) { +		element.removeEventListener(type, handler, false); +	} else { +		// delete the event handler from the hash table +		if (element.events && element.events[type]) { +			delete element.events[type][handler.$$guid]; +		} +	} +}; + +function handleEvent(event) { +	var returnValue = true; +	// grab the event object (IE uses a global event object) +	event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event); +	// get a reference to the hash table of event handlers +	var handlers = this.events[event.type]; +	// execute each event handler +	for (var i in handlers) { +		this.$$handleEvent = handlers[i]; +		if (this.$$handleEvent(event) === false) { +			returnValue = false; +		} +	} +	return returnValue; +}; + +function fixEvent(event) { +	// add W3C standard event methods +	event.preventDefault = fixEvent.preventDefault; +	event.stopPropagation = fixEvent.stopPropagation; +	return event; +}; +fixEvent.preventDefault = function() { +	this.returnValue = false; +}; +fixEvent.stopPropagation = function() { +  this.cancelBubble = true; +} + +// Dean's forEach: http://dean.edwards.name/base/forEach.js +/* +	forEach, version 1.0 +	Copyright 2006, Dean Edwards +	License: http://www.opensource.org/licenses/mit-license.php +*/ + +// array-like enumeration +if (!Array.forEach) { // mozilla already supports this +	Array.forEach = function(array, block, context) { +		for (var i = 0; i < array.length; i++) { +			block.call(context, array[i], i, array); +		} +	}; +} + +// generic enumeration +Function.prototype.forEach = function(object, block, context) { +	for (var key in object) { +		if (typeof this.prototype[key] == "undefined") { +			block.call(context, object[key], key, object); +		} +	} +}; + +// character enumeration +String.forEach = function(string, block, context) { +	Array.forEach(string.split(""), function(chr, index) { +		block.call(context, chr, index, string); +	}); +}; + +// globally resolve forEach enumeration +var forEach = function(object, block, context) { +	if (object) { +		var resolve = Object; // default +		if (object instanceof Function) { +			// functions have a "length" property +			resolve = Function; +		} else if (object.forEach instanceof Function) { +			// the object implements a custom forEach method so use that +			object.forEach(block, context); +			return; +		} else if (typeof object == "string") { +			// the object is a string +			resolve = String; +		} else if (typeof object.length == "number") { +			// the object is array-like +			resolve = Array; +		} +		resolve.forEach(object, block, context); +	} +}; + | 
