From 7d49e16d63838ebeb5a15e9495f385d04a94855f Mon Sep 17 00:00:00 2001 From: Jules Villard Date: Thu, 6 Oct 2016 03:58:10 -0700 Subject: [PATCH] [clang] rewrite hijack_and_normalize_clang_command in OCaml Summary: Let's start migrating some of our bash script to OCaml to make them easier to maintain and extend. For now replace just one script and put it in lib/clang_wrappers/ at compile time, where the former script used to be. Further simplifications will come later. Reviewed By: jberdine Differential Revision: D3929988 fbshipit-source-id: b2d8b37 --- .../hijack_and_normalize_clang_command | 1 - .../hijack_and_normalize_clang_command++ | 1 - .../hijack_and_normalize_clang_command.sh | 83 -------- infer/lib/wrappers/clang | 8 +- infer/lib/xcode_wrappers/clang | 7 +- infer/src/Makefile | 11 +- infer/src/backend/Process.ml | 6 +- infer/src/base/CommandLineOption.ml | 15 +- infer/src/base/CommandLineOption.mli | 4 +- infer/src/base/Config.ml | 22 +- infer/src/scripts/InferClangWrapper.re | 192 ++++++++++++++++++ .../componentkit/MutableLocalVariablesTest.mm | 4 +- 12 files changed, 240 insertions(+), 114 deletions(-) delete mode 120000 infer/lib/clang_wrappers/hijack_and_normalize_clang_command delete mode 120000 infer/lib/clang_wrappers/hijack_and_normalize_clang_command++ delete mode 100755 infer/lib/clang_wrappers/hijack_and_normalize_clang_command.sh create mode 100644 infer/src/scripts/InferClangWrapper.re diff --git a/infer/lib/clang_wrappers/hijack_and_normalize_clang_command b/infer/lib/clang_wrappers/hijack_and_normalize_clang_command deleted file mode 120000 index d2862a31c..000000000 --- a/infer/lib/clang_wrappers/hijack_and_normalize_clang_command +++ /dev/null @@ -1 +0,0 @@ -hijack_and_normalize_clang_command.sh \ No newline at end of file diff --git a/infer/lib/clang_wrappers/hijack_and_normalize_clang_command++ b/infer/lib/clang_wrappers/hijack_and_normalize_clang_command++ deleted file mode 120000 index d2862a31c..000000000 --- a/infer/lib/clang_wrappers/hijack_and_normalize_clang_command++ +++ /dev/null @@ -1 +0,0 @@ -hijack_and_normalize_clang_command.sh \ No newline at end of file diff --git a/infer/lib/clang_wrappers/hijack_and_normalize_clang_command.sh b/infer/lib/clang_wrappers/hijack_and_normalize_clang_command.sh deleted file mode 100755 index b5b7dd76f..000000000 --- a/infer/lib/clang_wrappers/hijack_and_normalize_clang_command.sh +++ /dev/null @@ -1,83 +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 more-or-less raw arguments passed to clang as arguments, -# this normalizes them via `clang -###` if needed to call the script -# that actually attaches the plugin on each source file. Unless we -# don't want to attach the plugin, in which case just run the original -# command. - -#### Configuration #### -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - -# script to run our own clang -CLANG_COMPILER="${SCRIPT_DIR}/filter_args_and_run_fcp_clang" -# script to attach the plugin to clang -cc1 commands and run InferClang -CLANG_CC1_CAPTURE="${SCRIPT_DIR}/attach_plugin_and_run_clang_frontend.sh" -# path to Apple's clang -APPLE_CLANG="$FCP_APPLE_CLANG" - -# Main -if [ "${0%++}" != "$0" ]; then XX="++"; fi - -# Skip -cc1as commands -if [ "$1" = "-cc1as" ]; then - STATUS=0 -# Normalize clang command if not -cc1 already. -cc1 is always the first argument if present. -elif [ "$1" = "-cc1" ]; then - "$CLANG_CC1_CAPTURE" "$@" - STATUS=$? -else - # Run `clang -###` to get one compile command per source file. - # Slow since it spawns clang as a separate process - # - # Generate a command containing all the commands in the output of `clang -###`. These are - # the lines that start with ' "/absolute/path/to/binary"'. - # - # In that command, replace /absolute/path/to/clang with our own wrapper, but only for the - # core compiler commands (those that start with "-cc1"). This means we'll capture all - # compilation commands (one per source file), without interfering with non-compiler commands - # (as they run with absolute paths, so they won't get captured again further down the line). - # - # Fail on errors: if we detect an error in the output of `clang -###`, we add the line - # `echo ; exit 1` to the generated command. This is because `clang -###` pretty much - # never fails, but warns of failures on stderr instead. - CC_COMMAND=$("$CLANG_COMPILER$XX" -### "$@" 2>&1 | \ - # only keep lines that are commands or errors - grep -e '^\([[:space:]]\"\|clang: error:\)' | \ - # replace -cc1 commands with our clang wrapper - sed -e "s#^[[:space:]]\"\([^\"]*\)\" \"-cc1\" \(.*\)\$# \"$CLANG_CC1_CAPTURE\" \"-cc1\" \2#g" | \ - # do not run if language is assembler or assembler-with-cpp - grep -v -- '"-x" "assembler' | \ - # do not run -cc1as commands - grep -v -- '"-cc1as"' | \ - # replace error messages by failures - sed -e 's#^\(^clang: error:.*$\)#echo "\1"; exit 1#g' | \ - # add trailing ; to each line - sed -e 's/$/;/g') - if [ -n "$CC_COMMAND" ]; then - eval $CC_COMMAND - else - # No command to execute after -###, this is fishy, let's execute the original command - # instead. - # - # In particular, this can happen when 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. - "$CLANG_COMPILER$XX" "$@" - fi - STATUS=$? -fi - -# run Apple clang if required (and if any) -if [ -n "$APPLE_CLANG" ]; then - "$APPLE_CLANG$XX" "$@" || exit $? -fi - -exit $STATUS diff --git a/infer/lib/wrappers/clang b/infer/lib/wrappers/clang index 6aebd5179..afee181ee 100755 --- a/infer/lib/wrappers/clang +++ b/infer/lib/wrappers/clang @@ -10,9 +10,11 @@ if [ -z "$INFER_RESULTS_DIR" ]; then fi # invoke the right compiler looking at the final plusplus (e.g. gcc/g++ clang/clang++) -if [ "${0%++}" != "$0" ]; then XX="++"; fi -FRONTEND_COMMAND=("$SCRIPT_DIR/../clang_wrappers/hijack_and_normalize_clang_command$XX" "$@") -HOST_COMPILER_COMMAND=("$SCRIPT_DIR/../clang_wrappers/filter_args_and_run_fcp_clang$XX" "$@") +if [ "${0%++}" != "$0" ]; then INFER_XX="++"; fi +export INFER_XX + +FRONTEND_COMMAND=("$SCRIPT_DIR/../clang_wrappers/InferClangWrapper" "$@") +HOST_COMPILER_COMMAND=("$SCRIPT_DIR/../clang_wrappers/filter_args_and_run_fcp_clang$INFER_XX" "$@") if [ -n "$INFER_COMPILER_WRAPPER_IN_RECURSION" ]; then if [ -z "$INFER_LISTENER" ]; then diff --git a/infer/lib/xcode_wrappers/clang b/infer/lib/xcode_wrappers/clang index bfef4ed44..803d806fa 100755 --- a/infer/lib/xcode_wrappers/clang +++ b/infer/lib/xcode_wrappers/clang @@ -3,9 +3,10 @@ SCRIPT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" CLANG_WRAPPERS_PATH="${SCRIPT_PATH}/../clang_wrappers" -if [ "${0%++}" != "$0" ]; then XX="++"; else XX=""; fi +if [ "${0%++}" != "$0" ]; then INFER_XX="++"; fi +export INFER_XX -export FCP_CLANG_COMPILER="${CLANG_WRAPPERS_PATH%/}/filter_args_and_run_fcp_clang$XX"; +export FCP_CLANG_COMPILER="${CLANG_WRAPPERS_PATH%/}/filter_args_and_run_fcp_clang$INFER_XX"; export FCP_RESULTS_DIR="${INFER_RESULTS_DIR}"; if [ -z "$INFER_RESULTS_DIR" ]; then @@ -16,4 +17,4 @@ if [ -z "$INFER_RESULTS_DIR" ]; then exit $? fi -"${CLANG_WRAPPERS_PATH%/}/hijack_and_normalize_clang_command$XX" "$@" -fno-cxx-modules +"${CLANG_WRAPPERS_PATH%/}/InferClangWrapper" "$@" -fno-cxx-modules diff --git a/infer/src/Makefile b/infer/src/Makefile index 76bf0928b..adf2e2cb6 100644 --- a/infer/src/Makefile +++ b/infer/src/Makefile @@ -122,6 +122,9 @@ 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 @@ -154,8 +157,9 @@ INFER_BASE_TARGETS = \ INFER_ALL_TARGETS = $(INFER_BASE_TARGETS) \ $(INFERJAVA_MAIN).native \ $(INFERCLANG_MAIN).native \ + $(CLANGWRAPPER_MAIN).native \ $(INFERLLVM_MAIN).native \ - $(BUCK_COMPILATION_DATABASE_MAIN).native + $(BUCK_COMPILATION_DATABASE_MAIN).native # configure-aware ocamlbuild commands and targets OCAMLBUILD_CONFIG = $(OCAMLBUILD_BASE) @@ -167,7 +171,7 @@ INFER_CONFIG_TARGETS += $(INFERJAVA_MAIN).native DEPENDENCIES += java endif ifeq ($(BUILD_C_ANALYZERS),yes) -INFER_CONFIG_TARGETS += $(INFERCLANG_MAIN).native +INFER_CONFIG_TARGETS += $(INFERCLANG_MAIN).native $(CLANGWRAPPER_MAIN).native INFER_CONFIG_TARGETS += $(BUCK_COMPILATION_DATABASE_MAIN).native DEPENDENCIES += clang endif @@ -196,6 +200,7 @@ 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) @@ -368,7 +373,7 @@ endif $(REMOVE) backend/jsonbug_{j,t}.ml{,i} $(REMOVE) checkers/stacktree_{j,t}.ml{,i} $(REMOVE) $(INFER_BIN) $(INFERANALYZE_BIN) $(INFERPRINT_BIN) $(STATSAGGREGATOR_BIN) - $(REMOVE) $(INFERJAVA_BIN) $(INFERCLANG_BIN) $(INFERLLVM_BIN) + $(REMOVE) $(INFERJAVA_BIN) $(INFERCLANG_BIN) $(CLANGWRAPPER_BIN) $(INFERLLVM_BIN) $(REMOVE) $(INFERUNIT_BIN) $(CHECKCOPYRIGHT_BIN) $(INFER_BUCK_COMPILATION_DATABASE_BIN) $(REMOVE) $(CLANG_ATDGEN_STUBS) $(REMOVE) $(INFER_CLANG_FCP_MIRRORED_FILES) diff --git a/infer/src/backend/Process.ml b/infer/src/backend/Process.ml index c178f48dd..56fc7c85f 100644 --- a/infer/src/backend/Process.ml +++ b/infer/src/backend/Process.ml @@ -31,9 +31,9 @@ let exec_command cmd args env = with (Unix.Unix_error _ as e) -> print_unix_error cmd e -(** Given an command to be executed, creates a process to execute this command, - and waits for it to terminate. The standard out and error are not redirected. - If the commands fails to execute, prints an error message and exits. *) +(** Given a command to be executed, create a process to execute this command, and wait for it to + terminate. The standard out and error are not redirected. If the command fails to execute, + print an error message and exit. *) let create_process_and_wait cmd = let pid = Unix.create_process cmd.(0) cmd Unix.stdin Unix.stdout Unix.stderr in let _, status = Unix.waitpid [] pid in diff --git a/infer/src/base/CommandLineOption.ml b/infer/src/base/CommandLineOption.ml index bb56f3d2c..5c62ce759 100644 --- a/infer/src/base/CommandLineOption.ml +++ b/infer/src/base/CommandLineOption.ml @@ -17,14 +17,15 @@ 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 | Interactive | Java | Llvm | Print | - StatsAggregator | Toplevel +type exe = Analyze | BuckCompilationDatabase | Clang | ClangWrapper | Interactive | Java | Llvm + | Print | StatsAggregator | Toplevel let exes = [ ("InferBuckCompilationDatabase", BuckCompilationDatabase); ("InferAnalyze", Analyze); ("InferClang", Clang); + ("InferClangWrapper", ClangWrapper); ("InferJava", Java); ("InferLLVM", Llvm); ("InferPrint", Print); @@ -561,9 +562,13 @@ let parse ?(incomplete=false) ?(accept_unknown=false) ?config_file env_var exe_u let env_args = c_args @ env_args in (* end transitional support for INFERCLANG_ARGS *) let exe_name = Sys.executable_name in - let env_cl_args = match current_exe with - | Interactive -> env_args - | _ -> prepend_to_argv env_args in + let should_parse_cl_args = match current_exe with + | ClangWrapper | Interactive -> false + | Analyze | BuckCompilationDatabase | Clang | Java | Llvm | Print | StatsAggregator + | Toplevel -> true in + let env_cl_args = + if should_parse_cl_args then prepend_to_argv env_args + else env_args in let all_args = match config_file with | None -> env_cl_args | Some path -> diff --git a/infer/src/base/CommandLineOption.mli b/infer/src/base/CommandLineOption.mli index d54bf8626..2774d9916 100644 --- a/infer/src/base/CommandLineOption.mli +++ b/infer/src/base/CommandLineOption.mli @@ -11,8 +11,8 @@ open! Utils -type exe = Analyze | BuckCompilationDatabase | Clang | Interactive | Java | Llvm | Print | - StatsAggregator | Toplevel +type exe = Analyze | BuckCompilationDatabase | Clang | ClangWrapper | Interactive | Java | Llvm + | Print | StatsAggregator | Toplevel val current_exe : exe diff --git a/infer/src/base/Config.ml b/infer/src/base/Config.ml index 824518f3c..6243798cd 100644 --- a/infer/src/base/Config.ml +++ b/infer/src/base/Config.ml @@ -1221,9 +1221,19 @@ let exe_usage (exe : CLOpt.exe) = Usage: InferAnalyze [options]\n\ Analyze the files captured in the project results directory, \ which can be specified with the --results-dir option." + | BuckCompilationDatabase -> + "Usage: BuckCompilationDatabase --Xbuck //target \n\ + Runs buck with the flavor compilation-database or uber-compilation-database. It then \n\ + reads the compilation database emited in json and runs the capture in parallel for \n\ + those commands" | Clang -> "Usage: InferClang -c -ast --results-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 -> + "Usage: interactive ocaml toplevel. To pass infer config options use env variable" | Java -> "Usage: InferJava [options]\n\ Translate the given files using javac into infer internal representation for later analysis." @@ -1241,13 +1251,6 @@ let exe_usage (exe : CLOpt.exe) = Aggregates all the perf stats generated by Buck on each target" | Toplevel -> version_string - | Interactive -> - "Usage: interactive ocaml toplevel. To pass infer config options use env variable" - | BuckCompilationDatabase -> - "Usage: BuckCompilationDatabase --Xbuck //target \n\ - Runs buck with the flavor compilation-database or uber-compilation-database. It then \n\ - reads the compilation database emited in json and runs the capture in parallel for \n\ - those commands" let post_parsing_initialization () = F.set_margin !margin ; @@ -1543,6 +1546,7 @@ let log_dir_of_current_exe = | Analyze -> "analyze" | BuckCompilationDatabase -> "buck_compilation_database" | Clang -> "clang" + | ClangWrapper -> "clang_wrapper" | Interactive -> "interactive" | Java -> "java" | Llvm -> "llvm" @@ -1554,6 +1558,7 @@ let log_identifier_of_current_exe = match CLOpt.current_exe with | Analyze -> Option.map Filename.basename cluster_cmdline | Clang -> Option.map Filename.basename source_file + | ClangWrapper | Interactive | Java | Llvm @@ -1576,7 +1581,8 @@ let log_files_of_current_exe = let should_log_current_exe = match CLOpt.current_exe with | Analyze - | Clang -> debug_mode || stats_mode + | Clang + | ClangWrapper -> debug_mode || stats_mode | BuckCompilationDatabase -> true | _ -> false diff --git a/infer/src/scripts/InferClangWrapper.re b/infer/src/scripts/InferClangWrapper.re new file mode 100644 index 000000000..cf7e2d24b --- /dev/null +++ b/infer/src/scripts/InferClangWrapper.re @@ -0,0 +1,192 @@ +/* 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 a clang command, normalize it via `clang -###` if needed to get a clear view of what work + is being done and which source files are being compiled, if any, then replace compilation + commands by our own clang with our plugin attached for each source file. */ + +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]); + Logging.out "clang -### invocation: %s@\n" clang_hashhashhash; + let normalized_commands = ref []; + let one_line line => + if (string_is_prefix " \"" line) { + /* massage line to remove edge-cases for splitting */ + "\"" ^ line ^ " \"" |> + /* split by whitespace */ + Str.split (Str.regexp_string "\" \"") |> + Array.of_list |> + typed_clang_invocation + } else { + `ClangError line + }; + let consume_input i => + try ( + while true { + let line = input_line i; + /* keep only commands and errors */ + if (Str.string_match commands_or_errors line 0) { + normalized_commands := [one_line line, ...!normalized_commands] + } + } + ) { + | End_of_file => () + }; + /* collect stdout and stderr output together (in reverse order) */ + with_process_full clang_hashhashhash consume_input consume_input |> ignore; + normalized_commands := IList.rev !normalized_commands; + /* Discard assembly commands. This may make the list of commands empty, in which case we'll run + the original clang command. We could be smarter about this and try to execute the assembly + commands with our own clang. */ + IList.filter + ( + fun + | `Assembly asm_cmd => { + Logging.out + "Skipping assembly command %s@\n" (String.concat " " @@ Array.to_list asm_cmd); + false + } + | _ => true + ) + !normalized_commands + | `Assembly _ => + /* discard assembly commands -- see above */ + Logging.out "InferClangWrapper got toplevel assembly command@\n"; + [] + }; + +let execute_clang_command clang_cmd => + switch clang_cmd { + | `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 => + /* 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 => + /* We shouldn't get any assembly command at this point */ + ( + if Config.debug_mode { + failwithf + } else { + Logging.err + } + ) + "WARNING: unexpected assembly command: %s@\n" (String.concat " " @@ Array.to_list 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 + }; + +let () = { + let args = Sys.argv; + let commands = normalize args; + if (commands == []) { + /* No command to execute after -###, let's execute the original command + instead. + + In particular, this can happen when + - there are only assembly commands to execute, which we skip, or + - 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 -### ` returned\n an empty set of commands to run and no error. Will run the original command directly:@\n\n %s@\n" + (String.concat " " @@ Array.to_list args); + Process.create_process_and_wait args + } else { + IList.iter execute_clang_command commands + }; + /* 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; + Process.create_process_and_wait args + } +}; diff --git a/infer/tests/codetoanalyze/objcpp/linters/componentkit/MutableLocalVariablesTest.mm b/infer/tests/codetoanalyze/objcpp/linters/componentkit/MutableLocalVariablesTest.mm index eb3f26f81..a4e4b031b 100644 --- a/infer/tests/codetoanalyze/objcpp/linters/componentkit/MutableLocalVariablesTest.mm +++ b/infer/tests/codetoanalyze/objcpp/linters/componentkit/MutableLocalVariablesTest.mm @@ -96,8 +96,8 @@ class BarClass { // Whitelisted Objc class NSError* const e = nil; // no error - NSError** e1; // no error - NSError const** e2; // no error + NSError* __autoreleasing* e1; // no error + NSError const* __autoreleasing* e2; // no error NSError* const* e3 = &e; // no error NSError* const* const e4 = &e; // no error }