diff --git a/.gitignore b/.gitignore index 584e2abf771..4b9b46b5872 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,6 @@ # generated by build and tests /_build /_build_logs -/infer/_build /infer/tests/codetoanalyze/java/*/codetoanalyze /dependencies/infer-deps-* _build_infer @@ -93,8 +92,8 @@ buck-out/ #other /infer/bin/infer -/infer/bin/infer.byte -/infer/bin/infer.native +/infer/bin/infer.bc +/infer/bin/infer.exe /infer/bin/infer-analyze /infer/bin/infer-capture /infer/bin/infer-compile @@ -151,3 +150,9 @@ infer/src/.project # generated by Maven /infer/annotations/target + +# jbuilder +/infer/src/_build +/infer/src/jbuild +/infer/src/jbuild-workspace +/infer/src/scripts/.merlin diff --git a/Makefile b/Makefile index 5cd1f38b36c..c1d55b2a556 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,11 @@ default: infer ROOT_DIR = . include $(ROOT_DIR)/Makefile.config +ifneq ($(UTOP),no) +BUILD_SYSTEMS_TESTS += infertop +build_infertop_print build_infertop_replace build_infertop_test: toplevel_test +endif + ifeq ($(BUILD_C_ANALYZERS),yes) BUILD_SYSTEMS_TESTS += \ assembly \ @@ -81,8 +86,8 @@ endif ifneq ($(BUCK),no) BUILD_SYSTEMS_TESTS += buck genrule # do not run these two tests in parallel otherwise Buck has a bad time -build_genrule_test: build_buck_test -build_genrule_print: build_buck_print +build_genrule_test: | build_buck_test +build_genrule_print: | build_buck_print endif ifneq ($(MVN),no) BUILD_SYSTEMS_TESTS += mvn @@ -149,14 +154,9 @@ byte: src_build_common test_build: src_build_common $(QUIET)$(call silent_on_success,Testing Infer builds without warnings,\ $(MAKE) -C $(SRC_DIR) TEST=1 byte_no_install) -# byte_no_install builds most of what toplevel needs, so it's more efficient to run the -# toplevel build straight after it rather than in parallel. Note that both targets build files -# that the other doesn't. - $(QUIET)$(call silent_on_success,Testing Infer toplevel builds,\ - $(MAKE) -C $(SRC_DIR) TEST=1 toplevel) ifeq ($(IS_FACEBOOK_TREE),yes) -byte src_build test_build: fb-setup +byte src_build_common src_build test_build: fb-setup endif ifeq ($(BUILD_C_ANALYZERS),yes) @@ -219,7 +219,7 @@ endif .PHONY: ocaml_unit_test ocaml_unit_test: test_build $(QUIET)$(call silent_on_success,Running OCaml unit tests,\ - $(BUILD_DIR)/test/infer/unit/inferunit.byte) + $(BUILD_DIR)/test/inferunit.bc) define silence_make ($(1) 2> >(grep -v "warning: \(ignoring old\|overriding\) \(commands\|recipe\) for target") \ @@ -296,15 +296,17 @@ check_missing_mli: $(QUIET)for x in $$(find $(INFER_DIR)/src -name "*.ml"); do \ test -f "$$x"i || echo Missing "$$x"i; done -.PHONY: toplevel -toplevel: clang_plugin src_build_common - $(QUIET)$(MAKE) -C $(SRC_DIR) toplevel +# depend on test_build because jbuilder doesn't like running concurrently with itself +.PHONY: toplevel_test +toplevel_test: src_build_common | test_build +# build with TEST=1 as the infer_repl scripts expects to find the toplevel in the test build + $(QUIET)$(call silent_on_success,Building infer toplevel (test mode),\ + $(MAKE) -C $(SRC_DIR) TEST=1 toplevel) -.PHONY: inferScriptMode_test -inferScriptMode_test: test_build - $(QUIET)$(call silent_on_success,Testing infer OCaml REPL,\ - INFER_REPL_BINARY=ocaml TOPLEVEL_DIR=$(BUILD_DIR)/test/infer $(SCRIPT_DIR)/infer_repl \ - $(INFER_DIR)/tests/repl/infer_batch_script.mltop) +.PHONY: toplevel +toplevel: src_build_common + $(QUIET)$(call silent_on_success,Building infer toplevel,\ + $(MAKE) -C $(SRC_DIR) toplevel) .PHONY: checkCopyright checkCopyright: @@ -342,8 +344,7 @@ else endif .PHONY: config_tests -config_tests: test_build ocaml_unit_test endtoend_test inferScriptMode_test \ - checkCopyright validate-skel +config_tests: test_build ocaml_unit_test endtoend_test checkCopyright validate-skel $(QUIET)$(call silent_on_success,Building Infer source dependency graph,\ $(MAKE) -C $(SRC_DIR) mod_dep.dot) @@ -550,7 +551,10 @@ opam.lock: opam $(QUIET)$(call silent_on_success,generating opam.lock,\ $(OPAM) lock --pkg $(INFER_PKG_OPAMLOCK) > opam.lock) -OPAM_DEV_DEPS = ocp-indent merlin tuareg +# This is a magical version number that doesn't reinstall the world when added on top of what we +# have in opam.lock. To upgrade this version number, manually try to install several utop versions +# until you find one that doesn't recompile the world. TODO(t20828442): get rid of magic +OPAM_DEV_DEPS = ocp-indent merlin tuareg utop.2.0.0 .PHONY: devsetup devsetup: Makefile.autoconf diff --git a/Makefile.autoconf.in b/Makefile.autoconf.in index c733943cf5c..da2aa8544ae 100644 --- a/Makefile.autoconf.in +++ b/Makefile.autoconf.in @@ -64,6 +64,7 @@ prefix = @prefix@ PYTHON_lxml = @PYTHON_lxml@ SHASUM = @SHASUM@ USER_JAVA_HOME = @USER_JAVA_HOME@ +UTOP = @UTOP@ XCODE_SELECT = @XCODE_SELECT@ XCPRETTY = @XCPRETTY@ diff --git a/Makefile.config b/Makefile.config index 7a36026d4ca..069dc07890c 100644 --- a/Makefile.config +++ b/Makefile.config @@ -45,7 +45,7 @@ JAVA_BUILTINS_DIR = $(MODELS_DIR)/java/builtins JAVA_MODELS_DIR = $(MODELS_DIR)/java/src SRC_DIR = $(INFER_DIR)/src -BUILD_DIR = $(INFER_DIR)/_build +BUILD_DIR = $(SRC_DIR)/_build JAVA_LIB_DIR = $(LIB_DIR)/java SPECS_LIB_DIR = $(LIB_DIR)/specs diff --git a/configure.ac b/configure.ac index 3a226476acf..79758dfc4c6 100644 --- a/configure.ac +++ b/configure.ac @@ -187,6 +187,7 @@ AC_ASSERT_OCAML_PKG([biniou]) AC_ASSERT_OCAML_PKG([camlzip], [zip]) AC_ASSERT_OCAML_PKG([easy-format]) AC_ASSERT_OCAML_PKG([oUnit], [], [2.0.0]) +AC_CHECK_TOOL([UTOP], [utop], [no]) AC_ASSERT_OCAML_PKG([yojson]) AC_ARG_VAR([CAML_LD_LIBRARY_PATH], diff --git a/infer/.merlin b/infer/.merlin deleted file mode 100644 index 7f6e6831442..00000000000 --- a/infer/.merlin +++ /dev/null @@ -1,20 +0,0 @@ -S src/** -B _build/infer/** -B _build/test/infer/unit/** -PKG ANSITerminal -PKG atdgen -PKG cmdliner -PKG core -PKG javalib -PKG oUnit -PKG parmap -PKG ppx_compare -PKG ptrees -PKG sawja -PKG str -PKG unix -PKG xmlm -PKG yojson -PKG zip -FLG -principal -safe-string -short-paths -strict-formats -strict-sequence -FLG -w +a-4-9-40-41-42-45-48 diff --git a/infer/src/.merlin b/infer/src/.merlin new file mode 100644 index 00000000000..6822d2620cd --- /dev/null +++ b/infer/src/.merlin @@ -0,0 +1,38 @@ +B _build/default/** +PKG ANSITerminal +PKG atdgen +PKG cmdliner +PKG core +PKG javalib +PKG oUnit +PKG parmap +PKG ppx_compare +PKG ptrees +PKG sawja +PKG str +PKG unix +PKG xmlm +PKG yojson +PKG zip +FLG -principal -safe-string -short-paths -strict-formats -strict-sequence +FLG -w +a-4-9-40-41-42-45-48 +FLG -open InferBaseStdlib -open InferGenerated -open InferModules +S backend +S base +S bufferoverrun +S checkers +S clang +S clang_plugin +S clang_stubs +S eradicate +S facebook +S harness +S integration +S IR +S java +S java_stubs +S labs +S opensource +S quandary +S scripts +S unit diff --git a/infer/src/IR/BUILTINS.mli b/infer/src/IR/BUILTINS.ml similarity index 100% rename from infer/src/IR/BUILTINS.mli rename to infer/src/IR/BUILTINS.ml diff --git a/infer/src/Makefile b/infer/src/Makefile index f5277d7b78a..3224a0a94a0 100644 --- a/infer/src/Makefile +++ b/infer/src/Makefile @@ -11,61 +11,20 @@ include $(ROOT_DIR)/Makefile.config #### Global declarations #### ETC_DIR = $(INFER_DIR)/etc +# paths to BUILD_DIR are relative because that's how jbuilder likes it +JBUILDER_BUILD_DEFAULT = _build/default +JBUILDER_BUILD_TEST = _build/test ifeq ($(TEST),1) -BASE_BUILD_DIR = $(BUILD_DIR)/test +INFER_BUILD_DIR = $(JBUILDER_BUILD_TEST) else -BASE_BUILD_DIR = $(BUILD_DIR) +INFER_BUILD_DIR = $(JBUILDER_BUILD_DEFAULT) endif -INFER_BUILD_DIR = $(BASE_BUILD_DIR)/infer ATDGEN_SUFFIXES = _t.ml _t.mli _j.ml _j.mli -#### ocamlbuild options #### - -GENERATED_OCAML_SOURCES_GLOB = <*{clang_plugin/*,backend/jsonbug_*,checkers/stacktree_*}> - -OCAML_FATAL_WARNINGS = +3+5+6+8+10+11+12+18+19+20+21+23+26+29+27+32+33+34+35+37+38+39+50+52+57 - -# options for ocamlbuild -# Notice that use-ocamlfind is used here to avoid a linker bug in ocamlbuild when using -tag thread -OCAMLBUILD_OPTIONS = \ - -r \ - -use-menhir -menhir '$(MENHIR) --explain --strict'\ - -use-ocamlfind \ - -cflags -g -lflags -g \ - -cflags -short-paths \ - -cflags -safe-string \ - -cflags -principal \ - -cflags -strict-formats \ - -cflags -strict-sequence \ - -cflags -w,$(OCAML_FATAL_WARNINGS)-4-9-40-41-42-45-48 \ - -tag-line "$(GENERATED_OCAML_SOURCES_GLOB): warn(-27-32-34-35-39)" \ - -tag-line "not $(GENERATED_OCAML_SOURCES_GLOB) and <*/{,*/}*.{ml,re}{,i}>: package(ppx_compare)" \ - -tag-line "not $(GENERATED_OCAML_SOURCES_GLOB) and not <*{base/IList,base/IStd}.*>: open(IStd)" \ - -tag thread \ - -pkgs ANSITerminal,atdgen,cmdliner,core,extlib,oUnit,parmap,str,unix,xmlm,yojson,zip - -ifeq ($(ENABLE_OCAML_BINANNOT),yes) -OCAMLBUILD_OPTIONS += -cflags -bin-annot -endif - -ifeq ($(ENABLE_OCAMLOPT_CUSTOM_CC),yes) -OCAMLBUILD_OPTIONS += -lflags -cc,$(CC) -cflags -cc,$(CC) -endif - -ifneq (,$(findstring s,$(MAKEFLAGS))) -OCAMLBUILD_OPTIONS += -quiet -endif - -ifeq ($(TEST),1) -OCAMLBUILD_OPTIONS += -cflags -warn-error,$(OCAML_FATAL_WARNINGS) -endif - -export OCAMLFIND_IGNORE_DUPS_IN=$(shell $(OCAMLC) -where)/compiler-libs - #### Backend declarations #### -INFER_MAIN = backend/infer +INFER_MAIN = infer #### Checkers declarations #### @@ -83,18 +42,16 @@ INFERPRINT_ATDGEN_STUBS = $(addprefix $(INFERPRINT_ATDGEN_STUB_BASE), $(ATDGEN_S FACEBOOK_DIR = facebook INFER_CREATE_TRACEVIEW_LINKS_MODULE = InferCreateTraceViewLinks -INFER_CREATE_TRACEVIEW_LINKS_MAIN = $(FACEBOOK_DIR)/$(INFER_CREATE_TRACEVIEW_LINKS_MODULE) +INFER_CREATE_TRACEVIEW_LINKS_MAIN = $(INFER_CREATE_TRACEVIEW_LINKS_MODULE) ### InferUnit declarations ### UNIT_SOURCES = unit -INFERUNIT_MAIN = $(UNIT_SOURCES)/inferunit +INFERUNIT_MAIN = inferunit #### Java declarations #### -JAVA_OCAMLBUILD_OPTIONS = -pkgs javalib,ptrees,sawja - JAVA_SOURCES = java #### Clang declarations #### @@ -127,65 +84,44 @@ CLANG_BINIOU_DICT = $(ETC_DIR)/clang_ast.dict SCRIPT_SOURCES = scripts CHECKCOPYRIGHT_BIN = $(SCRIPT_DIR)/checkCopyright -CHECKCOPYRIGHT_MAIN = $(SCRIPT_SOURCES)/checkCopyright +CHECKCOPYRIGHT_MAIN = checkCopyright #### End of declarations #### -ifeq ($(IS_FACEBOOK_TREE),yes) -EXTRA_DEPS = facebook -else -EXTRA_DEPS = opensource +ifeq ($(ENABLE_OCAMLOPT_CUSTOM_CC),yes) +EXTRA_CFLAGS += -cc,$(CC) endif -DEPENDENCIES = \ - absint backend base bufferoverrun checkers eradicate harness integration IR labs quandary unit \ - $(EXTRA_DEPS) - -# ocamlbuild command with options common to all build targets -OCAMLBUILD_BASE = \ - $(OCAMLBUILD) $(OCAMLBUILD_OPTIONS) -j $(NCPU) $(addprefix -I , $(DEPENDENCIES)) \ - -# ocamlbuild with options necessary to build all targets at once, regardless of configure flags -OCAMLBUILD_ALL = $(OCAMLBUILD_BASE) $(JAVA_OCAMLBUILD_OPTIONS) - -# configure-aware ocamlbuild commands and targets -OCAMLBUILD_CONFIG = $(OCAMLBUILD_BASE) # list of ocamlbuild targets common to all build targets -- native version -INFER_CONFIG_TARGETS = $(INFER_MAIN).native +INFER_CONFIG_TARGETS = $(INFER_BUILD_DIR)/$(INFER_MAIN).exe ifeq ($(IS_FACEBOOK_TREE),yes) -INFER_CONFIG_TARGETS += $(INFER_CREATE_TRACEVIEW_LINKS_MAIN).native +INFER_CONFIG_TARGETS += $(INFER_BUILD_DIR)/$(INFER_CREATE_TRACEVIEW_LINKS_MAIN).exe endif -ifeq ($(BUILD_JAVA_ANALYZERS),yes) -OCAMLBUILD_CONFIG += $(JAVA_OCAMLBUILD_OPTIONS) -DEPENDENCIES += java -else -DEPENDENCIES += java_stubs -endif -ifeq ($(BUILD_C_ANALYZERS),yes) -DEPENDENCIES += clang clang_plugin unit/clang -else -DEPENDENCIES += clang_stubs unit/clang_stubs -endif ifeq ($(TEST),1) -INFER_CONFIG_TARGETS += $(INFERUNIT_MAIN).native +INFER_CONFIG_TARGETS += $(INFER_BUILD_DIR)/$(INFERUNIT_MAIN).exe endif -OCAML_BASE_SOURCES = \ - $(wildcard $(DEPENDENCIES:=/[a-zA-Z]*.ml) $(DEPENDENCIES:=/[a-zA-Z]*.mli) \ - $(DEPENDENCIES:=/[a-zA-Z]*.mll) $(DEPENDENCIES:=/[a-zA-Z]*.mly) \ - $(DEPENDENCIES:=/[a-zA-Z]*.re) $(DEPENDENCIES:=/[a-zA-Z]*.rei)) \ - $(STACKTREE_ATDGEN_STUBS) $(INFERPRINT_ATDGEN_STUBS) -OCAML_CONFIG_SOURCES = $(OCAML_BASE_SOURCES) +OCAML_SOURCES = \ + $(wildcard */[a-zA-Z]*.ml */[a-zA-Z]*.ml[ily]) \ + base/Version.ml $(STACKTREE_ATDGEN_STUBS) $(INFERPRINT_ATDGEN_STUBS) + ifeq ($(BUILD_C_ANALYZERS),yes) -OCAML_CONFIG_SOURCES += $(CLANG_ATDGEN_STUBS) $(CLANG_PLUGIN_MIRRORED_FILES) +OCAML_SOURCES += $(CLANG_ATDGEN_STUBS) $(CLANG_PLUGIN_MIRRORED_FILES) endif -OCAML_ALL_SOURCES = $(OCAML_BASE_SOURCES) $(CLANG_ATDGEN_STUBS) $(CLANG_PLUGIN_MIRRORED_FILES) .PHONY: all all: infer -SRC_BUILD_COMMON = base/Version.ml $(OCAML_CONFIG_SOURCES) +$(INFER_BUILD_DIR)/.ppx/ppx_compare/ppx.exe: jbuild jbuild-workspace $(OCAML_SOURCES) +# some voodoo to make jbuilder tolerate being run in parallel: force jbuilder to build its +# jbuild files and some files that have been seen to race otherwise + jbuilder build \ + $(JBUILDER_BUILD_DEFAULT)/.ppx/ppx_compare/ppx.exe \ + $(JBUILDER_BUILD_TEST)/.ppx/ppx_compare/ppx.exe + touch $@ + +SRC_BUILD_COMMON = $(INFER_BUILD_DIR)/.ppx/ppx_compare/ppx.exe $(OCAML_SOURCES) ifeq ($(BUILD_C_ANALYZERS),yes) SRC_BUILD_COMMON += $(CLANG_BINIOU_DICT) endif @@ -193,12 +129,10 @@ endif .PHONY: src_build_common src_build_common: $(SRC_BUILD_COMMON) -# single out infer.native as the source of truth for make, knowing that in fact several targets are +# single out infer.exe as the source of truth for make, knowing that in fact several targets are # produced by the build -$(INFER_BUILD_DIR)/$(INFER_MAIN).native: $(SRC_BUILD_COMMON) $(MAKEFILE_LIST) - $(MKDIR_P) $(BASE_BUILD_DIR) - $(OCAMLBUILD_CONFIG) -build-dir $(INFER_BUILD_DIR) \ - $(INFER_CONFIG_TARGETS) +$(INFER_BUILD_DIR)/$(INFER_MAIN).exe: $(SRC_BUILD_COMMON) $(MAKEFILE_LIST) + jbuilder build $(INFER_CONFIG_TARGETS) # let make know that the target is up-to-date even if ocamlbuild cached it $(QUIET)touch $@ @@ -210,59 +144,38 @@ $(INFER_BIN_ALIASES): Makefile $(QUIET)cd $(@D) && $(LN_S) -f infer $(@F) $(QUIET)touch $@ -$(INFER_BIN).native: $(INFER_BUILD_DIR)/$(INFER_MAIN).native $(INFER_BIN_ALIASES) - $(INSTALL_PROGRAM) $(INFER_BUILD_DIR)/$(INFER_MAIN).native $(INFER_BIN) - $(INSTALL_PROGRAM) $(INFER_BUILD_DIR)/$(INFER_MAIN).native $(INFER_BIN).native +$(INFER_BIN).exe: $(INFER_BUILD_DIR)/$(INFER_MAIN).exe $(INFER_BIN_ALIASES) + $(INSTALL_PROGRAM) $(INFER_BUILD_DIR)/$(INFER_MAIN).exe $(INFER_BIN) + $(INSTALL_PROGRAM) $(INFER_BUILD_DIR)/$(INFER_MAIN).exe $(INFER_BIN).exe ifeq ($(IS_FACEBOOK_TREE),yes) - $(INSTALL_PROGRAM) $(INFER_BUILD_DIR)/$(INFER_CREATE_TRACEVIEW_LINKS_MAIN).native \ + $(INSTALL_PROGRAM) $(INFER_BUILD_DIR)/$(INFER_CREATE_TRACEVIEW_LINKS_MAIN).exe \ $(INFER_CREATE_TRACEVIEW_LINKS_BIN) endif .PHONY: infer -infer: $(INFER_BIN).native +infer: $(INFER_BIN).exe -$(INFER_BUILD_DIR)/$(INFER_MAIN).byte: $(SRC_BUILD_COMMON) $(MAKEFILE_LIST) - $(MKDIR_P) $(BASE_BUILD_DIR) - $(OCAMLBUILD_CONFIG) -build-dir $(INFER_BUILD_DIR) \ - $(INFER_CONFIG_TARGETS:.native=.byte) +$(INFER_BUILD_DIR)/$(INFER_MAIN).bc: $(SRC_BUILD_COMMON) $(MAKEFILE_LIST) + jbuilder build $(INFER_CONFIG_TARGETS:.exe=.bc) $(QUIET)touch $@ -$(INFER_BIN).byte: $(INFER_BUILD_DIR)/$(INFER_MAIN).byte $(INFER_BIN_ALIASES) - $(INSTALL_PROGRAM) $(INFER_BUILD_DIR)/$(INFER_MAIN).byte $(INFER_BIN) - $(INSTALL_PROGRAM) $(INFER_BUILD_DIR)/$(INFER_MAIN).byte $(INFER_BIN).byte +$(INFER_BIN).bc: $(INFER_BUILD_DIR)/$(INFER_MAIN).bc $(INFER_BIN_ALIASES) + $(INSTALL_PROGRAM) $(INFER_BUILD_DIR)/$(INFER_MAIN).bc $(INFER_BIN) + $(INSTALL_PROGRAM) $(INFER_BUILD_DIR)/$(INFER_MAIN).bc $(INFER_BIN).bc ifeq ($(TEST),1) - $(INSTALL_PROGRAM) $(INFER_BUILD_DIR)/$(INFERUNIT_MAIN).byte $(INFERUNIT_BIN) + $(INSTALL_PROGRAM) $(INFER_BUILD_DIR)/$(INFERUNIT_MAIN).bc $(INFERUNIT_BIN) endif ifeq ($(IS_FACEBOOK_TREE),yes) - $(INSTALL_PROGRAM) $(INFER_BUILD_DIR)/$(INFER_CREATE_TRACEVIEW_LINKS_MAIN).byte \ + $(INSTALL_PROGRAM) $(INFER_BUILD_DIR)/$(INFER_CREATE_TRACEVIEW_LINKS_MAIN).bc \ $(INFER_CREATE_TRACEVIEW_LINKS_BIN) endif .PHONY: byte -byte: $(INFER_BIN).byte +byte: $(INFER_BIN).bc .PHONY: byte_no_install -byte_no_install: $(INFER_BUILD_DIR)/$(INFER_MAIN).byte - -# to build only the single module (and its dependencies) with extra flags execute: -# make MFLAGS= M=.cm{o,x} module -# for example, to build the assembly for the Ident module, execute -# make MFLAGS="-ocamlopt 'ocamlopt -S'" M=Ident.cmx module -M= -MFLAGS= - -.PHONY: module -module: $(SRC_BUILD_COMMON) $(OCAML_ALL_SOURCES) - $(MKDIR_P) $(BASE_BUILD_DIR) - $(OCAMLBUILD_ALL) -build-dir $(INFER_BUILD_DIR) \ - $(MFLAGS) \ - $(M) - -# to generate interface file.mli from implementation file.ml execute: -# make M=file mli -mli: - $(OCAMLFIND) ocamlc -package atdgen,oUnit,str,unix,yojson,zip $(addprefix -I $(INFER_BUILD_DIR),$(DEPENDENCIES)) -i $(M).ml > $(M).mli +byte_no_install: $(INFER_BUILD_DIR)/$(INFER_MAIN).bc roots:=Infer ifeq ($(IS_FACEBOOK_TREE),yes) @@ -270,8 +183,8 @@ roots += $(INFER_CREATE_TRACEVIEW_LINKS_MODULE) endif clusters:=base clang java IR -ml_src_files:=$(shell find $(DEPENDENCIES) -regex '.*\.ml\(i\)*') -inc_flags:=$(foreach dir,$(DEPENDENCIES),-I $(dir)) +ml_src_files:=$(shell find . -regex '.*\.ml\(i\)*') +inc_flags:=$(foreach dir,$(shell find . -type d),-I $(dir)) root_flags:=$(foreach root,$(roots),-r $(root)) cluster_flags:=$(foreach cluster,$(clusters),-c $(cluster)) @@ -279,7 +192,7 @@ mod_dep.dot: $(ml_src_files) $(MAKE) -C $(DEPENDENCIES_DIR)/ocamldot ocamldep.opt $(inc_flags) $(ml_src_files) \ | $(DEPENDENCIES_DIR)/ocamldot/ocamldot $(cluster_flags) $(root_flags) \ - | grep -v -e "\"IList\"\|\"Utils\"" \ + | grep -v -e "\"IList\"\|\"Utils\"\|\"IStd\"\|\"Infertop\"" \ > mod_dep.dot mod_dep.pdf: mod_dep.dot @@ -289,47 +202,19 @@ mod_dep.pdf: mod_dep.dot dsort: $(QUIET)ocamldep.opt -sort $(inc_flags) $(ml_src_files) -define to_ocaml_module -$(shell \ - echo $(basename $(1)) \ - | awk 'BEGIN { FS = "/"; OFS = "/" } ; {$$NF=toupper(substr($$NF,1,1))substr($$NF,2); print $$0}') -endef - -toplevel.mlpack: $(SRC_BUILD_COMMON) $(MAKEFILE_LIST) -# We need to pack all the infer modules into another module to avoid name clashes with some -# of them coming from ocaml libraries (Ident for example). To do that, we generate a .mlpack -# file containing namespaced modules. -# 1. we filter out roots because they execute code upon loading -# 2. each source file is converted to a module by capitalizing its first letter -# 3. we `echo` each module individually to avoid too long a command line -# 4. prevent race conditions by using a temp file - $(eval $@_tmp := $(shell mktemp $@.tmp.XXXX)) - $(foreach module,\ - $(filter-out $(foreach root,$(roots),%/$(root)),\ - $(foreach source,\ - $(filter-out unit/%,$(OCAML_CONFIG_SOURCES)),\ - $(call to_ocaml_module,$(source)))),\ - $(shell echo $(module) >> $($@_tmp))) - mv $($@_tmp) $@ - -$(INFER_BUILD_DIR)/toplevel.cmo: toplevel.mlpack - $(MKDIR_P) $(BASE_BUILD_DIR) - $(OCAMLBUILD_CONFIG) -build-dir $(INFER_BUILD_DIR) toplevel.cmo +$(INFER_BUILD_DIR)/infertop.bc: $(SRC_DIR)/infertop.ml $(SRC_BUILD_COMMON) $(MAKEFILE_LIST) + jbuilder build $@ $(QUIET)touch $@ .PHONY: toplevel -toplevel: $(INFER_BUILD_DIR)/toplevel.cmo +toplevel: $(INFER_BUILD_DIR)/infertop.bc .PHONY: checkCopyright checkCopyright: $(CHECKCOPYRIGHT_BIN) -$(CHECKCOPYRIGHT_BIN): $(CHECKCOPYRIGHT_MAIN).ml $(MAKEFILE_LIST) - $(MKDIR_P) $(BASE_BUILD_DIR) - $(OCAMLBUILD) -quiet -r -j $(NCPU) -build-dir $(BASE_BUILD_DIR)/checkCopyright \ - -cflags -g,-safe-string -lflags -g \ - -pkgs core,str -tag thread -use-ocamlfind $(CHECKCOPYRIGHT_MAIN).native - $(INSTALL_PROGRAM) \ - $(BASE_BUILD_DIR)/checkCopyright/$(CHECKCOPYRIGHT_MAIN).native $(CHECKCOPYRIGHT_BIN) +$(CHECKCOPYRIGHT_BIN): scripts/$(CHECKCOPYRIGHT_MAIN).ml $(MAKEFILE_LIST) + jbuilder build $(INFER_BUILD_DIR)/scripts/$(CHECKCOPYRIGHT_MAIN).exe + $(INSTALL_PROGRAM) $(INFER_BUILD_DIR)/scripts/$(CHECKCOPYRIGHT_MAIN).exe $(CHECKCOPYRIGHT_BIN) define gen_atdgen_rules # generate files using atdgen @@ -376,24 +261,30 @@ $(CLANG_BINIOU_DICT): $(CLANG_ATDGEN_STUB_ATD) | sort | uniq \ > $@ -base/Version.ml: base/Version.ml.in $(MAKEFILE_LIST) +jbuild jbuild-workspace base/Version.ml: $(MAKEFILE_LIST) TMPFILE=$$(mktemp $@.tmp.XXXX); \ INFER_GIT_COMMIT=$$(git --work-tree=$(ROOT_DIR) --git-dir=$(ROOT_DIR)/.git rev-parse --short HEAD || printf "unknown"); \ INFER_GIT_BRANCH=$$(git --work-tree=$(ROOT_DIR) --git-dir=$(ROOT_DIR)/.git rev-parse --abbrev-ref HEAD || printf "unknown"); \ sed \ + -e 's|@EXTRA_CFLAGS[@]|$(EXTRA_CFLAGS)|g' \ -e 's|@INFER_MAJOR[@]|$(INFER_MAJOR)|g' \ -e 's|@INFER_MINOR[@]|$(INFER_MINOR)|g' \ -e 's|@INFER_PATCH[@]|$(INFER_PATCH)|g' \ + -e 's|@IS_FACEBOOK_TREE[@]|$(IS_FACEBOOK_TREE)|g' \ -e 's|@IS_RELEASE_TREE[@]|$(IS_RELEASE_TREE)|g' \ -e "s|@INFER_GIT_COMMIT[@]|$$INFER_GIT_COMMIT|g" \ -e "s|@INFER_GIT_BRANCH[@]|$$INFER_GIT_BRANCH|g" \ -e "s|@BUILD_C_ANALYZERS[@]|$(BUILD_C_ANALYZERS)|g" \ -e "s|@BUILD_JAVA_ANALYZERS[@]|$(BUILD_JAVA_ANALYZERS)|g" \ + -e "s|@OPAMSWITCH[@]|$(OPAMSWITCH)|g" \ -e "s|@XCODE_SELECT[@]|$(XCODE_SELECT)|g" \ -e "s|@INFER_MAN_LAST_MODIFIED[@]|$(INFER_MAN_LAST_MODIFIED)|g" \ - $< > "$$TMPFILE"; \ + $@.in > "$$TMPFILE"; \ cat "$$TMPFILE" > $@; \ $(REMOVE) "$$TMPFILE" +jbuild: jbuild.in +jbuild-workspace: jbuild-workspace.in +base/Version.ml: base/Version.ml.in .PHONY: clean clean: @@ -401,13 +292,13 @@ clean: $(REMOVE) toplevel.mlpack $(REMOVE_DIR) $(BUILD_DIR) $(REMOVE) $(ETC_DIR)/clang_ast.dict - $(REMOVE) base/Version.ml - $(REMOVE) base/Version.ml.tmp.* + $(REMOVE) base/Version.ml jbuild jbuild-workspace + $(REMOVE) base/Version.ml.tmp.* jbuild.tmp.* jbuild-workspace.tmp.* $(REMOVE) backend/jsonbug_{j,t}.ml{,i} $(REMOVE) checkers/stacktree_{j,t}.ml{,i} # be a bit more aggressive than needed with what we remove here so that stale binaries that # only existed in previous versions get removed as well - $(REMOVE) $(BIN_DIR)/Infer* $(BIN_DIR)/infer-* $(INFER_BIN){,.byte,.native} $(INFER_BIN_ALIASES) \ + $(REMOVE) $(BIN_DIR)/Infer* $(BIN_DIR)/infer-* $(INFER_BIN){,.bc,.exe} $(INFER_BIN_ALIASES) \ $(INFERUNIT_BIN) $(CHECKCOPYRIGHT_BIN) $(REMOVE) $(INFER_CREATE_TRACEVIEW_LINKS_BIN) $(REMOVE) $(CLANG_PLUGIN_MIRROR)/* diff --git a/infer/src/README.md b/infer/src/README.md new file mode 100644 index 00000000000..4c15e27fff4 --- /dev/null +++ b/infer/src/README.md @@ -0,0 +1,8 @@ +The OCaml source files for infer live here. The Makefile is +responsible for building them, together with jbuild.in. + +If you make changes to the build process, also update .merlin +accordingly. For instance, if you want to add a new source directory +you will need to: +- add it to jbuild.in +- add it to .merlin diff --git a/infer/src/eradicate/modelTables.ml b/infer/src/eradicate/modelTables.ml index dc473519282..35d21e0df80 100644 --- a/infer/src/eradicate/modelTables.ml +++ b/infer/src/eradicate/modelTables.ml @@ -418,7 +418,7 @@ let mk_table list = List.iter ~f:(function v, pn_id -> Hashtbl.replace map pn_id v) list ; map -let this_file = __FILE__ +let this_file = Filename.basename __FILE__ let annotated_table_nullable = mk_table annotated_list_nullable diff --git a/infer/src/infertop.ml b/infer/src/infertop.ml new file mode 100644 index 00000000000..99491e89f56 --- /dev/null +++ b/infer/src/infertop.ml @@ -0,0 +1,9 @@ +(* + * Copyright (c) 2017 - 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. + *) +;; UTop_main.main () diff --git a/infer/src/jbuild-workspace.in b/infer/src/jbuild-workspace.in new file mode 100644 index 00000000000..bb5c3ebfa71 --- /dev/null +++ b/infer/src/jbuild-workspace.in @@ -0,0 +1,9 @@ +;; Do not let jbuilder generate .merlin files because it gets it +;; wrong. This is mainly due to the fact that source files live in +;; sub-directories and are copied into _build/ by our jbuild. jbuilder +;; wants to write "S ." in .merlin but that makes merlin use the +;; copied source files in _build/ instead of only using the original +;; files. + +(context ((switch @OPAMSWITCH@) (name default))) +(context ((switch @OPAMSWITCH@) (name test))) diff --git a/infer/src/jbuild.in b/infer/src/jbuild.in new file mode 100644 index 00000000000..9495e18f22d --- /dev/null +++ b/infer/src/jbuild.in @@ -0,0 +1,209 @@ +(* -*- tuareg -*- *) +(* use strings so that it looks like OCaml even before substituting, e.g. to use ocamlformat *) + +let is_yes = String.equal "yes" + +let clang = is_yes "@BUILD_C_ANALYZERS@" + +let java = is_yes "@BUILD_JAVA_ANALYZERS@" + +let facebook = is_yes "@IS_FACEBOOK_TREE@" + +let extra_cflags = if "@EXTRA_CFLAGS" = "" then [] else ["@EXTRA_CFLAGS@"] + +let ( ^/ ) = Filename.concat + +(* Compute the list of all the source files. + + This is needed until jbuilder supports finding sources in subdirectories somehow + (https://github.com/janestreet/jbuilder/issues/34). *) + +let sources = + let src_dirs = + let src_dir = "." in + (* if you update the list of source directories, do not forget to also update .merlin *) + src_dir + :: ( if facebook then + (* do not use the symlinks in src/facebook/: jbuilder will not detect that the files have changed if they are hidden behind a symlink *) + "../../facebook/skel/infer/src/facebook" + else src_dir ^/ "opensource" ) + :: List.map (Filename.concat src_dir) + ( ( if clang then ["clang"; "clang_plugin"; ("unit" ^/ "clang")] + else ["clang_stubs"; ("unit" ^/ "clang_stubs")] ) + @ [ (if java then "java" else "java_stubs") + ; "absint" + ; "backend" + ; "base" + ; "bufferoverrun" + ; "checkers" + ; "eradicate" + ; "harness" + ; "integration" + ; "IR" + ; "labs" + ; "quandary" + ; "unit" ] ) + in + let files = ref [] in + let ml_suffixes = [".ml"; ".mli"; ".mll"; ".mly"] in + let add_file dir file = + if List.exists (Filename.check_suffix file) ml_suffixes then files := (dir ^/ file) :: !files + in + let one_dir dir = + (* absolute path so that running jbuilder from any subdirectory (in particular from src/Makefile) + points at the right original files in ocamlc's error messages *) + let abs_dir = Sys.getcwd () ^/ dir in + Array.iter (add_file abs_dir) (Sys.readdir dir) + in + List.iter one_dir src_dirs ; !files + +let common_cflags = + let fatal_warnings = "+3+5+6+8+10+11+12+18+19+20+21+23+26+29+27+33+34+35+37+38+39+50+52+57" in + let warnings = fatal_warnings ^ "-4-9-32-40-41-42-45-48" in + let common_flags = + [ "-g" + ; "-short-paths" + ; "-safe-string" + ; "-principal" + ; "-strict-formats" + ; "-strict-sequence" + ; "-bin-annot" + ; "-w" + ; warnings ] + in + match Jbuild_plugin.V1.context with + | "test" + -> "-warn-error" :: fatal_warnings :: common_flags + | "default" + -> common_flags + | ctx + -> invalid_arg ("unknown context: " ^ ctx) + +(** Make a library that includes just IStd and IList. These files have different compilation flags + than infer (they don't include IStd!). *) +let inferBaseStdlib = "InferBaseStdlib" + +let iStd = "IStd" + +let inferBaseStdlib_modules = ["IList"; iStd] + +let inferBaseStdlib_cflags = common_cflags + +(** Generated code (eg, from atdgen) is also compiled using different flags, so pack it in a + separated library. *) +let inferGenerated = "InferGenerated" + +let inferGenerated_modules = + ( if clang then + [ "Clang_ast_b" + ; "Clang_ast_j" + ; "Clang_ast_t" + ; "Clang_ast_v" + ; "Clang_ast_proj" + ; "Clang_ast_types" + ; "Clang_ast_visit" ] + else [] ) + @ ["Jsonbug_j"; "Jsonbug_t"; "Stacktree_j"; "Stacktree_t"] + +let inferGenerated_cflags = common_cflags @ ["-w"; "-27-32-34-35-39"] + +let infer_binaries = + ["infer"; "inferunit"] @ if facebook then ["InferCreateTraceViewLinks"] else [] + +let infer_cflags = + common_cflags @ ["-open"; inferBaseStdlib; "-open"; iStd; "-open"; inferGenerated] + +let common_libraries = + (if java then ["javalib"; "ptrees"; "sawja"] else []) + @ [ "ANSITerminal" + ; "atdgen" + ; "cmdliner" + ; "core" + ; "extlib" + ; "oUnit" + ; "parmap" + ; "str" + ; "unix" + ; "xmlm" + ; "yojson" + ; "zip" ] + +(** Return the best copy action for a given [source] file. That is, when possibly, use "copy#" so + that jump-to-defition and compiler errors go to the right original source file, otherwise do a + normal copy. *) +let copy_action_of_source source = + if Filename.check_suffix source ".mly" then + (* menhir doesn't support '# 1 ""' directives at the start of the file inserted by + copy# actions *) + "copy" + else "copy#" + +(** The build stanzas to be passed to jbuilder *) +let stanzas = + ( if clang then + ["(ocamllex (types_lexer ctl_lexer))"; "(menhir ((modules (types_parser ctl_parser))))"] + else [] ) + @ [ Format.sprintf + {| +(library + ((name %s) + (flags (%s)) + (modules (%s)) + (libraries (atdgen)) + )) +|} + inferGenerated (String.concat " " inferGenerated_cflags) + (String.concat " " inferGenerated_modules) + ; Format.sprintf + {| +(library + ((name %s) + (flags (%s)) + (modules (%s)) + (libraries (%s)) + )) +|} + inferBaseStdlib (String.concat " " inferBaseStdlib_cflags) + (String.concat " " inferBaseStdlib_modules) (String.concat " " common_libraries) + ; Format.sprintf + {| +(library + ((name InferModules) + (flags (%s)) + (libraries (%s %s %s)) + (modules (:standard \ %s infertop %s %s)) + (preprocess (pps (ppx_compare))) + )) +|} + (String.concat " " infer_cflags) (String.concat " " common_libraries) inferBaseStdlib + inferGenerated (String.concat " " infer_binaries) + (String.concat " " inferBaseStdlib_modules) (String.concat " " inferGenerated_modules) + ; Format.sprintf + {| +(executables + ((names (%s)) + (flags (%s -open InferModules)) + (libraries (InferModules)) + (modules (%s)) + (preprocess (pps (ppx_compare))) + )) +|} + (String.concat " " infer_binaries) (String.concat " " infer_cflags) + (String.concat " " infer_binaries) + ; Format.sprintf + {| +(executable + ((name infertop) + (flags (%s)) + (libraries (utop InferModules)) + (modules (:standard \ %s)) + (link_flags (-linkall -warn-error -31)))) +|} + (String.concat " " infer_cflags) (String.concat " " infer_binaries) ] + @ List.map + (fun source -> + Printf.sprintf "(rule (%s %s %s))" (copy_action_of_source source) source + (Filename.basename source)) + sources + +;; String.concat "\n" stanzas |> Jbuild_plugin.V1.send diff --git a/infer/src/scripts/checkCopyright.ml b/infer/src/scripts/checkCopyright.ml index 76687c0cd78..62cba5dce33 100644 --- a/infer/src/scripts/checkCopyright.ml +++ b/infer/src/scripts/checkCopyright.ml @@ -7,7 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. *) -open! Core.Std +open! Core module F = Format let copyright_modified_exit_code = 1 @@ -193,7 +193,7 @@ let copyright_has_changed mono fb_year com_style prefix cstart cend lines_arr = let update_file fname mono fb_year com_style prefix cstart cend lines_arr = try - let cout = open_out fname in + let cout = Out_channel.create fname in let fmt = F.formatter_of_out_channel cout in for i = 0 to cstart - 1 do F.fprintf fmt "%s@." lines_arr.(i) done ; pp_copyright mono fb_year com_style fmt prefix ; diff --git a/infer/src/scripts/jbuild b/infer/src/scripts/jbuild new file mode 100644 index 00000000000..b6de5ab89ee --- /dev/null +++ b/infer/src/scripts/jbuild @@ -0,0 +1,7 @@ +(jbuild_version 1) + +(executable + ((name checkCopyright) + (flags (:standard -g -short-paths -safe-string -principal -strict-formats -strict-sequence -bin-annot -w +3+5+6+8+10+11+12+18+19+20+21+23+26+29+27+33+34+35+37+38+39+50+52+57-4-9-32-40-41-42-45-48)) + (libraries (core str)) + )) diff --git a/infer/tests/build_systems/buck_flavors_deterministic/Makefile b/infer/tests/build_systems/buck_flavors_deterministic/Makefile index 93dfdb302d4..c6b291e42e6 100644 --- a/infer/tests/build_systems/buck_flavors_deterministic/Makefile +++ b/infer/tests/build_systems/buck_flavors_deterministic/Makefile @@ -45,6 +45,9 @@ differences.exp.test: $(CLANG_DEPS) $(SOURCES) $(MAKEFILE_LIST) ) $(QUIET)$(REMOVE) capture_hash-1.sha capture_hash-2.sha +.PHONY: print +print: differences.exp.test + .PHONY: test test: differences.exp.test $(QUIET)diff -u differences.exp $< diff --git a/infer/tests/build_systems/infertop/Makefile b/infer/tests/build_systems/infertop/Makefile new file mode 100644 index 00000000000..f52aaa8f85c --- /dev/null +++ b/infer/tests/build_systems/infertop/Makefile @@ -0,0 +1,31 @@ +# Copyright (c) 2017 - 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. + +TESTS_DIR = ../.. +ROOT_DIR = $(TESTS_DIR)/../.. + +include $(TESTS_DIR)/base.make + +toplevel.exp.test: $(BUILD_DIR)/test/infertop.bc + $(QUIET)$(call silent_on_success,Testing infer OCaml REPL,\ + TOPLEVEL_DIR=$(BUILD_DIR)/test $(SCRIPT_DIR)/infer_repl \ + $(INFER_DIR)/tests/repl/infer_batch_script.mltop > $@) + +.PHONY: print +print: toplevel.exp.test + +.PHONY: test +test: toplevel.exp.test + $(QUIET)diff -u toplevel.exp $< + +.PHONY: replace +replace: toplevel.exp.test + $(QUIET)$(COPY) $< toplevel.exp + +.PHONY: clean +clean: + $(QUIET)$(REMOVE) toplevel.exp.test diff --git a/infer/tests/build_systems/infertop/toplevel.exp b/infer/tests/build_systems/infertop/toplevel.exp new file mode 100644 index 00000000000..5272fa1cc09 --- /dev/null +++ b/infer/tests/build_systems/infertop/toplevel.exp @@ -0,0 +1,11 @@ +Findlib has been successfully loaded. Additional directives: + #require "package";; to load a package + #list;; to list the available packages + #camlp4o;; to load camlp4 (standard syntax) + #camlp4r;; to load camlp4 (revised syntax) + #predicates "p,q,...";; to set these predicates + Topfind.reset();; to force that packages will be reloaded + #thread;; to enable threads + +n$2 +false diff --git a/infer/tests/codetoanalyze/java/eradicate/issues.exp b/infer/tests/codetoanalyze/java/eradicate/issues.exp index c5b294efec0..811cd53a5b9 100644 --- a/infer/tests/codetoanalyze/java/eradicate/issues.exp +++ b/infer/tests/codetoanalyze/java/eradicate/issues.exp @@ -27,11 +27,11 @@ codetoanalyze/java/eradicate/InconsistentSubclassAnnotation.java, String Inconsi codetoanalyze/java/eradicate/InconsistentSubclassAnnotation.java, SubclassExample$T SubclassExample$B.foo(), 0, ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION, [Method `SubclassExample$B.foo()` is annotated with `@Nullable` but overrides unannotated method `SubclassExample$A.foo()`.] codetoanalyze/java/eradicate/InconsistentSubclassAnnotation.java, SubclassExample$T SubclassExample$C.baz(), 0, ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION, [Method `SubclassExample$C.baz()` is annotated with `@Nullable` but overrides unannotated method `SubclassExample$I.baz()`.] codetoanalyze/java/eradicate/InconsistentSubclassAnnotation.java, void SubclassExample$D.deref(SubclassExample$T), 0, ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION, [First parameter `t` of method `SubclassExample$D.deref(...)` is not `@Nullable` but is declared `@Nullable`in the parent class method `SubclassExample$A.deref(...)`.] -codetoanalyze/java/eradicate/LibraryCalls.java, String LibraryCalls.badAtomicReferenceDereference(AtomicReference), 1, ERADICATE_NULL_METHOD_CALL, [origin,The value of `ref.get()` in the call to `toString()` could be null. (Origin: call to get() modelled in eradicate/modelTables.ml at line 37)] -codetoanalyze/java/eradicate/LibraryCalls.java, String LibraryCalls.badPhantomReferenceDereference(PhantomReference), 1, ERADICATE_NULL_METHOD_CALL, [origin,The value of `ref.get()` in the call to `toString()` could be null. (Origin: call to get() modelled in eradicate/modelTables.ml at line 29)] -codetoanalyze/java/eradicate/LibraryCalls.java, String LibraryCalls.badReferenceDereference(Reference), 1, ERADICATE_NULL_METHOD_CALL, [origin,The value of `ref.get()` in the call to `toString()` could be null. (Origin: call to get() modelled in eradicate/modelTables.ml at line 21)] -codetoanalyze/java/eradicate/LibraryCalls.java, String LibraryCalls.badSoftReferenceDereference(SoftReference), 1, ERADICATE_NULL_METHOD_CALL, [origin,The value of `ref.get()` in the call to `toString()` could be null. (Origin: call to get() modelled in eradicate/modelTables.ml at line 33)] -codetoanalyze/java/eradicate/LibraryCalls.java, String LibraryCalls.badWeakReferenceDereference(WeakReference), 1, ERADICATE_NULL_METHOD_CALL, [origin,The value of `ref.get()` in the call to `toString()` could be null. (Origin: call to get() modelled in eradicate/modelTables.ml at line 25)] +codetoanalyze/java/eradicate/LibraryCalls.java, String LibraryCalls.badAtomicReferenceDereference(AtomicReference), 1, ERADICATE_NULL_METHOD_CALL, [origin,The value of `ref.get()` in the call to `toString()` could be null. (Origin: call to get() modelled in modelTables.ml at line 37)] +codetoanalyze/java/eradicate/LibraryCalls.java, String LibraryCalls.badPhantomReferenceDereference(PhantomReference), 1, ERADICATE_NULL_METHOD_CALL, [origin,The value of `ref.get()` in the call to `toString()` could be null. (Origin: call to get() modelled in modelTables.ml at line 29)] +codetoanalyze/java/eradicate/LibraryCalls.java, String LibraryCalls.badReferenceDereference(Reference), 1, ERADICATE_NULL_METHOD_CALL, [origin,The value of `ref.get()` in the call to `toString()` could be null. (Origin: call to get() modelled in modelTables.ml at line 21)] +codetoanalyze/java/eradicate/LibraryCalls.java, String LibraryCalls.badSoftReferenceDereference(SoftReference), 1, ERADICATE_NULL_METHOD_CALL, [origin,The value of `ref.get()` in the call to `toString()` could be null. (Origin: call to get() modelled in modelTables.ml at line 33)] +codetoanalyze/java/eradicate/LibraryCalls.java, String LibraryCalls.badWeakReferenceDereference(WeakReference), 1, ERADICATE_NULL_METHOD_CALL, [origin,The value of `ref.get()` in the call to `toString()` could be null. (Origin: call to get() modelled in modelTables.ml at line 25)] codetoanalyze/java/eradicate/NullFieldAccess.java, Object NullFieldAccess.arrayAccess(), 1, ERADICATE_NULL_FIELD_ACCESS, [origin,Object `NullFieldAccess.objects` could be null when accessing element at index `0`. (Origin: field NullFieldAccess.objects at line 63)] codetoanalyze/java/eradicate/NullFieldAccess.java, int NullFieldAccess.arrayLength(), 1, ERADICATE_NULL_FIELD_ACCESS, [origin,Object `NullFieldAccess.objects` could be null when accessing field `length`. (Origin: field NullFieldAccess.objects at line 59)] codetoanalyze/java/eradicate/NullFieldAccess.java, int NullFieldAccess.useInterface(NullFieldAccess$I), 2, ERADICATE_NULL_FIELD_ACCESS, [origin,Object `c` could be null when accessing field `NullFieldAccess$C.n`. (Origin: field NullFieldAccess$I.c at line 52)] @@ -39,11 +39,11 @@ codetoanalyze/java/eradicate/NullFieldAccess.java, int NullFieldAccess.useX(), 2 codetoanalyze/java/eradicate/NullFieldAccess.java, int NullFieldAccess.useZ(), 2, ERADICATE_NULL_FIELD_ACCESS, [origin,Object `c` could be null when accessing field `NullFieldAccess$C.n`. (Origin: field NullFieldAccess.z at line 47)] codetoanalyze/java/eradicate/NullMethodCall.java, int NullMethodCall$Inner.outerField(), 2, ERADICATE_NULL_METHOD_CALL, [origin,The value of `s` in the call to `length()` could be null. (Origin: field NullMethodCall.fld at line 71)] codetoanalyze/java/eradicate/NullMethodCall.java, int NullMethodCall$Inner.outerPrivateField(), 2, ERADICATE_NULL_METHOD_CALL, [origin,The value of `s` in the call to `length()` could be null. (Origin: field NullMethodCall.pfld at line 82)] -codetoanalyze/java/eradicate/NullMethodCall.java, int NullMethodCall.testSystemGetenvBad(), 2, ERADICATE_NULL_METHOD_CALL, [origin,The value of `envValue` in the call to `length()` could be null. (Origin: call to getenv(...) modelled in eradicate/modelTables.ml at line 243)] +codetoanalyze/java/eradicate/NullMethodCall.java, int NullMethodCall.testSystemGetenvBad(), 2, ERADICATE_NULL_METHOD_CALL, [origin,The value of `envValue` in the call to `length()` could be null. (Origin: call to getenv(...) modelled in modelTables.ml at line 243)] codetoanalyze/java/eradicate/NullMethodCall.java, void NullMethodCall.callOnNull(), 2, ERADICATE_NULL_METHOD_CALL, [origin,The value of `s` in the call to `length()` could be null. (Origin: null constant at line 22)] codetoanalyze/java/eradicate/NullMethodCall.java, void NullMethodCall.testExceptionPerInstruction(int), 6, ERADICATE_NULL_METHOD_CALL, [origin,The value of `s` in the call to `length()` could be null. (Origin: null constant at line 183)] codetoanalyze/java/eradicate/NullMethodCall.java, void NullMethodCall.testFieldAssignmentIfThenElse(String), 2, ERADICATE_NULL_METHOD_CALL, [origin,The value of `s` in the call to `length()` could be null. (Origin: null constant at line 174)] -codetoanalyze/java/eradicate/NullMethodCall.java, void NullMethodCall.testSystemGetPropertyReturn(), 2, ERADICATE_NULL_METHOD_CALL, [origin,The value of `s` in the call to `length()` could be null. (Origin: call to getProperty(...) modelled in eradicate/modelTables.ml at line 238)] +codetoanalyze/java/eradicate/NullMethodCall.java, void NullMethodCall.testSystemGetPropertyReturn(), 2, ERADICATE_NULL_METHOD_CALL, [origin,The value of `s` in the call to `length()` could be null. (Origin: call to getProperty(...) modelled in modelTables.ml at line 238)] codetoanalyze/java/eradicate/ParameterNotNullable.java, ParameterNotNullable$ConstructorCall.(ParameterNotNullable,int), 1, ERADICATE_PARAMETER_NOT_NULLABLE, [origin,`ParameterNotNullable$ConstructorCall(...)` needs a non-null value in parameter 2 but argument `null` can be null. (Origin: null constant at line 102)] codetoanalyze/java/eradicate/ParameterNotNullable.java, String ParameterNotNullable.testSystemGetPropertyArgument(), 1, ERADICATE_PARAMETER_NOT_NULLABLE, [origin,`getProperty(...)` needs a non-null value in parameter 1 but argument `null` can be null. (Origin: null constant at line 71)] codetoanalyze/java/eradicate/ParameterNotNullable.java, String ParameterNotNullable.testSystemGetenvBad(), 1, ERADICATE_PARAMETER_NOT_NULLABLE, [origin,`getenv(...)` needs a non-null value in parameter 1 but argument `null` can be null. (Origin: null constant at line 76)] @@ -67,4 +67,4 @@ codetoanalyze/java/eradicate/ReturnNotNullable.java, String ReturnNotNullable.re codetoanalyze/java/eradicate/ReturnNotNullable.java, String ReturnNotNullable.returnNullable(String), 0, ERADICATE_RETURN_NOT_NULLABLE, [Method `returnNullable(...)` may return null but it is not annotated with `@Nullable`. (Origin: method parameter s)] codetoanalyze/java/eradicate/ReturnNotNullable.java, String ReturnNotNullable.return_null_in_catch(), 0, ERADICATE_RETURN_NOT_NULLABLE, [origin,Method `return_null_in_catch()` may return null but it is not annotated with `@Nullable`. (Origin: null constant at line 111)] codetoanalyze/java/eradicate/ReturnNotNullable.java, String ReturnNotNullable.return_null_in_catch_after_throw(), 0, ERADICATE_RETURN_NOT_NULLABLE, [origin,Method `return_null_in_catch_after_throw()` may return null but it is not annotated with `@Nullable`. (Origin: null constant at line 123)] -codetoanalyze/java/eradicate/ReturnNotNullable.java, URL ReturnNotNullable.getResourceNullable(Class,String), 0, ERADICATE_RETURN_NOT_NULLABLE, [origin,Method `getResourceNullable(...)` may return null but it is not annotated with `@Nullable`. (Origin: call to getResource(...) modelled in eradicate/modelTables.ml at line 128)] +codetoanalyze/java/eradicate/ReturnNotNullable.java, URL ReturnNotNullable.getResourceNullable(Class,String), 0, ERADICATE_RETURN_NOT_NULLABLE, [origin,Method `getResourceNullable(...)` may return null but it is not annotated with `@Nullable`. (Origin: call to getResource(...) modelled in modelTables.ml at line 128)] diff --git a/opam b/opam index 9a042c39787..fe3513645fe 100644 --- a/opam +++ b/opam @@ -34,6 +34,7 @@ depends: [ "ctypes" {>="0.9.2"} "extlib-compat" "javalib" {>="2.3.3"} + "jbuilder" {build & >="1.0+beta11"} "ocamlfind" {build} "ounit" {="2.0.0"} "parmap" {>="1.0-rc8"} diff --git a/scripts/infer_repl b/scripts/infer_repl index 87cc921e2a9..654adc512d0 100755 --- a/scripts/infer_repl +++ b/scripts/infer_repl @@ -1,20 +1,25 @@ #!/bin/bash -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +## Wrapper around infertop. To build infertop, run `make toplevel`. -# where to find toplevel.cmo, can be overridden -TOPLEVEL_DIR=${TOPLEVEL_DIR:-"$SCRIPT_DIR"/../infer/_build/infer} +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -# how to load the toplevel; can be overridden (with "ocaml" for example) -INFER_REPL_BINARY=${INFER_REPL_BINARY:-"utop"} +# where to find toplevel.bc, can be overridden +TOPLEVEL_DIR=${TOPLEVEL_DIR:-"$SCRIPT_DIR"/../infer/src/_build/default} -# to build new toplevel, run `make toplevel` -# -init option is used only in interactive mode -# in batch mode, scripts need to import toplevel_init themselves +# The -init option is effective only in interactive mode. +# In batch mode, scripts need to import toplevel_init themselves. # It can be done by adding #use "toplevel_init";; to the beginning # of a script. # NOTE: $SCRIPT_DIR is added search path for batch scripts # so they can be located anywhere and still find toplevel_init # file. In interactive mode $SCRIPT_DIR isn't needed -"$INFER_REPL_BINARY" -init "$SCRIPT_DIR"/toplevel_init -I "$TOPLEVEL_DIR" -I "$SCRIPT_DIR" $@ +set -x +# infertop expects to be run from where jbuild is located +cd "$SCRIPT_DIR"/../infer +"$TOPLEVEL_DIR"/infertop.bc \ + -init "$SCRIPT_DIR"/toplevel_init \ + -I "$TOPLEVEL_DIR" \ + -I "$SCRIPT_DIR" \ + "$@" diff --git a/scripts/toplevel_init b/scripts/toplevel_init index ff5a83ec36a..ddaad8650c7 100644 --- a/scripts/toplevel_init +++ b/scripts/toplevel_init @@ -1,3 +1,5 @@ +(* to be used with infertop, infer's custom toplevel *) + (* load dependencies *) #use "topfind";; #thread;; @@ -10,6 +12,5 @@ #require "xmlm";; #require "ANSITerminal";; -(* load infer code *) -#load_rec "toplevel.cmo";; -open Toplevel;; +open InferModules;; +open InferBaseStdlib;;