forked from OSchip/llvm-project
				
			[analyzer] FixItHint: Apply and test hints with the Clang-Tidy's script
Summary: This patch introduces a way to apply the fix-its by the Analyzer: `-analyzer-config apply-fixits=true`. The fix-its should be testable, therefore I have copied the well-tested `check_clang_tidy.py` script. The idea is that the Analyzer's workflow is different so it would be very difficult to use only one script for both Tidy and the Analyzer, the script would diverge a lot. Example test: `// RUN: %check-analyzer-fixit %s %t -analyzer-checker=core` When the copy-paste happened the original authors were: @alexfh, @zinovy.nis, @JonasToth, @hokein, @gribozavr, @lebedev.ri Reviewed By: NoQ, alexfh, zinovy.nis Differential Revision: https://reviews.llvm.org/D69746
This commit is contained in:
		
							parent
							
								
									cac068600e
								
							
						
					
					
						commit
						f69c74db34
					
				| 
						 | 
					@ -310,6 +310,10 @@ ANALYZER_OPTION(bool, ShouldEmitFixItHintsAsRemarks, "fixits-as-remarks",
 | 
				
			||||||
                "Emit fix-it hints as remarks for testing purposes",
 | 
					                "Emit fix-it hints as remarks for testing purposes",
 | 
				
			||||||
                false)
 | 
					                false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ANALYZER_OPTION(bool, ShouldApplyFixIts, "apply-fixits",
 | 
				
			||||||
 | 
					                "Apply the fix-it hints to the files",
 | 
				
			||||||
 | 
					                false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//===----------------------------------------------------------------------===//
 | 
					//===----------------------------------------------------------------------===//
 | 
				
			||||||
// Unsigned analyzer options.
 | 
					// Unsigned analyzer options.
 | 
				
			||||||
//===----------------------------------------------------------------------===//
 | 
					//===----------------------------------------------------------------------===//
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,7 +12,6 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
 | 
					#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
 | 
				
			||||||
#include "ModelInjector.h"
 | 
					#include "ModelInjector.h"
 | 
				
			||||||
#include "clang/Analysis/PathDiagnostic.h"
 | 
					 | 
				
			||||||
#include "clang/AST/Decl.h"
 | 
					#include "clang/AST/Decl.h"
 | 
				
			||||||
#include "clang/AST/DeclCXX.h"
 | 
					#include "clang/AST/DeclCXX.h"
 | 
				
			||||||
#include "clang/AST/DeclObjC.h"
 | 
					#include "clang/AST/DeclObjC.h"
 | 
				
			||||||
| 
						 | 
					@ -21,10 +20,12 @@
 | 
				
			||||||
#include "clang/Analysis/CFG.h"
 | 
					#include "clang/Analysis/CFG.h"
 | 
				
			||||||
#include "clang/Analysis/CallGraph.h"
 | 
					#include "clang/Analysis/CallGraph.h"
 | 
				
			||||||
#include "clang/Analysis/CodeInjector.h"
 | 
					#include "clang/Analysis/CodeInjector.h"
 | 
				
			||||||
 | 
					#include "clang/Analysis/PathDiagnostic.h"
 | 
				
			||||||
#include "clang/Basic/SourceManager.h"
 | 
					#include "clang/Basic/SourceManager.h"
 | 
				
			||||||
#include "clang/CrossTU/CrossTranslationUnit.h"
 | 
					#include "clang/CrossTU/CrossTranslationUnit.h"
 | 
				
			||||||
#include "clang/Frontend/CompilerInstance.h"
 | 
					#include "clang/Frontend/CompilerInstance.h"
 | 
				
			||||||
#include "clang/Lex/Preprocessor.h"
 | 
					#include "clang/Lex/Preprocessor.h"
 | 
				
			||||||
 | 
					#include "clang/Rewrite/Core/Rewriter.h"
 | 
				
			||||||
#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h"
 | 
					#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h"
 | 
				
			||||||
#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
 | 
					#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
 | 
				
			||||||
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
 | 
					#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
 | 
				
			||||||
| 
						 | 
					@ -33,6 +34,8 @@
 | 
				
			||||||
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
 | 
					#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
 | 
				
			||||||
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
 | 
					#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
 | 
				
			||||||
#include "clang/StaticAnalyzer/Frontend/CheckerRegistration.h"
 | 
					#include "clang/StaticAnalyzer/Frontend/CheckerRegistration.h"
 | 
				
			||||||
 | 
					#include "clang/Tooling/Core/Replacement.h"
 | 
				
			||||||
 | 
					#include "clang/Tooling/Tooling.h"
 | 
				
			||||||
#include "llvm/ADT/PostOrderIterator.h"
 | 
					#include "llvm/ADT/PostOrderIterator.h"
 | 
				
			||||||
#include "llvm/ADT/Statistic.h"
 | 
					#include "llvm/ADT/Statistic.h"
 | 
				
			||||||
#include "llvm/Support/FileSystem.h"
 | 
					#include "llvm/Support/FileSystem.h"
 | 
				
			||||||
| 
						 | 
					@ -46,6 +49,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using namespace clang;
 | 
					using namespace clang;
 | 
				
			||||||
using namespace ento;
 | 
					using namespace ento;
 | 
				
			||||||
 | 
					using namespace tooling;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define DEBUG_TYPE "AnalysisConsumer"
 | 
					#define DEBUG_TYPE "AnalysisConsumer"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -84,11 +88,16 @@ void ento::createTextPathDiagnosticConsumer(
 | 
				
			||||||
namespace {
 | 
					namespace {
 | 
				
			||||||
class ClangDiagPathDiagConsumer : public PathDiagnosticConsumer {
 | 
					class ClangDiagPathDiagConsumer : public PathDiagnosticConsumer {
 | 
				
			||||||
  DiagnosticsEngine &Diag;
 | 
					  DiagnosticsEngine &Diag;
 | 
				
			||||||
  bool IncludePath = false, ShouldEmitAsError = false, FixitsAsRemarks = false;
 | 
					  LangOptions LO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool IncludePath = false;
 | 
				
			||||||
 | 
					  bool ShouldEmitAsError = false;
 | 
				
			||||||
 | 
					  bool FixitsAsRemarks = false;
 | 
				
			||||||
 | 
					  bool ApplyFixIts = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
  ClangDiagPathDiagConsumer(DiagnosticsEngine &Diag)
 | 
					  ClangDiagPathDiagConsumer(DiagnosticsEngine &Diag, LangOptions LO)
 | 
				
			||||||
      : Diag(Diag) {}
 | 
					      : Diag(Diag), LO(LO) {}
 | 
				
			||||||
  ~ClangDiagPathDiagConsumer() override {}
 | 
					  ~ClangDiagPathDiagConsumer() override {}
 | 
				
			||||||
  StringRef getName() const override { return "ClangDiags"; }
 | 
					  StringRef getName() const override { return "ClangDiags"; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -102,6 +111,7 @@ public:
 | 
				
			||||||
  void enablePaths() { IncludePath = true; }
 | 
					  void enablePaths() { IncludePath = true; }
 | 
				
			||||||
  void enableWerror() { ShouldEmitAsError = true; }
 | 
					  void enableWerror() { ShouldEmitAsError = true; }
 | 
				
			||||||
  void enableFixitsAsRemarks() { FixitsAsRemarks = true; }
 | 
					  void enableFixitsAsRemarks() { FixitsAsRemarks = true; }
 | 
				
			||||||
 | 
					  void enableApplyFixIts() { ApplyFixIts = true; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
 | 
					  void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
 | 
				
			||||||
                            FilesMade *filesMade) override {
 | 
					                            FilesMade *filesMade) override {
 | 
				
			||||||
| 
						 | 
					@ -111,29 +121,44 @@ public:
 | 
				
			||||||
            : Diag.getCustomDiagID(DiagnosticsEngine::Warning, "%0");
 | 
					            : Diag.getCustomDiagID(DiagnosticsEngine::Warning, "%0");
 | 
				
			||||||
    unsigned NoteID = Diag.getCustomDiagID(DiagnosticsEngine::Note, "%0");
 | 
					    unsigned NoteID = Diag.getCustomDiagID(DiagnosticsEngine::Note, "%0");
 | 
				
			||||||
    unsigned RemarkID = Diag.getCustomDiagID(DiagnosticsEngine::Remark, "%0");
 | 
					    unsigned RemarkID = Diag.getCustomDiagID(DiagnosticsEngine::Remark, "%0");
 | 
				
			||||||
 | 
					    SourceManager &SM = Diag.getSourceManager();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto reportPiece =
 | 
					    Replacements Repls;
 | 
				
			||||||
        [&](unsigned ID, SourceLocation Loc, StringRef String,
 | 
					    auto reportPiece = [&](unsigned ID, FullSourceLoc Loc, StringRef String,
 | 
				
			||||||
            ArrayRef<SourceRange> Ranges, ArrayRef<FixItHint> Fixits) {
 | 
					                           ArrayRef<SourceRange> Ranges,
 | 
				
			||||||
          if (!FixitsAsRemarks) {
 | 
					                           ArrayRef<FixItHint> Fixits) {
 | 
				
			||||||
            Diag.Report(Loc, ID) << String << Ranges << Fixits;
 | 
					      if (!FixitsAsRemarks && !ApplyFixIts) {
 | 
				
			||||||
          } else {
 | 
					        Diag.Report(Loc, ID) << String << Ranges << Fixits;
 | 
				
			||||||
            Diag.Report(Loc, ID) << String << Ranges;
 | 
					        return;
 | 
				
			||||||
            for (const FixItHint &Hint : Fixits) {
 | 
					      }
 | 
				
			||||||
              SourceManager &SM = Diag.getSourceManager();
 | 
					
 | 
				
			||||||
              llvm::SmallString<128> Str;
 | 
					      Diag.Report(Loc, ID) << String << Ranges;
 | 
				
			||||||
              llvm::raw_svector_ostream OS(Str);
 | 
					      if (FixitsAsRemarks) {
 | 
				
			||||||
              // FIXME: Add support for InsertFromRange and
 | 
					        for (const FixItHint &Hint : Fixits) {
 | 
				
			||||||
              // BeforePreviousInsertion.
 | 
					          llvm::SmallString<128> Str;
 | 
				
			||||||
              assert(!Hint.InsertFromRange.isValid() && "Not implemented yet!");
 | 
					          llvm::raw_svector_ostream OS(Str);
 | 
				
			||||||
              assert(!Hint.BeforePreviousInsertions && "Not implemented yet!");
 | 
					          // FIXME: Add support for InsertFromRange and
 | 
				
			||||||
              OS << SM.getSpellingColumnNumber(Hint.RemoveRange.getBegin())
 | 
					          // BeforePreviousInsertion.
 | 
				
			||||||
                 << "-" << SM.getSpellingColumnNumber(Hint.RemoveRange.getEnd())
 | 
					          assert(!Hint.InsertFromRange.isValid() && "Not implemented yet!");
 | 
				
			||||||
                 << ": '" << Hint.CodeToInsert << "'";
 | 
					          assert(!Hint.BeforePreviousInsertions && "Not implemented yet!");
 | 
				
			||||||
              Diag.Report(Loc, RemarkID) << OS.str();
 | 
					          OS << SM.getSpellingColumnNumber(Hint.RemoveRange.getBegin()) << "-"
 | 
				
			||||||
            }
 | 
					             << SM.getSpellingColumnNumber(Hint.RemoveRange.getEnd()) << ": '"
 | 
				
			||||||
 | 
					             << Hint.CodeToInsert << "'";
 | 
				
			||||||
 | 
					          Diag.Report(Loc, RemarkID) << OS.str();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (ApplyFixIts) {
 | 
				
			||||||
 | 
					        for (const FixItHint &Hint : Fixits) {
 | 
				
			||||||
 | 
					          Replacement Repl(SM, Hint.RemoveRange, Hint.CodeToInsert);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          if (llvm::Error Err = Repls.add(Repl)) {
 | 
				
			||||||
 | 
					            llvm::errs() << "Error applying replacement " << Repl.toString()
 | 
				
			||||||
 | 
					                         << ": " << Err << "\n";
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        };
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (std::vector<const PathDiagnostic *>::iterator I = Diags.begin(),
 | 
					    for (std::vector<const PathDiagnostic *>::iterator I = Diags.begin(),
 | 
				
			||||||
         E = Diags.end();
 | 
					         E = Diags.end();
 | 
				
			||||||
| 
						 | 
					@ -165,6 +190,16 @@ public:
 | 
				
			||||||
                    Piece->getString(), Piece->getRanges(), Piece->getFixits());
 | 
					                    Piece->getString(), Piece->getRanges(), Piece->getFixits());
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!ApplyFixIts || Repls.empty())
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Rewriter Rewrite(SM, LO);
 | 
				
			||||||
 | 
					    if (!applyAllReplacements(Repls, Rewrite)) {
 | 
				
			||||||
 | 
					      llvm::errs() << "An error occured during applying fix-it.\n";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Rewrite.overwriteChangedFiles();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
} // end anonymous namespace
 | 
					} // end anonymous namespace
 | 
				
			||||||
| 
						 | 
					@ -257,7 +292,7 @@ public:
 | 
				
			||||||
    if (Opts->AnalysisDiagOpt != PD_NONE) {
 | 
					    if (Opts->AnalysisDiagOpt != PD_NONE) {
 | 
				
			||||||
      // Create the PathDiagnosticConsumer.
 | 
					      // Create the PathDiagnosticConsumer.
 | 
				
			||||||
      ClangDiagPathDiagConsumer *clangDiags =
 | 
					      ClangDiagPathDiagConsumer *clangDiags =
 | 
				
			||||||
          new ClangDiagPathDiagConsumer(PP.getDiagnostics());
 | 
					          new ClangDiagPathDiagConsumer(PP.getDiagnostics(), PP.getLangOpts());
 | 
				
			||||||
      PathConsumers.push_back(clangDiags);
 | 
					      PathConsumers.push_back(clangDiags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (Opts->AnalyzerWerror)
 | 
					      if (Opts->AnalyzerWerror)
 | 
				
			||||||
| 
						 | 
					@ -266,6 +301,9 @@ public:
 | 
				
			||||||
      if (Opts->ShouldEmitFixItHintsAsRemarks)
 | 
					      if (Opts->ShouldEmitFixItHintsAsRemarks)
 | 
				
			||||||
        clangDiags->enableFixitsAsRemarks();
 | 
					        clangDiags->enableFixitsAsRemarks();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (Opts->ShouldApplyFixIts)
 | 
				
			||||||
 | 
					        clangDiags->enableApplyFixIts();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (Opts->AnalysisDiagOpt == PD_TEXT) {
 | 
					      if (Opts->AnalysisDiagOpt == PD_TEXT) {
 | 
				
			||||||
        clangDiags->enablePaths();
 | 
					        clangDiags->enablePaths();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,4 +21,6 @@ add_clang_library(clangStaticAnalyzerFrontend
 | 
				
			||||||
  clangLex
 | 
					  clangLex
 | 
				
			||||||
  clangStaticAnalyzerCheckers
 | 
					  clangStaticAnalyzerCheckers
 | 
				
			||||||
  clangStaticAnalyzerCore
 | 
					  clangStaticAnalyzerCore
 | 
				
			||||||
 | 
					  clangRewrite
 | 
				
			||||||
 | 
					  clangToolingCore
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,6 +11,7 @@
 | 
				
			||||||
// CHECK-NEXT: alpha.security.MmapWriteExec:MmapProtExec = 0x04
 | 
					// CHECK-NEXT: alpha.security.MmapWriteExec:MmapProtExec = 0x04
 | 
				
			||||||
// CHECK-NEXT: alpha.security.MmapWriteExec:MmapProtRead = 0x01
 | 
					// CHECK-NEXT: alpha.security.MmapWriteExec:MmapProtRead = 0x01
 | 
				
			||||||
// CHECK-NEXT: alpha.security.taint.TaintPropagation:Config = ""
 | 
					// CHECK-NEXT: alpha.security.taint.TaintPropagation:Config = ""
 | 
				
			||||||
 | 
					// CHECK-NEXT: apply-fixits = false
 | 
				
			||||||
// CHECK-NEXT: avoid-suppressing-null-argument-paths = false
 | 
					// CHECK-NEXT: avoid-suppressing-null-argument-paths = false
 | 
				
			||||||
// CHECK-NEXT: c++-allocator-inlining = true
 | 
					// CHECK-NEXT: c++-allocator-inlining = true
 | 
				
			||||||
// CHECK-NEXT: c++-container-inlining = false
 | 
					// CHECK-NEXT: c++-container-inlining = false
 | 
				
			||||||
| 
						 | 
					@ -100,4 +101,4 @@
 | 
				
			||||||
// CHECK-NEXT: unroll-loops = false
 | 
					// CHECK-NEXT: unroll-loops = false
 | 
				
			||||||
// CHECK-NEXT: widen-loops = false
 | 
					// CHECK-NEXT: widen-loops = false
 | 
				
			||||||
// CHECK-NEXT: [stats]
 | 
					// CHECK-NEXT: [stats]
 | 
				
			||||||
// CHECK-NEXT: num-entries = 97
 | 
					// CHECK-NEXT: num-entries = 98
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,121 @@
 | 
				
			||||||
 | 
					#!/usr/bin/env python
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					#===- check-analyzer-fixit.py - Static Analyzer test helper ---*- python -*-===#
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | 
				
			||||||
 | 
					# See https://llvm.org/LICENSE.txt for license information.
 | 
				
			||||||
 | 
					# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					#===------------------------------------------------------------------------===#
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# This file copy-pasted mostly from the Clang-Tidy's 'check_clang_tidy.py'.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					#===------------------------------------------------------------------------===#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					r"""
 | 
				
			||||||
 | 
					Clang Static Analyzer test helper
 | 
				
			||||||
 | 
					=================================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This script runs the Analyzer in fix-it mode and verify fixes, warnings, notes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Usage:
 | 
				
			||||||
 | 
					  check-analyzer-fixit.py <source-file> <temp-file> [analyzer arguments]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Example:
 | 
				
			||||||
 | 
					  // RUN: %check-analyzer-fixit %s %t -analyzer-checker=core
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import argparse
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import re
 | 
				
			||||||
 | 
					import subprocess
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def write_file(file_name, text):
 | 
				
			||||||
 | 
					    with open(file_name, 'w') as f:
 | 
				
			||||||
 | 
					        f.write(text)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def run_test_once(args, extra_args):
 | 
				
			||||||
 | 
					    input_file_name = args.input_file_name
 | 
				
			||||||
 | 
					    temp_file_name = args.temp_file_name
 | 
				
			||||||
 | 
					    clang_analyzer_extra_args = extra_args
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    file_name_with_extension = input_file_name
 | 
				
			||||||
 | 
					    _, extension = os.path.splitext(file_name_with_extension)
 | 
				
			||||||
 | 
					    if extension not in ['.c', '.hpp', '.m', '.mm']:
 | 
				
			||||||
 | 
					        extension = '.cpp'
 | 
				
			||||||
 | 
					    temp_file_name = temp_file_name + extension
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    with open(input_file_name, 'r') as input_file:
 | 
				
			||||||
 | 
					        input_text = input_file.read()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Remove the contents of the CHECK lines to avoid CHECKs matching on
 | 
				
			||||||
 | 
					    # themselves.  We need to keep the comments to preserve line numbers while
 | 
				
			||||||
 | 
					    # avoiding empty lines which could potentially trigger formatting-related
 | 
				
			||||||
 | 
					    # checks.
 | 
				
			||||||
 | 
					    cleaned_test = re.sub('// *CHECK-[A-Z0-9\-]*:[^\r\n]*', '//', input_text)
 | 
				
			||||||
 | 
					    write_file(temp_file_name, cleaned_test)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    original_file_name = temp_file_name + ".orig"
 | 
				
			||||||
 | 
					    write_file(original_file_name, cleaned_test)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        builtin_include_dir = subprocess.check_output(
 | 
				
			||||||
 | 
					            ['clang', '-print-file-name=include'], stderr=subprocess.STDOUT)
 | 
				
			||||||
 | 
					    except subprocess.CalledProcessError as e:
 | 
				
			||||||
 | 
					        print('Cannot print Clang include directory: ' + e.output.decode())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    builtin_include_dir = os.path.normpath(builtin_include_dir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    args = (['clang', '-cc1', '-internal-isystem', builtin_include_dir,
 | 
				
			||||||
 | 
					             '-nostdsysteminc', '-analyze', '-analyzer-constraints=range',
 | 
				
			||||||
 | 
					             '-analyzer-config', 'apply-fixits=true']
 | 
				
			||||||
 | 
					            + clang_analyzer_extra_args + ['-verify', temp_file_name])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    print('Running ' + str(args) + '...')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        clang_analyzer_output = \
 | 
				
			||||||
 | 
					            subprocess.check_output(args, stderr=subprocess.STDOUT).decode()
 | 
				
			||||||
 | 
					    except subprocess.CalledProcessError as e:
 | 
				
			||||||
 | 
					        print('Clang Static Analyzer test failed:\n' + e.output.decode())
 | 
				
			||||||
 | 
					        raise
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    print('----------------- Clang Static Analyzer output -----------------\n' +
 | 
				
			||||||
 | 
					          clang_analyzer_output +
 | 
				
			||||||
 | 
					          '\n--------------------------------------------------------------')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        diff_output = subprocess.check_output(
 | 
				
			||||||
 | 
					            ['diff', '-u', original_file_name, temp_file_name],
 | 
				
			||||||
 | 
					            stderr=subprocess.STDOUT)
 | 
				
			||||||
 | 
					    except subprocess.CalledProcessError as e:
 | 
				
			||||||
 | 
					        diff_output = e.output
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    print('----------------------------- Fixes ----------------------------\n' +
 | 
				
			||||||
 | 
					          diff_output.decode() +
 | 
				
			||||||
 | 
					          '\n--------------------------------------------------------------')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        subprocess.check_output(
 | 
				
			||||||
 | 
					            ['FileCheck', '-input-file=' + temp_file_name, input_file_name,
 | 
				
			||||||
 | 
					             '-check-prefixes=CHECK-FIXES', '-strict-whitespace'],
 | 
				
			||||||
 | 
					            stderr=subprocess.STDOUT)
 | 
				
			||||||
 | 
					    except subprocess.CalledProcessError as e:
 | 
				
			||||||
 | 
					        print('FileCheck failed:\n' + e.output.decode())
 | 
				
			||||||
 | 
					        raise
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def main():
 | 
				
			||||||
 | 
					    parser = argparse.ArgumentParser()
 | 
				
			||||||
 | 
					    parser.add_argument('input_file_name')
 | 
				
			||||||
 | 
					    parser.add_argument('temp_file_name')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    args, extra_args = parser.parse_known_args()
 | 
				
			||||||
 | 
					    run_test_once(args, extra_args)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == '__main__':
 | 
				
			||||||
 | 
					    main()
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,13 @@
 | 
				
			||||||
 | 
					// RUN: %check_analyzer_fixit %s %t \
 | 
				
			||||||
 | 
					// RUN:   -analyzer-checker=core,optin.cplusplus.VirtualCall \
 | 
				
			||||||
 | 
					// RUN:   -analyzer-config optin.cplusplus.VirtualCall:ShowFixIts=true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct S {
 | 
				
			||||||
 | 
					  virtual void foo();
 | 
				
			||||||
 | 
					  S() {
 | 
				
			||||||
 | 
					    foo();
 | 
				
			||||||
 | 
					    // expected-warning@-1 {{Call to virtual method 'S::foo' during construction bypasses virtual dispatch}}
 | 
				
			||||||
 | 
					    // CHECK-FIXES: S::foo();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  ~S();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -77,6 +77,11 @@ if config.clang_staticanalyzer:
 | 
				
			||||||
    if config.clang_staticanalyzer_z3 == '1':
 | 
					    if config.clang_staticanalyzer_z3 == '1':
 | 
				
			||||||
        config.available_features.add('z3')
 | 
					        config.available_features.add('z3')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    check_analyzer_fixit_path = os.path.join(
 | 
				
			||||||
 | 
					        config.test_source_root, "Analysis", "check-analyzer-fixit.py")
 | 
				
			||||||
 | 
					    config.substitutions.append(
 | 
				
			||||||
 | 
					        ('%check_analyzer_fixit',
 | 
				
			||||||
 | 
					         '%s %s' % (config.python_executable, check_analyzer_fixit_path)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
llvm_config.add_tool_substitutions(tools, tool_dirs)
 | 
					llvm_config.add_tool_substitutions(tools, tool_dirs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue