[clang] rewrite plugin attachment logic in OCaml

Summary:
This changes executions of the former InferClang into a function call. In
particular, it can be called several times per execution.

The new InferClang must be called as if it was clang, and knows how to run
clang with our plugin to get the AST of the source file.

Reviewed By: akotulski

Differential Revision: D3981017

fbshipit-source-id: 7af6490
master
Jules Villard 8 years ago committed by Facebook Github Bot
parent caa7534a71
commit c53bfe3159

@ -1,157 +0,0 @@
#!/bin/bash
# Copyright (c) 2016 - present Facebook, Inc.
# All rights reserved.
#
# This source code is licensed under the BSD style license found in the
# LICENSE file in the root directory of this source tree. An additional grant
# of patent rights can be found in the PATENTS file in the same directory.
# Given the arguments a `clang -cc1 ...` command, attaches a plugin to
# the clang command, then run our own clang with all the arguments
# (passing through filter_args_and_run_fcp_clang.sh) and pipe the
# output to InferClang.
#### Configuration ####
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
BIN_DIR="${SCRIPT_DIR}/../../bin"
ETC_DIR="${SCRIPT_DIR}/../../etc"
# path to the wrapped clang compiler to invoke
CLANG_COMPILER="${SCRIPT_DIR}/filter_args_and_run_fcp_clang"
# extension of the file containing the clang cmd intercepted
CMD_FILE_EXT=".sh"
# path of the plugin to load in clang
PLUGIN_PATH="${SCRIPT_DIR}/../../../facebook-clang-plugins/libtooling/build/FacebookClangPlugin.dylib"
# name of the plugin to use
PLUGIN_NAME="BiniouASTExporter"
# output directory of the plugin
RESULTS_DIR="$FCP_RESULTS_DIR"
# this skips the creation of .o files
SYNTAX_ONLY="$FCP_RUN_SYNTAX_ONLY"
# extra arguments to pass during the execution of the infer frontend
INFER_FRONTEND_ARGS=($FCP_INFER_FRONTEND_ARGS)
# this fails the execution of clang if the frontend fails
REPORT_FRONTEND_FAILURE="$FCP_REPORT_FRONTEND_FAILURE"
# enable debug mode (to get more data saved to disk for future inspections)
DEBUG_MODE="$FCP_DEBUG_MODE"
# specify where is located Apple's clang
APPLE_CLANG="$FCP_APPLE_CLANG"
# whether to amend include search path with C++ model headers
INFER_CXX_MODELS="$FCP_INFER_CXX_MODELS"
# invariants that this script expects
if [ -z "$RESULTS_DIR" ]; then
echo '$FCP_RESULTS_DIR with the name of the output directory not provided.' 1>&2
exit 1
fi
if [ "$1" != "-cc1" ]; then
echo "$0 expects to be run with -cc1" 1>&2
exit 1
fi
# we know the first argument is "-cc1"
shift
# Functions
function get_option_argument {
# retrieves the value passed to an argument of a clang command
OPT="$1"
shift
while [ -n "$1" ] && [ "$1" != "$OPT" ]; do shift; done
echo "$2"
}
function has_flag {
# return if the given flag is part of the given command or not
local FLAG="$1"
shift
while [ -n "$1" ] && [ "$1" != "$FLAG" ]; do shift; done
[ -n "$1" ];
}
# Main
INPUT_ARGUMENTS=("$@")
# -cc1 has to be the first argument or clang will think it runs in driver mode
CLANG_CMD=("${CLANG_COMPILER}${XX}" "-cc1")
# It's important to place this option before other -isystem options.
if [ -n "$INFER_CXX_MODELS" ]; then
CLANG_CMD+=("-isystem" "${SCRIPT_DIR}/../../models/cpp/include")
fi
# (t7400979) this is a workaround to avoid that clang crashes when the -fmodules flag
# and the YojsonASTExporter plugin are used. Since the -plugin argument disables
# the generation of .o files, we invoke apple clang again to generate the expected
# artifacts. This will keep xcodebuild plus all the sub-steps happy.
if [ -n "$APPLE_CLANG" ]; then
ADD_PLUGIN_FLAG="-plugin"
else
ADD_PLUGIN_FLAG="-add-plugin"
fi
CLANG_CMD+=(
"-load"
"${PLUGIN_PATH}"
"$ADD_PLUGIN_FLAG"
"${PLUGIN_NAME}"
"-plugin-arg-${PLUGIN_NAME}"
"-"
"-plugin-arg-${PLUGIN_NAME}"
"PREPEND_CURRENT_DIR=1")
# add the remaining arguments
CLANG_CMD+=("$@")
# the source file is at the end of the command, match it with the wanted extensions
SOURCE_FILENAME="${INPUT_ARGUMENTS[${#INPUT_ARGUMENTS[@]} - 1]}"
if ! [[ "$SOURCE_FILENAME" = /* ]]; then
SOURCE_FILENAME="$(pwd)/$SOURCE_FILENAME"
fi
# add fsyntax-only to the end of arg list to override previous options
if [ -n "$SYNTAX_ONLY" ]; then
CLANG_CMD+=("-fsyntax-only")
fi
LANGUAGE=$(get_option_argument "-x" "${INPUT_ARGUMENTS[@]}")
if [ -n "$LANGUAGE" ]; then INFER_FRONTEND_ARGS+=("-x" "$LANGUAGE"); fi
if has_flag "-fobjc-arc" "${INPUT_ARGUMENTS[@]}"; then
INFER_FRONTEND_ARGS+=("-fobjc-arc");
fi
INFER_FRONTEND_CMD=(
"${BIN_DIR}/InferClang"
"-c" "$SOURCE_FILENAME"
"-results_dir" "$RESULTS_DIR"
"${INFER_FRONTEND_ARGS[@]}")
if [ -n "$DEBUG_MODE" ]; then
OBJECT_FILENAME="$(get_option_argument "-o" "${INPUT_ARGUMENTS[@]}")"
# Emit the clang command with the extra args piped to InferClang
echo "${CLANG_CMD[@]} " \
"| tee ${OBJECT_FILENAME}.biniou " \
"| ${INFER_FRONTEND_CMD[@]}" \
> "${OBJECT_FILENAME}${CMD_FILE_EXT}"
echo "bdump -x -d ${ETC_DIR}/clang_ast.dict -w '!!DUMMY!!' ${OBJECT_FILENAME}.biniou " \
"> ${OBJECT_FILENAME}.bdump" \
>> "${OBJECT_FILENAME}${CMD_FILE_EXT}"
fi
# run clang and pipe its output to InferClang, or flush it in case the latter crashes
"${CLANG_CMD[@]}" | \
("${INFER_FRONTEND_CMD[@]}" || \
{ EC=$?; cat > /dev/null; exit $EC; }) 2>&1
STATUSES=("${PIPESTATUS[@]}")
STATUS="${STATUSES[0]}"
INFER_STATUS="${STATUSES[1]}"
# if clang fails, then fail, otherwise, fail with the frontend's exitcode if required
if [ "$STATUS" == 0 ] && [ -n "$REPORT_FRONTEND_FAILURE" ]; then
STATUS="$INFER_STATUS"
fi
exit $STATUS

@ -129,26 +129,5 @@ def get_clang_frontend_envvars(args):
frontend_args = []
env_vars['INFER_RESULTS_DIR'] = args.infer_out
if args.headers:
frontend_args.append('-headers')
if args.models_mode:
frontend_args.append('-models_mode')
if args.project_root:
frontend_args += ['-project_root', args.project_root]
if args.testing_mode:
frontend_args.append('-testing_mode')
if args.cxx:
frontend_args.append('-cxx-experimental')
env_vars['FCP_INFER_CXX_MODELS'] = '1'
if args.frontend_debug:
frontend_args += ['-debug']
env_vars['FCP_DEBUG_MODE'] = '1'
if args.frontend_stats:
frontend_args += ['-stats']
env_vars['FCP_DEBUG_MODE'] = '1'
if args.no_failures_allowed:
env_vars['FCP_REPORT_FRONTEND_FAILURE'] = '1'
# export an env variable with all the arguments to pass to InferClang
env_vars['FCP_INFER_FRONTEND_ARGS'] = ' '.join(frontend_args)
return env_vars

@ -13,7 +13,7 @@ fi
if [ "${0%++}" != "$0" ]; then INFER_XX="++"; fi
export INFER_XX
FRONTEND_COMMAND=("$SCRIPT_DIR/../clang_wrappers/InferClangWrapper" "$@")
FRONTEND_COMMAND=("$SCRIPT_DIR/../../bin/InferClang" "$@")
HOST_COMPILER_COMMAND=("$SCRIPT_DIR/../clang_wrappers/filter_args_and_run_fcp_clang$INFER_XX" "$@")
if [ -n "$INFER_COMPILER_WRAPPER_IN_RECURSION" ]; then
@ -22,7 +22,6 @@ if [ -n "$INFER_COMPILER_WRAPPER_IN_RECURSION" ]; then
fi
else
export INFER_COMPILER_WRAPPER_IN_RECURSION="Y"
export FCP_RESULTS_DIR="$INFER_RESULTS_DIR";
"${FRONTEND_COMMAND[@]}"
fi

@ -6,8 +6,7 @@ CLANG_WRAPPERS_PATH="${SCRIPT_PATH}/../clang_wrappers"
if [ "${0%++}" != "$0" ]; then INFER_XX="++"; fi
export INFER_XX
export FCP_CLANG_COMPILER="${CLANG_WRAPPERS_PATH%/}/filter_args_and_run_fcp_clang$INFER_XX";
export FCP_RESULTS_DIR="${INFER_RESULTS_DIR}";
FCP_CLANG_COMPILER="${CLANG_WRAPPERS_PATH%/}/filter_args_and_run_fcp_clang$INFER_XX";
if [ -z "$INFER_RESULTS_DIR" ]; then
# this redirects to the compiler without adding any FCP flag
@ -17,4 +16,4 @@ if [ -z "$INFER_RESULTS_DIR" ]; then
exit $?
fi
"${CLANG_WRAPPERS_PATH%/}/InferClangWrapper" "$@" -fno-cxx-modules
"${CLANG_WRAPPERS_PATH%/}/InferClang" "$@" -fno-cxx-modules

@ -89,7 +89,7 @@ INFERJAVA_MAIN = $(JAVA_SOURCES)/jMain
#### Clang declarations ####
CLANG_SOURCES = clang
INFERCLANG_MAIN = $(CLANG_SOURCES)/cMain
INFERCLANG_MAIN = $(CLANG_SOURCES)/InferClang
FCP_CLANG_OCAML_BUILD_DIR = $(FCP_CLANG_OCAML_DIR)/build
@ -118,9 +118,6 @@ SCRIPT_SOURCES = scripts
CHECKCOPYRIGHT_BIN = $(SCRIPT_DIR)/checkCopyright
CHECKCOPYRIGHT_MAIN = $(SCRIPT_SOURCES)/checkCopyright
CLANGWRAPPER_BIN = $(LIB_DIR)/clang_wrappers/InferClangWrapper
CLANGWRAPPER_MAIN = $(SCRIPT_SOURCES)/InferClangWrapper
STATSAGGREGATOR_BIN = $(BIN_DIR)/InferStatsAggregator
STATSAGGREGATOR_MAIN = $(SCRIPT_SOURCES)/StatsAggregator
@ -165,7 +162,7 @@ INFER_CONFIG_TARGETS += $(INFERJAVA_MAIN).native
DEPENDENCIES += java
endif
ifeq ($(BUILD_C_ANALYZERS),yes)
INFER_CONFIG_TARGETS += $(INFERCLANG_MAIN).native $(CLANGWRAPPER_MAIN).native
INFER_CONFIG_TARGETS += $(INFERCLANG_MAIN).native
INFER_CONFIG_TARGETS += $(BUCK_COMPILATION_DATABASE_MAIN).native
DEPENDENCIES += clang
endif
@ -187,7 +184,6 @@ ifeq ($(BUILD_JAVA_ANALYZERS),yes)
endif
ifeq ($(BUILD_C_ANALYZERS),yes)
$(COPY) $(INFER_BUILD_DIR)/$(INFERCLANG_MAIN).native $(INFERCLANG_BIN)
$(COPY) $(INFER_BUILD_DIR)/$(CLANGWRAPPER_MAIN).native $(CLANGWRAPPER_BIN)
$(COPY) $(INFER_BUILD_DIR)/$(BUCK_COMPILATION_DATABASE_MAIN).native $(INFER_BUCK_COMPILATION_DATABASE_BIN)
endif
ifeq ($(ENABLE_OCAML_ANNOT),yes)
@ -218,7 +214,7 @@ test_build: init $(STACKTREE_ATDGEN_STUBS) $(INFERPRINT_ATDGEN_STUBS) $(CLANG_AT
-cflags -warn-error,$(OCAML_FATAL_WARNINGS) \
$(INFER_ALL_TARGETS:.native=.byte)
roots:=Infer InferAnalyze CMain JMain InferPrint BuckCompilationDatabase
roots:=Infer InferAnalyze InferClang CMain JMain InferPrint BuckCompilationDatabase
clusters:=base clang java IR
src_dirs:=$(shell find * -type d)

@ -16,12 +16,6 @@ let set_env_for_clang_wrapper () =
| Some dir -> Unix.putenv "FCP_CLANG_INCLUDE_TO_REPLACE" dir
| None -> ()
);
if Config.cxx_experimental then
Unix.putenv "FCP_INFER_CXX_MODELS" "1" ;
if Config.debug_mode || Config.frontend_stats then
Unix.putenv "FCP_DEBUG_MODE" "1" ;
if not Config.failures_allowed then
Unix.putenv "FCP_REPORT_FRONTEND_FAILURE" "1" ;
()
(** as the Config.fail_on_bug flag mandates, exit with error when an issue is reported *)

@ -17,15 +17,14 @@ module YBU = Yojson.Basic.Util
(** Each command line option may appear in the --help list of any executable, these tags are used to
specify which executables for which an option will be documented. *)
type exe = Analyze | BuckCompilationDatabase | Clang | ClangWrapper | Interactive | Java | Print
| StatsAggregator | Toplevel
type exe = Analyze | BuckCompilationDatabase | Clang | Interactive | Java | Print | StatsAggregator
| Toplevel
let exes = [
("InferBuckCompilationDatabase", BuckCompilationDatabase);
("InferAnalyze", Analyze);
("InferClang", Clang);
("InferClangWrapper", ClangWrapper);
("InferJava", Java);
("InferPrint", Print);
("InferStatsAggregator", StatsAggregator);
@ -234,6 +233,11 @@ let mk ?(deprecated=[]) ?(exes=[])
) deprecated ;
variable
(* arguments passed to Arg.parse_argv_dynamic, susceptible to be modified on the fly when parsing *)
let args_to_parse : string array ref = ref (Array.of_list [])
(* reference used by Arg.parse_argv_dynamic to track the index of the argument being parsed *)
let arg_being_parsed : int ref = ref 0
type 'a t =
?deprecated:string list -> long:Arg.key -> ?short:Arg.key ->
?exes:exe list -> ?meta:string -> Arg.doc ->
@ -332,6 +336,23 @@ let mk_string ~default ?(f=fun s -> s) ?(deprecated=[]) ~long ?short ?exes ?(met
~decode_json:(string_json_decoder ~long)
~mk_spec:(fun set -> Arg.String set)
let mk_path ~default ?(deprecated=[]) ~long ?short ?exes ?(meta="") doc =
mk ~deprecated ~long ?short ~default ?exes ~meta doc
~default_to_string:(fun s -> s)
~mk_setter:(fun var str ->
if Filename.is_relative str then (
(* Replace relative paths with absolute ones on the fly in the args being parsed. This
assumes that [!arg_being_parsed] points at the option name position in
[!args_to_parse], as is the case e.g. when calling [Arg.parse_argv_dynamic
~current:arg_being_parsed !args_to_parse ...]. *)
let abs_path = Sys.getcwd () // str in
var := abs_path;
(!args_to_parse).(!arg_being_parsed + 1) <- abs_path;
) else
var := str)
~decode_json:(string_json_decoder ~long)
~mk_spec:(fun set -> Arg.String set)
let mk_string_opt ?default ?(f=fun s -> s) ?(deprecated=[]) ~long ?short ?exes ?(meta="") doc =
let default_to_string = function Some s -> s | None -> "" in
let f s = Some (f s) in
@ -562,9 +583,8 @@ let parse ?(incomplete=false) ?(accept_unknown=false) ?config_file env_var exe_u
(* end transitional support for INFERCLANG_ARGS *)
let exe_name = Sys.executable_name in
let should_parse_cl_args = match current_exe with
| ClangWrapper | Interactive -> false
| Analyze | BuckCompilationDatabase | Clang | Java | Print | StatsAggregator
| Toplevel -> true in
| Clang | Interactive -> false
| Analyze | BuckCompilationDatabase | Java | Print | StatsAggregator | Toplevel -> true in
let env_cl_args =
if should_parse_cl_args then prepend_to_argv env_args
else env_args in
@ -574,22 +594,26 @@ let parse ?(incomplete=false) ?(accept_unknown=false) ?config_file env_var exe_u
let json_args = decode_inferconfig_to_argv path in
(* read .inferconfig first, as both env vars and command-line options overwrite it *)
json_args @ env_cl_args in
let argv = Array.of_list (exe_name :: all_args) in
let current = ref 0 in
args_to_parse := Array.of_list (exe_name :: all_args);
arg_being_parsed := 0;
(* tests if msg indicates an unknown option, as opposed to a known option with bad argument *)
let is_unknown msg =
let prefix = exe_name ^ ": unknown option" in
prefix = (String.sub msg 0 (String.length prefix)) in
let rec parse_loop () =
try
Arg.parse_argv_dynamic ~current argv curr_speclist !anon_fun usage_msg
Arg.parse_argv_dynamic ~current:arg_being_parsed !args_to_parse curr_speclist !anon_fun
usage_msg
with
| Arg.Bad _ when incomplete -> parse_loop ()
| Arg.Bad msg when accept_unknown && is_unknown msg -> !anon_fun argv.(!current) ; parse_loop ()
| Arg.Bad msg when accept_unknown && is_unknown msg ->
!anon_fun !args_to_parse.(!arg_being_parsed);
parse_loop ()
| Arg.Bad usage_msg -> Pervasives.prerr_string usage_msg; exit 2
| Arg.Help usage_msg -> Pervasives.print_string usage_msg; exit 0
in
parse_loop ();
if not incomplete then
Unix.putenv env_var (encode_argv_to_env (prefix_before_rest all_args)) ;
Unix.putenv env_var
(encode_argv_to_env (prefix_before_rest (IList.tl (Array.to_list !args_to_parse)))) ;
curr_usage

@ -11,8 +11,8 @@
open! Utils
type exe = Analyze | BuckCompilationDatabase | Clang | ClangWrapper | Interactive | Java | Print
| StatsAggregator | Toplevel
type exe = Analyze | BuckCompilationDatabase | Clang | Interactive | Java | Print | StatsAggregator
| Toplevel
val current_exe : exe
@ -66,6 +66,8 @@ val mk_float : default:float -> float ref t
val mk_string : default:string -> ?f:(string -> string) -> string ref t
val mk_path : default:string -> string ref t
val mk_string_opt : ?default:string -> ?f:(string -> string) -> string option ref t
(** [mk_string_list] defines a [string list ref], initialized to [[]] unless overridden by

@ -32,8 +32,6 @@ let clang_frontend_action_symbols = [
("lint_and_capture", `Lint_and_capture);
]
type clang_lang = C | CPP | OBJC | OBJCPP
type language = Clang | Java
let string_of_language = function
@ -245,6 +243,9 @@ let bin_dir =
let lib_dir =
bin_dir // Filename.parent_dir_name // "lib"
let etc_dir =
bin_dir // Filename.parent_dir_name // "etc"
(** Path to lib/specs to retrieve the default models *)
let models_dir =
lib_dir // specs_dir_name
@ -409,8 +410,8 @@ let inferconfig_home =
and project_root =
CLOpt.mk_string_opt ~deprecated:["project_root"; "-project_root"] ~long:"project-root" ~short:"pr"
?default:CLOpt.(match current_exe with Print | Toplevel | StatsAggregator ->
Some (Sys.getcwd ()) | _ -> None)
?default:CLOpt.(match current_exe with Print | Toplevel | StatsAggregator | Clang ->
Some init_work_dir | _ -> None)
~f:resolve
~exes:CLOpt.[Analyze;Clang;Java;Print;Toplevel]
~meta:"dir" "Specify the root directory of the project"
@ -629,6 +630,10 @@ and calls_csv =
~exes:CLOpt.[Print]
~meta:"file" "Write individual calls in csv format to a file"
and clang_biniou_file =
CLOpt.mk_string_opt ~long:"clang-biniou-file" ~exes:CLOpt.[Clang] ~meta:"file"
"Specify a file containing the AST of the program, in biniou format"
and changed_files_index =
CLOpt.mk_string_opt ~long:"changed-files-index" ~exes:CLOpt.[Toplevel] ~meta:"file"
"Specify the file containing the list of files from which reactive analysis should start"
@ -650,12 +655,6 @@ and clang_include_to_override =
location of internal compiler headers. This option should specify the path to those headers \
so that infer can use its own clang internal headers instead."
(* Default is objc, since it's the default for clang (at least in Mac OS) *)
and clang_lang =
CLOpt.mk_symbol ~long:"clang-lang" ~short:"x" ~default:OBJC
"Specify language for clang frontend"
~symbols:[("c", C); ("objective-c", OBJC); ("c++", CPP); ("objective-c++", OBJCPP)]
and _ =
CLOpt.mk_string_opt ~deprecated:["classpath"] ~long:"classpath"
~meta:"path" "Specify where to find user class files and annotation processors"
@ -970,7 +969,7 @@ and reports_include_ml_loc =
"Include the location in the Infer source code from where reports are generated"
and results_dir =
CLOpt.mk_string ~deprecated:["results_dir"; "-out"] ~long:"results-dir" ~short:"o"
CLOpt.mk_path ~deprecated:["results_dir"; "-out"] ~long:"results-dir" ~short:"o"
~default:(init_work_dir // "infer-out")
~exes:CLOpt.[Analyze;Clang;Java;Print;StatsAggregator]
~meta:"dir" "Write results and internal files in the specified directory"
@ -998,12 +997,6 @@ and skip_translation_headers =
~exes:CLOpt.[Clang]
~meta:"path prefix" "Ignore headers whose path matches the given prefix"
(** File to translate *)
and source_file =
(* clang-plugin normalizes filenames *)
CLOpt.mk_string_opt ~long:"source-file" ~short:"c" ~f:filename_to_absolute
~meta:"file" ""
and source_file_copy =
CLOpt.mk_string_opt ~deprecated:["source_file_copy"] ~long:"source-file-copy"
~meta:"source_file" "Print the path of the copy of source_file in the results directory"
@ -1229,9 +1222,6 @@ let exe_usage (exe : CLOpt.exe) =
reads the compilation database emited in json and runs the capture in parallel for \n\
those commands"
| Clang ->
"Usage: InferClang -c <c files> -ast <ast files> --results-dir <output-dir> [options] \n\
Translate the given files using clang into infer internal representation for later analysis."
| ClangWrapper ->
"Usage: internal script to capture compilation commands from clang and clang++. \n\
You shouldn't need to call this directly."
| Interactive ->
@ -1392,7 +1382,7 @@ and checkers = !checkers
(** should the checkers be run? *)
and checkers_enabled = not (!eradicate || !crashcontext || !quandary)
and clang_biniou_file = !clang_biniou_file
and clang_frontend_do_capture, clang_frontend_do_lint =
match !clang_frontend_action with
| Some `Lint -> false, true (* no capture, lint *)
@ -1405,7 +1395,6 @@ and clang_frontend_do_capture, clang_frontend_do_lint =
| _ -> true, true (* capture, lint *)
and clang_include_to_override = !clang_include_to_override
and clang_lang = !clang_lang
and cluster_cmdline = !cluster
and continue_capture = !continue
and copy_propagation = !copy_propagation
@ -1475,7 +1464,6 @@ and show_progress_bar = !progress_bar
and skip_analysis_in_path = !skip_analysis_in_path
and skip_clang_analysis_in_path = !skip_clang_analysis_in_path
and skip_translation_headers = !skip_translation_headers
and source_file = !source_file
and source_file_copy = !source_file_copy
and spec_abs_level = !spec_abs_level
and specs_library = !specs_library

@ -22,8 +22,6 @@ type analyzer = Capture | Compile | Infer | Eradicate | Checkers | Tracing
val string_to_analyzer : (string * analyzer) list
type clang_lang = C | CPP | OBJC | OBJCPP
type language = Clang | Java
val string_of_language : language -> string
@ -63,6 +61,7 @@ val anonymous_block_prefix : string
val assign : string
val attributes_dir_name : string
val backend_stats_dir_name : string
val bin_dir : string
val bound_error_allowed_in_procedure_call : bool
val buck_generated_folder : string
val buck_infer_deps_file_name : string
@ -75,6 +74,7 @@ val csl_analysis : bool
val default_failure_name : string
val default_in_zip_results_dir : string
val dotty_output : string
val etc_dir : string
val fail_on_issue_exit_code : int
val filter_buckets : bool
val frontend_stats_dir_name : string
@ -84,6 +84,7 @@ val incremental_procs : bool
val infer_py_argparse_error_exit_code : int
val initial_analysis_time : float
val ivar_attributes : string
val lib_dir : string
val lint_issues_dir_name : string
val load_average : float
val log_analysis_crash : string
@ -168,11 +169,11 @@ val calls_csv : outfile option
val check_duplicate_symbols : bool
val checkers : bool
val checkers_enabled : bool
val clang_biniou_file : string option
val clang_frontend_action_string : string
val clang_frontend_do_capture : bool
val clang_frontend_do_lint : bool
val clang_include_to_override : string option
val clang_lang : clang_lang
val cluster_cmdline : string option
val continue_capture : bool
val copy_propagation : bool
@ -241,7 +242,6 @@ val show_progress_bar : bool
val skip_analysis_in_path : string list
val skip_clang_analysis_in_path : string list
val skip_translation_headers : string list
val source_file : string option
val source_file_copy : string option
val spec_abs_level : int
val specs_library : string list

@ -21,7 +21,6 @@ let log_dir_of_current_exe =
| Analyze -> "analyze"
| BuckCompilationDatabase -> "buck_compilation_database"
| Clang -> "clang"
| ClangWrapper -> "clang_wrapper"
| Interactive -> "interactive"
| Java -> "java"
| Print -> "print"
@ -40,8 +39,7 @@ let set_log_file_identifier string_opt =
let should_setup_log_files =
match CommandLineOption.current_exe with
| Analyze
| Clang
| ClangWrapper -> Config.debug_mode || Config.stats_mode
| Clang -> Config.debug_mode || Config.stats_mode
| BuckCompilationDatabase -> true
| _ -> false in
if should_setup_log_files then (

@ -282,6 +282,8 @@ val read_optional_json_file : string -> (Yojson.Basic.json, string) result
val write_json_to_file : string -> Yojson.Basic.json -> unit
val consume_in : in_channel -> unit
val with_process_in: string -> (in_channel -> 'a) -> ('a * Unix.process_status)
val with_process_full: string -> (in_channel -> 'a) -> (in_channel -> 'b) ->

@ -0,0 +1,180 @@
/*
* Copyright (c) 2016 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
open! Utils;
/** this fails the execution of clang if the frontend fails */
let report_frontend_failure = not Config.failures_allowed;
/** enable debug mode (to get more data saved to disk for future inspections) */
let debug_mode = Config.debug_mode || Config.frontend_stats;
let buffer_len = 262143;
let catch_biniou_buffer_errors f x =>
try (f x) {
| Invalid_argument "Bi_inbuf.refill_from_channel" =>
Logging.err "WARNING: biniou buffer too short, skipping the file@\n";
assert false
};
/* This function reads the json file in fname, validates it, and encoded in the AST data structure
defined in Clang_ast_t. */
let validate_decl_from_file fname =>
catch_biniou_buffer_errors (Ag_util.Biniou.from_file len::buffer_len Clang_ast_b.read_decl) fname;
let validate_decl_from_channel chan =>
catch_biniou_buffer_errors
(Ag_util.Biniou.from_channel len::buffer_len Clang_ast_b.read_decl) chan;
let register_perf_stats_report source_file => {
let stats_dir = Filename.concat Config.results_dir Config.frontend_stats_dir_name;
let abbrev_source_file = DB.source_file_encoding source_file;
let stats_file = Config.perf_stats_prefix ^ "_" ^ abbrev_source_file ^ ".json";
create_dir Config.results_dir;
create_dir stats_dir;
PerfStats.register_report_at_exit (Filename.concat stats_dir stats_file)
};
let init_global_state_for_capture_and_linters source_file => {
Logging.set_log_file_identifier (Some (Filename.basename (DB.source_file_to_string source_file)));
register_perf_stats_report source_file;
Config.curr_language := Config.Clang;
DB.Results_dir.init source_file;
Clang_ast_main.reset_cache ();
CFrontend_config.reset_global_state ()
};
let run_clang_frontend trans_unit_ctx ast_source => {
let init_time = Unix.gettimeofday ();
let print_elapsed () => {
let elapsed = Unix.gettimeofday () -. init_time;
Logging.out "Elapsed: %07.3f seconds.@\n" elapsed
};
let (ast_filename, ast_decl) =
switch ast_source {
| `File path => (path, validate_decl_from_file path)
| `Pipe chan => (
"stdin of " ^ DB.source_file_to_string trans_unit_ctx.CFrontend_config.source_file,
validate_decl_from_channel chan
)
};
let (decl_index, stmt_index, type_index, ivar_to_property_index) = Clang_ast_main.index_node_pointers ast_decl;
CFrontend_config.pointer_decl_index := decl_index;
CFrontend_config.pointer_stmt_index := stmt_index;
CFrontend_config.pointer_type_index := type_index;
CFrontend_config.ivar_to_property_index := ivar_to_property_index;
CFrontend_config.json := ast_filename;
Logging.out "Clang frontend action is %s@\n" Config.clang_frontend_action_string;
Logging.out
"Start %s of AST from %s@\n" Config.clang_frontend_action_string !CFrontend_config.json;
if Config.clang_frontend_do_lint {
CFrontend_checkers_main.do_frontend_checks trans_unit_ctx ast_decl
};
if Config.clang_frontend_do_capture {
CFrontend.do_source_file trans_unit_ctx ast_decl
};
Logging.out "End translation AST file %s... OK!@\n" !CFrontend_config.json;
print_elapsed ()
};
let run_clang clang_command read =>
switch (with_process_in clang_command read) {
| (res, Unix.WEXITED 0) => res
| (_, Unix.WEXITED n) =>
/* exit with the same error code as clang in case of compilation failure */
exit n
| _ => exit 1
};
let run_plugin_and_frontend frontend clang_args => {
let clang_command = ClangCommand.command_to_run (ClangCommand.with_plugin_args clang_args);
if debug_mode {
/* -cc1 clang commands always set -o explicitly */
let object_filename = Option.get (ClangCommand.value_of_option clang_args "-o");
/* Emit the clang command with the extra args piped to InferClang */
let frontend_script_fname = Printf.sprintf "%s.sh" object_filename;
let debug_script_out = open_out frontend_script_fname;
let debug_script_fmt = Format.formatter_of_out_channel debug_script_out;
let biniou_fname = Printf.sprintf "%s.biniou" object_filename;
Format.fprintf debug_script_fmt "%s \\@\n > %s@\n" clang_command biniou_fname;
let infer_clang_options =
String.concat
"^"
(
(
try [Unix.getenv "INFER_ARGS"] {
| Not_found => []
}
)
@ [
"--clang-biniou-file",
biniou_fname
]
);
Format.fprintf
debug_script_fmt
"INFER_ARGS=\"%s\" %s@\n"
infer_clang_options
(ClangCommand.with_exec Sys.executable_name clang_args |> ClangCommand.command_to_run);
Format.fprintf
debug_script_fmt
"bdump -x -d \"%s/clang_ast.dict\" -w '!!DUMMY!!' %s \\@\n > %s.bdump"
Config.etc_dir
biniou_fname
object_filename;
close_out debug_script_out
};
run_clang clang_command frontend
};
let capture clang_args => {
let source_path = {
let argv = ClangCommand.get_argv clang_args;
/* the source file is always the last argument -cc1 clang commands */
filename_to_absolute argv.(Array.length argv - 1)
};
Logging.out "@\n*** Beginning capture of file %s ***@\n" source_path;
if (CLocation.is_file_blacklisted source_path) {
Logging.out "@\n Skip the analysis of source file %s@\n@\n" source_path;
/* We still need to run clang, but we don't have to attach the plugin. */
run_clang (ClangCommand.command_to_run clang_args) consume_in
} else {
let source_file = CLocation.source_file_from_path source_path;
init_global_state_for_capture_and_linters source_file;
let trans_unit_ctx = {
let clang_langs =
CFrontend_config.[("c", C), ("objective-c", ObjC), ("c++", CPP), ("objective-c++", ObjCPP)];
let lang =
switch (ClangCommand.value_of_option clang_args "-x") {
| Some lang_opt when IList.mem_assoc string_equal lang_opt clang_langs =>
IList.assoc string_equal lang_opt clang_langs
| _ => assert false
};
{CFrontend_config.source_file: source_file, lang}
};
Config.arc_mode := ClangCommand.has_flag clang_args "-fobjc-arc";
try (
switch Config.clang_biniou_file {
| Some fname => run_clang_frontend trans_unit_ctx (`File fname)
| None =>
run_plugin_and_frontend
(fun chan_in => run_clang_frontend trans_unit_ctx (`Pipe chan_in)) clang_args
}
) {
| exc =>
if report_frontend_failure {
raise exc
}
};
()
}
};

@ -0,0 +1,10 @@
/*
* Copyright (c) 2016 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
let capture: ClangCommand.args => unit;

@ -0,0 +1,153 @@
/*
* Copyright (c) 2016 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
open! Utils;
type quoting_style = | DoubleQuotes | SingleQuotes;
type args = {exec: string, argv: list string, quoting_style: quoting_style};
type t = | Assembly of args | CC1 of args | ClangError of string | NonCCCommand of args;
/** path of the plugin to load in clang */
let plugin_path =
Config.bin_dir /\/
Filename.parent_dir_name /\/
Filename.parent_dir_name /\/
"facebook-clang-plugins" /\/
"libtooling" /\/
"build" /\/
"FacebookClangPlugin.dylib";
let test_env_var var =>
switch (Sys.getenv var) {
| "1" => true
| _ => false
| exception Not_found => false
};
/** name of the plugin to use */
let plugin_name = "BiniouASTExporter";
/** this skips the creation of .o files */
let syntax_only = test_env_var "FCP_RUN_SYNTAX_ONLY";
/** specify where is located Apple's clang */
let apple_clang = test_env_var "FCP_APPLE_CLANG";
/** whether to amend include search path with C++ model headers */
let infer_cxx_models = Config.cxx_experimental;
let value_of_argv_option argv opt_name =>
IList.fold_left
(
fun (prev_arg, result) arg => {
let result' =
if (Option.is_some result) {
result
} else if (string_equal opt_name prev_arg) {
Some arg
} else {
None
};
(arg, result')
}
)
("", None)
argv |> snd;
let value_of_option {argv} => value_of_argv_option argv;
let has_flag {argv} flag => IList.exists (string_equal flag) argv;
let mk quoting_style argv => {
let argv_list = Array.to_list argv;
let is_assembly =
/* whether language is set to "assembler" or "assembler-with-cpp" */
{
let assembly_language =
switch (value_of_argv_option argv_list "-x") {
| Some lang => string_is_prefix "assembler" lang
| _ => false
};
/* Detect -cc1as or assembly language commands. -cc1as is always the first argument if
present. */
string_equal argv.(1) "-cc1as" || assembly_language
};
let args = {exec: argv.(0), argv: IList.tl argv_list, quoting_style};
if is_assembly {
Assembly args
} else if (argv.(1) == "-cc1") {
CC1
/* -cc1 is always the first argument if present. */
args
} else {
NonCCCommand args
}
};
let command_to_run {exec, argv, quoting_style} => {
let quote =
switch quoting_style {
| DoubleQuotes => (fun s => "\"" ^ s ^ "\"")
| SingleQuotes => (fun s => "'" ^ s ^ "'")
};
Printf.sprintf "'%s' %s" exec (IList.map quote argv |> String.concat " ")
};
let with_exec exec args => {...args, exec};
let with_plugin_args args => {
let cons a b => [a, ...b];
let do_if cond action x =>
if cond {
action x
} else {
x
};
let rev_args_before =
[] |>
/* -cc1 has to be the first argument or clang will think it runs in driver mode */
cons "-cc1" |>
/* It's important to place this option before other -isystem options. */
do_if infer_cxx_models (IList.rev_append ["-isystem", Config.cpp_models_dir]) |>
IList.rev_append [
"-load",
plugin_path,
/* (t7400979) this is a workaround to avoid that clang crashes when the -fmodules flag and the
YojsonASTExporter plugin are used. Since the -plugin argument disables the generation of .o
files, we invoke apple clang again to generate the expected artifacts. This will keep
xcodebuild plus all the sub-steps happy. */
if apple_clang {
"-plugin"
} else {
"-add-plugin"
},
plugin_name,
"-plugin-arg-" ^ plugin_name,
"-",
"-plugin-arg-" ^ plugin_name,
"PREPEND_CURRENT_DIR=1"
];
let args_after = [] |> do_if syntax_only (cons "-fsyntax-only");
{...args, argv: IList.rev_append rev_args_before (args.argv @ args_after)}
};
let prepend_arg arg clang_args => {...clang_args, argv: [arg, ...clang_args.argv]};
let prepend_args args clang_args => {...clang_args, argv: args @ clang_args.argv};
let append_args args clang_args => {...clang_args, argv: clang_args.argv @ args};
let get_argv {exec, argv} => Array.of_list [exec, ...argv];

@ -0,0 +1,55 @@
/*
* Copyright (c) 2016 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
type args;
type t =
| Assembly of args
/** a normalized clang command that runs the assembler */
| CC1 of args
/** a -cc1 clang command */
| ClangError of string
| NonCCCommand of args /** other commands (as, ld, ...) */;
type quoting_style =
| DoubleQuotes /** the arguments are ready to be enclosed in "double quotes" */
| SingleQuotes /** the arguments are ready to be enclodes in 'single quotes' */;
/** [mk qs argv] finds the type of command depending on its arguments [argv]. The quoting style of
the arguments have to be provided, so that the command may be run later on. */
let mk: quoting_style => array string => t;
/** change an args object into a string ready to be passed to a shell to be executed */
let command_to_run: args => string;
/** whether the command has this flag set in its arguments */
let has_flag: args => string => bool;
/** the value passed to an option in the arguments of a command */
let value_of_option: args => string => option string;
/** add the arguments needed to attach the facebook-clang-plugins plugin */
let with_plugin_args: args => args;
let prepend_arg: string => args => args;
let prepend_args: list string => args => args;
let append_args: list string => args => args;
let get_argv: args => array string;
/** updates the executable to be run */
let with_exec: string => args => args;

@ -12,74 +12,16 @@
open! Utils;
/** where we are */
let exec_dir = Filename.dirname @@ Sys.executable_name;
let xx_suffix =
try (Sys.getenv "INFER_XX") {
| Not_found => ""
};
/** script to run our own clang */
let infer_clang_bin = exec_dir /\/ ("filter_args_and_run_fcp_clang" ^ xx_suffix);
/** script to attach the plugin to clang -cc1 commands and run InferClang */
let clang_cc1_capture = exec_dir /\/ "attach_plugin_and_run_clang_frontend.sh";
/** path to Apple's clang */
let apple_clang =
try (Some (Sys.getenv "FCP_APPLE_CLANG" ^ xx_suffix)) {
| Not_found => None
};
let typed_clang_invocation args => {
let is_assembly =
/* whether language is set to "assembler" or "assembler-with-cpp" */
{
let assembly_language =
Array.fold_left
(
fun (prev_arg, b) arg => {
let b' = b || string_equal "-x" prev_arg && string_is_prefix "assembler" arg;
(arg, b')
}
)
("", false)
args |> snd;
/* Detect -cc1as or assembly language commands. -cc1as is always the first argument if
present. */
string_equal args.(1) "-cc1as" || assembly_language
};
if is_assembly {
`Assembly args
} else if (args.(1) == "-cc1") {
`CC1
/* -cc1 is always the first argument if present. */
args
} else {
`NonCCCommand args
}
};
let commands_or_errors =
/* commands generated by `clang -### ...` start with ' "/absolute/path/to/binary"' */
Str.regexp " \"/\\|clang\\(\\|++\\): error:";
/** Given a list of arguments for clang [args], return a list of new commands to run according to
the results of `clang -### [args]`. */
let normalize args =>
switch (typed_clang_invocation args) {
| `CC1 args =>
Logging.out "InferClangWrapper got toplevel -cc1 command@\n";
[`CC1 args]
| `NonCCCommand args =>
let clang_hashhashhash =
String.concat
" " (IList.map Filename.quote [infer_clang_bin, "-###", ...Array.to_list args |> IList.tl]);
the results of `clang -### [args]`. Assembly commands (eg, clang -cc1as ...) are filtered out,
although the type cannot reflect that fact. */
let normalize (args: array string) :list ClangCommand.t =>
switch (ClangCommand.mk ClangCommand.SingleQuotes args) {
| CC1 args =>
Logging.out "InferClang got toplevel -cc1 command@\n";
[ClangCommand.CC1 args]
| NonCCCommand args =>
let clang_hashhashhash = ClangCommand.prepend_arg "-###" args |> ClangCommand.command_to_run;
Logging.out "clang -### invocation: %s@\n" clang_hashhashhash;
let normalized_commands = ref [];
let one_line line =>
@ -89,10 +31,13 @@ let normalize args =>
/* split by whitespace */
Str.split (Str.regexp_string "\" \"") |>
Array.of_list |>
typed_clang_invocation
ClangCommand.mk ClangCommand.DoubleQuotes
} else {
`ClangError line
ClangCommand.ClangError line
};
let commands_or_errors =
/* commands generated by `clang -### ...` start with ' "/absolute/path/to/binary"' */
Str.regexp " \"/\\|clang\\(\\|++\\): error:";
let consume_input i =>
try (
while true {
@ -114,34 +59,37 @@ let normalize args =>
IList.filter
(
fun
| `Assembly asm_cmd => {
Logging.out
"Skipping assembly command %s@\n" (String.concat " " @@ Array.to_list asm_cmd);
| ClangCommand.Assembly asm_cmd => {
Logging.out "Skipping assembly command %s@\n" (ClangCommand.command_to_run asm_cmd);
false
}
| _ => true
)
!normalized_commands
| `Assembly _ =>
| Assembly _ =>
/* discard assembly commands -- see above */
Logging.out "InferClangWrapper got toplevel assembly command@\n";
Logging.out "InferClang got toplevel assembly command@\n";
[]
| ClangError _ =>
/* we cannot possibly get this from the command-line... */
assert false
};
let execute_clang_command clang_cmd =>
let execute_clang_command (clang_cmd: ClangCommand.t) =>
switch clang_cmd {
| `CC1 args =>
| CC1 args =>
/* this command compiles some code; replace the invocation of clang with our own clang and
plugin */
args.(0) = clang_cc1_capture;
Logging.out "Executing -cc1 command: %s@\n" (String.concat " " @@ Array.to_list args);
Process.create_process_and_wait args
| `ClangError error =>
/* reset logging, otherwise we might print into the logs of the previous file that was compiled */
Logging.set_log_file_identifier None;
Logging.out "Capturing -cc1 command: %s@\n" (ClangCommand.command_to_run args);
Capture.capture args
| ClangError error =>
/* An error in the output of `clang -### ...`. Outputs the error and fail. This is because
`clang -###` pretty much never fails, but warns of failures on stderr instead. */
Logging.err "%s" error;
exit 1
| `Assembly args =>
| Assembly args =>
/* We shouldn't get any assembly command at this point */
(
if Config.debug_mode {
@ -150,19 +98,23 @@ let execute_clang_command clang_cmd =>
Logging.err
}
)
"WARNING: unexpected assembly command: %s@\n" (String.concat " " @@ Array.to_list args)
| `NonCCCommand args =>
"WARNING: unexpected assembly command: %s@\n" (ClangCommand.command_to_run args)
| NonCCCommand args =>
/* Non-compilation (eg, linking) command. Run the command as-is. It will not get captured
further since `clang -### ...` will only output commands that invoke binaries using their
absolute paths. */
if Config.debug_mode {
Logging.out "Executing raw command: %s@\n" (String.concat " " @@ Array.to_list args)
};
Process.create_process_and_wait args
Logging.out "Executing raw command: %s@\n" (ClangCommand.command_to_run args);
Process.create_process_and_wait (ClangCommand.get_argv args)
};
let () = {
let args = Sys.argv;
let xx_suffix =
try (Sys.getenv "INFER_XX") {
| Not_found => ""
};
let args = Array.copy Sys.argv;
/* make sure we don't call ourselves recursively */
args.(0) = CFrontend_config.clang_bin xx_suffix;
let commands = normalize args;
if (commands == []) {
/* No command to execute after -###, let's execute the original command
@ -173,9 +125,8 @@ let () = {
- the user tries to run `infer -- clang -c file_that_does_not_exist.c`. In this case, this
will fail with the appropriate error message from clang instead of silently analyzing 0
files. */
args.(0) = infer_clang_bin;
Logging.out
"WARNING: `clang -### <args>` returned\n an empty set of commands to run and no error. Will run the original command directly:@\n\n %s@\n"
"WARNING: `clang -### <args>` returned an empty set of commands to run and no error. Will run the original command directly:@\n %s@\n"
(String.concat " " @@ Array.to_list args);
Process.create_process_and_wait args
} else {
@ -183,10 +134,10 @@ let () = {
};
/* xcodebuild projects may require the object files to be generated by the Apple compiler, eg to
generate precompiled headers compatible with Apple's clang. */
switch apple_clang {
| None => ()
| Some bin =>
args.(0) = bin;
switch (Sys.getenv "FCP_APPLE_CLANG") {
| bin =>
args.(0) = bin ^ xx_suffix;
Process.create_process_and_wait args
| exception Not_found => ()
}
};

@ -97,6 +97,6 @@ let do_frontend_checks trans_unit_ctx ast =
Logging.out "End linting file %s@\n" (DB.source_file_to_string source_file)
| _ -> assert false (* NOTE: Assumes that an AST always starts with a TranslationUnitDecl *)
with
| Assert_failure (file, line, column) ->
| Assert_failure (file, line, column) as exn ->
Logging.err "Fatal error: exception Assert_failure(%s, %d, %d)@\n%!" file line column;
exit 1
raise exn

@ -11,8 +11,10 @@ open! Utils
(** Module that contains constants and global state used in the frontend *)
type clang_lang = C | CPP | ObjC | ObjCPP
type translation_unit_context = {
lang : Config.clang_lang;
lang : clang_lang;
source_file : DB.source_file
}
@ -35,6 +37,9 @@ let cf_bridging_retain = "CFBridgingRetain"
let cf_non_null_alloc ="__cf_non_null_alloc"
let ckcomponent_cl = "CKComponent"
let ckcomponentcontroller_cl = "CKComponentController"
(** script to run our own clang *)
let clang_bin xx = Config.lib_dir // "clang_wrappers" // "filter_args_and_run_fcp_clang" ^ xx
let class_method = "class"
let class_type = "Class"
let copy = "copy"
@ -87,7 +92,19 @@ let enum_map = ref Clang_ast_main.PointerMap.empty
let global_translation_unit_decls : Clang_ast_t.decl list ref = ref []
let ivar_to_property_index = ref Clang_ast_main.PointerMap.empty
let json = ref ""
let log_out = ref Format.std_formatter
let pointer_decl_index = ref Clang_ast_main.PointerMap.empty
let pointer_stmt_index = ref Clang_ast_main.PointerMap.empty
let pointer_type_index = ref Clang_ast_main.PointerMap.empty
let sil_types_map = ref Clang_ast_types.TypePointerMap.empty
let reset_global_state () =
enum_map := Clang_ast_main.PointerMap.empty;
global_translation_unit_decls := [];
ivar_to_property_index := Clang_ast_main.PointerMap.empty;
json := "";
log_out := Format.std_formatter;
pointer_decl_index := Clang_ast_main.PointerMap.empty;
pointer_stmt_index := Clang_ast_main.PointerMap.empty;
pointer_type_index := Clang_ast_main.PointerMap.empty;
sil_types_map := Clang_ast_types.TypePointerMap.empty;

@ -11,8 +11,10 @@ open! Utils
(** Module that contains constants and global state used in the frontend *)
type clang_lang = C | CPP | ObjC | ObjCPP
type translation_unit_context = {
lang : Config.clang_lang;
lang : clang_lang;
source_file : DB.source_file
}
@ -35,6 +37,10 @@ val cf_bridging_retain : string
val cf_non_null_alloc : string
val ckcomponent_cl : string
val ckcomponentcontroller_cl : string
(** Script to run our own clang. The argument is expected to be either "" or "++". *)
val clang_bin : string -> string
val class_method : string
val class_type : string
val copy : string
@ -89,6 +95,7 @@ val enum_map : (Clang_ast_t.pointer option * Exp.t option) Clang_ast_main.Pointe
val global_translation_unit_decls : Clang_ast_t.decl list ref
val ivar_to_property_index : Clang_ast_t.decl Clang_ast_main.PointerMap.t ref
val json : string ref
val log_out : Format.formatter ref
val pointer_decl_index : Clang_ast_t.decl Clang_ast_main.PointerMap.t ref
val pointer_stmt_index : Clang_ast_t.stmt Clang_ast_main.PointerMap.t ref
@ -99,3 +106,5 @@ val pointer_type_index : Clang_ast_t.c_type Clang_ast_main.PointerMap.t ref
(** Map from type pointers (clang pointers and types created later by frontend) to sil types
Populated during frontend execution when new type is found *)
val sil_types_map : (Typ.t Clang_ast_types.TypePointerMap.t) ref
val reset_global_state : unit -> unit

@ -214,10 +214,9 @@ struct
let class_decl = Ast_utils.get_decl parent_ptr in
(match class_decl with
| Some (CXXRecordDecl _)
| Some (ClassTemplateSpecializationDecl _) ->
| Some (ClassTemplateSpecializationDecl _) when Config.cxx_experimental ->
let curr_class = CContext.ContextClsDeclPtr parent_ptr in
if Config.cxx_experimental then
process_methods trans_unit_ctx tenv cg cfg curr_class [dec]
process_methods trans_unit_ctx tenv cg cfg curr_class [dec]
| Some dec ->
Logging.out "Methods of %s skipped\n" (Ast_utils.string_of_decl dec)
| None -> ())

@ -639,11 +639,11 @@ struct
let is_cpp_translation translation_unit_context =
let lang = translation_unit_context.CFrontend_config.lang in
lang = Config.CPP || lang = Config.OBJCPP
lang = CFrontend_config.CPP || lang = CFrontend_config.ObjCPP
let is_objc_extension translation_unit_context =
let lang = translation_unit_context.CFrontend_config.lang in
lang = Config.OBJC || lang = Config.OBJCPP
lang = CFrontend_config.ObjC || lang = CFrontend_config.ObjCPP
let rec get_mangled_method_name function_decl_info method_decl_info =
(* For virtual methods return mangled name of the method from most base class

@ -243,10 +243,10 @@ sig
val mk_sil_var : Clang_ast_t.named_decl_info -> var_info option -> Procname.t -> Procname.t ->
Pvar.t
(** true if Config.clang_lang is C++ or ObjC++ *)
(** true if the current language is C++ or ObjC++ *)
val is_cpp_translation : CFrontend_config.translation_unit_context -> bool
(** true if Config.clang_lang is ObjC or ObjC++ *)
(** true if the current language is ObjC or ObjC++ *)
val is_objc_extension : CFrontend_config.translation_unit_context -> bool
end

@ -17,10 +17,9 @@ let curr_file = ref DB.source_file_empty
let source_file_from_path path =
if Filename.is_relative path then
(Logging.err_debug
(failwithf
"ERROR: Path %s is relative. Please pass an absolute path in the -c argument.@."
path;
exit 1);
path);
match Config.project_root with
| Some root ->
(try
@ -146,9 +145,3 @@ let get_sil_location stmt_info context =
let sloc = choose_sloc sloc1 sloc2 in
clang_to_sil_location context.CContext.translation_unit_context sloc
(Some (CContext.get_procdesc context))
let check_source_file source_file =
if is_file_blacklisted source_file then
(Logging.out "%s"
("\n Skip the analysis of source file" ^ source_file ^ "\n\n");
exit(0));

@ -29,7 +29,7 @@ val should_do_frontend_check : CFrontend_config.translation_unit_context ->
val update_curr_file : CFrontend_config.translation_unit_context -> Clang_ast_t.decl_info -> unit
val check_source_file : string -> unit
val is_file_blacklisted : string -> bool
val source_file_from_path : string -> DB.source_file

@ -1,94 +0,0 @@
(*
* Copyright (c) 2013 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*)
open! Utils
(* Take as input an ast file and a C or ObjectiveC file such that the ast file
corresponds to the compilation of the C file with clang.
Parse the ast file into a data structure and translates it into a cfg. *)
module L = Logging
let buffer_len = 262143
(* This function reads the json file in fname, validates it, and encoded in the AST data structure*)
(* defined in Clang_ast_t. *)
let validate_decl_from_file fname =
try
Ag_util.Biniou.from_file ~len:buffer_len Clang_ast_b.read_decl fname
with (Invalid_argument "Bi_inbuf.refill_from_channel") ->
Logging.out "WARNING: biniou buffer too short, skipping the file\n";
assert false
let validate_decl_from_stdin () =
try
Ag_util.Biniou.from_channel ~len:buffer_len Clang_ast_b.read_decl stdin
with (Invalid_argument "Bi_inbuf.refill_from_channel") ->
Logging.out "WARNING: biniou buffer too short, skipping the file\n";
assert false
let register_perf_stats_report source_file =
let stats_dir = Filename.concat Config.results_dir Config.frontend_stats_dir_name in
let abbrev_source_file = DB.source_file_encoding source_file in
let stats_file = Config.perf_stats_prefix ^ "_" ^ abbrev_source_file ^ ".json" in
create_dir Config.results_dir ;
create_dir stats_dir ;
PerfStats.register_report_at_exit (Filename.concat stats_dir stats_file)
let init_global_state_for_capture_and_linters source_file =
register_perf_stats_report source_file;
DB.Results_dir.init source_file
let do_run source_file ast_path =
let init_time = Unix.gettimeofday () in
let print_elapsed () =
let elapsed = Unix.gettimeofday () -. init_time in
Logging.out "Elapsed: %07.3f seconds.\n" elapsed in
try
let ast_filename, ast_decl =
match ast_path with
| Some path ->
path, validate_decl_from_file path
| None ->
"stdin of " ^ DB.source_file_to_string source_file, validate_decl_from_stdin () in
let decl_index, stmt_index, type_index, ivar_to_property_index =
Clang_ast_main.index_node_pointers ast_decl in
CFrontend_config.pointer_decl_index := decl_index;
CFrontend_config.pointer_stmt_index := stmt_index;
CFrontend_config.pointer_type_index := type_index;
CFrontend_config.ivar_to_property_index := ivar_to_property_index;
CFrontend_config.json := ast_filename;
Logging.out "Clang frontend action is %s\n" Config.clang_frontend_action_string;
Logging.out "Start %s of AST from %s\n" Config.clang_frontend_action_string
!CFrontend_config.json;
init_global_state_for_capture_and_linters source_file;
let translation_unit_context = CFrontend_config.{source_file; lang=Config.clang_lang} in
if Config.clang_frontend_do_lint then
CFrontend_checkers_main.do_frontend_checks translation_unit_context ast_decl;
if Config.clang_frontend_do_capture then
CFrontend.do_source_file translation_unit_context ast_decl;
Logging.out "End translation AST file %s... OK!@\n" !CFrontend_config.json;
print_elapsed ();
with
(Yojson.Json_error s) as exc ->
Logging.err_debug "%s\n" s;
print_elapsed ();
raise exc
let () =
let source_file =
(match Config.source_file with
| Some path ->
Logging.set_log_file_identifier (Some (Filename.basename path));
CLocation.check_source_file path;
CLocation.source_file_from_path path
| None ->
Logging.err_debug "Incorrect command line arguments\n";
Config.print_usage_exit ()) in
do_run source_file Config.ast_file

@ -1,16 +0,0 @@
(*
* Copyright (c) 2013 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*)
open! Utils
(** Main module of InferClang. Take as input AST files produced by clang during compilation and
their corresponding C/C++/ObjectiveC source files.
Parse the arguments, parse and validate the input AST into a data structure and translates it
into a cfg. *)

@ -21,7 +21,7 @@ type method_signature = {
loc : Clang_ast_t.source_range;
is_instance : bool;
is_cpp_virtual : bool;
language : Config.clang_lang;
language : CFrontend_config.clang_lang;
pointer_to_parent : Clang_ast_t.pointer option;
pointer_to_property_opt : Clang_ast_t.pointer option; (* If set then method is a getter/setter *)
return_param_typ : Typ.t option;

@ -31,7 +31,7 @@ val ms_is_instance : method_signature -> bool
val ms_is_cpp_virtual : method_signature -> bool
val ms_get_lang : method_signature -> Config.clang_lang
val ms_get_lang : method_signature -> CFrontend_config.clang_lang
val ms_get_pointer_to_parent : method_signature -> Clang_ast_t.pointer option
@ -45,7 +45,7 @@ val ms_is_setter : method_signature -> bool
val make_ms : Procname.t -> (Mangled.t * Clang_ast_t.qual_type) list -> Clang_ast_t.type_ptr
-> Clang_ast_t.attribute list -> Clang_ast_t.source_range -> bool -> ?is_cpp_virtual:bool
-> Config.clang_lang -> Clang_ast_t.pointer option -> Clang_ast_t.pointer option
-> CFrontend_config.clang_lang -> Clang_ast_t.pointer option -> Clang_ast_t.pointer option
-> Typ.t option -> method_signature
val replace_name_ms : method_signature -> Procname.t -> method_signature

@ -92,9 +92,9 @@ let get_param_decls function_method_decl_info =
let get_language trans_unit_ctx function_method_decl_info =
match function_method_decl_info with
| Func_decl_info (_, _) -> trans_unit_ctx.CFrontend_config.lang
| Cpp_Meth_decl_info _ -> Config.CPP
| ObjC_Meth_decl_info _ -> Config.OBJC
| Block_decl_info _ -> Config.OBJC
| Cpp_Meth_decl_info _ -> CFrontend_config.CPP
| ObjC_Meth_decl_info _ -> CFrontend_config.ObjC
| Block_decl_info _ -> CFrontend_config.ObjC
let is_cpp_virtual function_method_decl_info =
match function_method_decl_info with
@ -300,9 +300,9 @@ let get_formal_parameters tenv ms =
| (mangled, {Clang_ast_t.qt_type_ptr}):: pl' ->
let should_add_pointer name ms =
let is_objc_self = name = CFrontend_config.self &&
CMethod_signature.ms_get_lang ms = Config.OBJC in
CMethod_signature.ms_get_lang ms = CFrontend_config.ObjC in
let is_cxx_this = name = CFrontend_config.this &&
CMethod_signature.ms_get_lang ms = Config.CPP in
CMethod_signature.ms_get_lang ms = CFrontend_config.CPP in
(is_objc_self && CMethod_signature.ms_is_instance ms) || is_cxx_this in
let tp = if should_add_pointer (Mangled.to_string mangled) ms then
(Ast_expressions.create_pointer_type qt_type_ptr)
@ -386,7 +386,7 @@ let create_local_procdesc trans_unit_ctx cfg tenv ms fbody captured is_objc_inst
let method_annotation =
sil_method_annotation_of_args (CMethod_signature.ms_get_args ms) in
let is_cpp_inst_method = CMethod_signature.ms_is_instance ms
&& CMethod_signature.ms_get_lang ms = Config.CPP in
&& CMethod_signature.ms_get_lang ms = CFrontend_config.CPP in
let create_new_procdesc () =
let formals = get_formal_parameters tenv ms in
let captured_mangled = IList.map (fun (var, t) -> (Pvar.get_name var), t) captured in

@ -48,7 +48,7 @@ struct
| Procname.ObjC_Cpp objc_cpp ->
let class_name = Procname.objc_cpp_get_class_name objc_cpp in
CTrans_models.get_predefined_model_method_signature class_name selector
General_utils.mk_procname_from_objc_method Config.OBJC
General_utils.mk_procname_from_objc_method CFrontend_config.ObjC
| _ ->
None in
match predefined_ms_opt, ms_opt with

@ -41,6 +41,6 @@ val is_cf_retain_release : Procname.t -> bool
val get_predefined_model_method_signature : string -> string ->
(string -> string -> Procname.objc_cpp_method_kind -> Procname.t) ->
Config.clang_lang -> CMethod_signature.method_signature option
CFrontend_config.clang_lang -> CMethod_signature.method_signature option
val is_dispatch_function_name : string -> (string * int) option

@ -15,11 +15,9 @@ FILES = \
include_header/include_templ.cpp \
memory_leaks/*.cpp \
models/*.cpp \
nestedoperators/*.cpp \
npe/*.cpp \
numeric/*.cpp \
overwrite_attribute/*.cpp \
reference/*.cpp \
resource_leaks/*.cpp \
shared/attributes/*.cpp \
shared/conditional/*.cpp \
@ -53,8 +51,6 @@ FILES = \
shared/types/typeid_expr.cpp \
smart_ptr/*.cpp \
subtyping/*.cpp \
templates/*.cpp \
types/*.cpp \
vector/*.cpp \
compile:

Loading…
Cancel
Save