From 7c0bf66794ad5fa9b161a15c450735e3490fca56 Mon Sep 17 00:00:00 2001 From: Jules Villard Date: Wed, 11 Apr 2018 10:10:22 -0700 Subject: [PATCH] [clang] do not run clang on unsupported commands Summary: Try to skip running even `clang -###` when we know there's nothing to capture in the clang command. Do that by recording in the `ClangCommand.t` whether the command is pre- or post-running `clang -###`, and apply slightly different filters in each case. In particular, the filter has to let in more commands than necessary pre-`clang -###` because the flags are not normalised at that point. Also regard `-x cuda` commands as unsupported. Reviewed By: mbouaziz Differential Revision: D7584934 fbshipit-source-id: c27bb63 --- infer/src/clang/Capture.mli | 2 ++ infer/src/clang/ClangCommand.ml | 22 ++++++++++++++++----- infer/src/clang/ClangCommand.mli | 15 ++++++++------ infer/src/clang/ClangWrapper.ml | 34 ++++++++++++++++++++++++-------- 4 files changed, 54 insertions(+), 19 deletions(-) diff --git a/infer/src/clang/Capture.mli b/infer/src/clang/Capture.mli index c4c5f18b2..bd21b78e2 100644 --- a/infer/src/clang/Capture.mli +++ b/infer/src/clang/Capture.mli @@ -9,4 +9,6 @@ open! IStd +val run_clang : ClangCommand.t -> (In_channel.t -> 'a) -> 'a + val capture : ClangCommand.t -> unit diff --git a/infer/src/clang/ClangCommand.ml b/infer/src/clang/ClangCommand.ml index 11f52eac2..b36e5c0b5 100644 --- a/infer/src/clang/ClangCommand.ml +++ b/infer/src/clang/ClangCommand.ml @@ -10,7 +10,12 @@ open! IStd module L = Logging -type t = {exec: string; argv: string list; orig_argv: string list; quoting_style: ClangQuotes.style} +type t = + { exec: string + ; argv: string list + ; orig_argv: string list + ; quoting_style: ClangQuotes.style + ; is_driver: bool } (** bad for every clang invocation *) let clang_blacklisted_flags = @@ -49,7 +54,11 @@ let can_attach_ast_exporter cmd = let is_supported_language cmd = match value_of_option cmd "-x" with | None -> - L.external_warning "malformed -cc1 command has no \"-x\" flag!" ; + if cmd.is_driver then (* let's continue and ask clang -### *) true + else ( + L.external_warning "malformed -cc1 command has no \"-x\" flag!" ; + false ) + | Some "cuda" -> false | Some lang when String.is_prefix ~prefix:"assembler" lang -> false @@ -58,9 +67,12 @@ let can_attach_ast_exporter cmd = in (* -Eonly is -cc1 flag that gets produced by 'clang -M -### ...' *) let is_preprocessor_only cmd = has_flag cmd "-E" || has_flag cmd "-Eonly" in - has_flag cmd "-cc1" && is_supported_language cmd && not (is_preprocessor_only cmd) + (cmd.is_driver || has_flag cmd "-cc1") && is_supported_language cmd + && not (is_preprocessor_only cmd) +let may_capture cmd = can_attach_ast_exporter cmd + let argv_cons a b = a :: b let argv_do_if cond action x = if cond then action x else x @@ -174,14 +186,14 @@ let clang_cc1_cmd_sanitizer cmd = file_arg_cmd_sanitizer {cmd with argv= clang_arguments} -let mk quoting_style ~prog ~args = +let mk ~is_driver quoting_style ~prog ~args = (* Some arguments break the compiler so they need to be removed even before the normalization step *) let blacklisted_flags_with_arg = ["-index-store-path"] in let sanitized_args = filter_and_replace_unsupported_args ~blacklisted_flags:clang_blacklisted_flags ~blacklisted_flags_with_arg args in - {exec= prog; orig_argv= sanitized_args; argv= sanitized_args; quoting_style} + {exec= prog; orig_argv= sanitized_args; argv= sanitized_args; quoting_style; is_driver} let to_unescaped_args cmd = diff --git a/infer/src/clang/ClangCommand.mli b/infer/src/clang/ClangCommand.mli index f366f0187..da7097fba 100644 --- a/infer/src/clang/ClangCommand.mli +++ b/infer/src/clang/ClangCommand.mli @@ -11,12 +11,12 @@ open! IStd type t -val mk : ClangQuotes.style -> prog:string -> args:string list -> t -(** [mk qs prog args] finds the type of command depending on its arguments [args]. The quoting style - of the arguments have to be provided, so that the command may be run later on. Beware that this - doesn't look inside argument files. This can be used to create a "clang -### ..." command on - which to call [command_to_run], but other functions from the module will not work as expected - unless the command has been normalized by "clang -### ...". *) +val mk : is_driver:bool -> ClangQuotes.style -> prog:string -> args:string list -> t +(** [mk ~is_driver qs prog args] finds the type of command depending on its arguments [args]. The + quoting style of the arguments have to be provided, so that the command may be run later + on. Beware that this doesn't look inside argument files. This can be used to create a "clang -### + ..." command on which to call [command_to_run], but other functions from the module will not work + as expected unless the command has been normalized by "clang -### ...". *) val command_to_run : t -> string (** Make a command into a string ready to be passed to a shell to be executed. Fine to call with @@ -25,6 +25,9 @@ val command_to_run : t -> string val can_attach_ast_exporter : t -> bool (** Whether the command is suitable for attaching the AST exporter. Must be called on normalized commands. *) +val may_capture : t -> bool +(** Whether the command has a chance of triggering compilation steps we can capture. *) + val with_plugin_args : t -> t (** Add the arguments needed to attach the facebook-clang-plugins plugin. Must be called on normalized commands. *) diff --git a/infer/src/clang/ClangWrapper.ml b/infer/src/clang/ClangWrapper.ml index 938f8fd96..7ddc2ae1f 100644 --- a/infer/src/clang/ClangWrapper.ml +++ b/infer/src/clang/ClangWrapper.ml @@ -13,7 +13,11 @@ open! IStd module L = Logging -type action_item = Command of ClangCommand.t | ClangError of string | ClangWarning of string +type action_item = + | CanonicalCommand of ClangCommand.t (** commands output by [clang -###] *) + | DriverCommand of ClangCommand.t (** commands prior to [clang -###] treatment *) + | ClangError of string + | ClangWarning of string let clang_ignore_regex = Option.map ~f:Str.regexp Config.clang_ignore_regex @@ -50,10 +54,10 @@ let check_for_existing_file args = check_for_existing_file_arg all_args -(** 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 ~prog ~args : action_item list = - let cmd = ClangCommand.mk ClangQuotes.SingleQuotes ~prog ~args in +(** Given a clang command, return a list of new commands to run according to the results of `clang + -### [args]`. *) +let clang_driver_action_items : ClangCommand.t -> action_item list = + fun cmd -> let clang_hashhashhash = Printf.sprintf "%s 2>&1" ( ClangCommand.prepend_arg "-###" cmd @@ -77,14 +81,14 @@ let normalize ~prog ~args : action_item list = let normalized_commands = ref [] in let one_line line = if String.is_prefix ~prefix:" \"" line then - Command + CanonicalCommand ( match (* massage line to remove edge-cases for splitting *) "\"" ^ line ^ " \"" |> (* split by whitespace *) Str.split (Str.regexp_string "\" \"") with | prog :: args -> - ClangCommand.mk ClangQuotes.EscapedDoubleQuotes ~prog ~args + ClangCommand.mk ~is_driver:false ClangQuotes.EscapedDoubleQuotes ~prog ~args | [] -> L.(die InternalError) "ClangWrapper: argv cannot be empty" ) else if Str.string_match (Str.regexp "clang[^ :]*: warning: ") line 0 then ClangWarning line @@ -114,6 +118,13 @@ let normalize ~prog ~args : action_item list = !normalized_commands +(** Given a list of arguments for clang [args], return a list of new commands to run according to + the results of `clang -### [args]` if the command can be analysed. *) +let normalize ~prog ~args : action_item list = + let cmd = ClangCommand.mk ~is_driver:true ClangQuotes.SingleQuotes ~prog ~args in + if ClangCommand.may_capture cmd then clang_driver_action_items cmd else [DriverCommand cmd] + + let exec_action_item ~prog ~args = function | ClangError error -> (* An error in the output of `clang -### ...`. Outputs the error and fail. This is because @@ -128,8 +139,15 @@ let exec_action_item ~prog ~args = function *** Infer needs a working compilation command to run." prog Pp.cli_args args error | ClangWarning warning -> L.external_warning "%s@\n" warning - | Command clang_cmd -> + | CanonicalCommand clang_cmd -> Capture.capture clang_cmd + | DriverCommand clang_cmd -> + if Option.is_none Config.buck_compilation_database + || Config.skip_analysis_in_path_skips_compilation + then Capture.run_clang clang_cmd Utils.echo_in + else + L.debug Capture Quiet "Skipping seemingly uninteresting clang driver command %s@\n" + (ClangCommand.command_to_run clang_cmd) let exe ~prog ~args =