diff --git a/infer/man/man1/infer-analyze.txt b/infer/man/man1/infer-analyze.txt index 219e01fd00..75746c2bac 100644 --- a/infer/man/man1/infer-analyze.txt +++ b/infer/man/man1/infer-analyze.txt @@ -17,8 +17,10 @@ OPTIONS indirectly, another method annotated with `@B`. Besides the custom pairs, it is also possible to enable some built-in checks, such as `@PerformanceCritical` reaching `@Expensive` or `@NoAllocation` - reaching `new`. See flags starting with - `--annotation-reachability`. (Conversely: + reaching `new`. It is also possible to model methods as if they + were annotated, using regular expressions. This should also work + in languages where there are no annotations. See flags starting + with `--annotation-reachability`. (Conversely: --no-annotation-reachability) --annotation-reachability-only diff --git a/infer/man/man1/infer-full.txt b/infer/man/man1/infer-full.txt index aa8e3a2c23..ab1cb19505 100644 --- a/infer/man/man1/infer-full.txt +++ b/infer/man/man1/infer-full.txt @@ -65,8 +65,10 @@ OPTIONS indirectly, another method annotated with `@B`. Besides the custom pairs, it is also possible to enable some built-in checks, such as `@PerformanceCritical` reaching `@Expensive` or `@NoAllocation` - reaching `new`. See flags starting with - `--annotation-reachability`. (Conversely: + reaching `new`. It is also possible to model methods as if they + were annotated, using regular expressions. This should also work + in languages where there are no annotations. See flags starting + with `--annotation-reachability`. (Conversely: --no-annotation-reachability) See also infer-analyze(1). diff --git a/infer/man/man1/infer.txt b/infer/man/man1/infer.txt index 849764030b..65bc58a110 100644 --- a/infer/man/man1/infer.txt +++ b/infer/man/man1/infer.txt @@ -65,8 +65,10 @@ OPTIONS indirectly, another method annotated with `@B`. Besides the custom pairs, it is also possible to enable some built-in checks, such as `@PerformanceCritical` reaching `@Expensive` or `@NoAllocation` - reaching `new`. See flags starting with - `--annotation-reachability`. (Conversely: + reaching `new`. It is also possible to model methods as if they + were annotated, using regular expressions. This should also work + in languages where there are no annotations. See flags starting + with `--annotation-reachability`. (Conversely: --no-annotation-reachability) See also infer-analyze(1). diff --git a/infer/src/backend/registerCheckers.ml b/infer/src/backend/registerCheckers.ml index 36fef05a54..86b55c14cc 100644 --- a/infer/src/backend/registerCheckers.ml +++ b/infer/src/backend/registerCheckers.ml @@ -202,7 +202,7 @@ let all_checkers = (let annot_reach = interprocedural Payloads.Fields.annot_map AnnotationReachability.checker in - [(annot_reach, Java); (annot_reach, Clang)] ) } + [(annot_reach, Erlang); (annot_reach, Java)] ) } ; { checker= ConfigImpactAnalysis ; callbacks= (let checker = diff --git a/infer/src/base/Checker.ml b/infer/src/base/Checker.ml index 72ce002001..ab58ad03fb 100644 --- a/infer/src/base/Checker.ml +++ b/infer/src/base/Checker.ml @@ -84,13 +84,15 @@ let config_unsafe checker = | AnnotationReachability -> { id= "annotation-reachability" ; kind= UserFacing {title= "Annotation Reachability"; markdown_body= ""} - ; support= mk_support_func ~java:Support () + ; support= mk_support_func ~erlang:Support ~java:Support () ; short_documentation= "Given pairs of source and sink annotations, e.g. `@A` and `@B`, this checker will warn \ whenever some method annotated with `@A` calls, directly or indirectly, another method \ annotated with `@B`. Besides the custom pairs, it is also possible to enable some \ built-in checks, such as `@PerformanceCritical` reaching `@Expensive` or \ - `@NoAllocation` reaching `new`. See flags starting with `--annotation-reachability`." + `@NoAllocation` reaching `new`. It is also possible to model methods as if they were \ + annotated, using regular expressions. This should also work in languages where there \ + are no annotations. See flags starting with `--annotation-reachability`." ; cli_flags= Some {deprecated= []; show_in_help= true} ; enabled_by_default= false ; activates= [] } diff --git a/infer/src/checkers/annotationReachability.ml b/infer/src/checkers/annotationReachability.ml index 2d4a8b8f5f..2a006082bf 100644 --- a/infer/src/checkers/annotationReachability.ml +++ b/infer/src/checkers/annotationReachability.ml @@ -93,18 +93,24 @@ let parse_custom_models () = let check_attributes check tenv pname = - let proc_has_attribute = Annotations.pname_has_return_annot pname check in - let class_has_attribute = - ( if Config.annotation_reachability_apply_superclass_annotations then - PatternMatch.Java.check_class_attributes - else PatternMatch.Java.check_current_class_attributes ) - check tenv pname - in - class_has_attribute || proc_has_attribute + match pname with + | Procname.Java _ -> + let proc_has_attribute = Annotations.pname_has_return_annot pname check in + let class_has_attribute = + ( if Config.annotation_reachability_apply_superclass_annotations then + PatternMatch.Java.check_class_attributes + else PatternMatch.Java.check_current_class_attributes ) + check tenv pname + in + class_has_attribute || proc_has_attribute + | _ -> + false let check_modeled_annotation models annot pname = - let method_name = Procname.to_string ~verbosity:FullNameOnly pname in + let method_name = + Procname.to_string ~verbosity:(if Procname.is_erlang pname then Verbose else FullNameOnly) pname + in Option.exists (String.Map.find models annot.Annot.class_name) ~f:(fun methods -> List.exists methods ~f:(fun r -> Str.string_match r method_name 0) ) diff --git a/infer/tests/codetoanalyze/erlang/annotreach/.inferconfig b/infer/tests/codetoanalyze/erlang/annotreach/.inferconfig new file mode 100644 index 0000000000..2d9141c750 --- /dev/null +++ b/infer/tests/codetoanalyze/erlang/annotreach/.inferconfig @@ -0,0 +1,15 @@ +{ + "annotation-reachability": true, + "annotation-reachability-custom-pairs": [ + { + "sources": ["Source"], + "sinks": ["Sink"], + "sanitizers": ["Sanitizer"] + } + ], + "annotation-reachability-custom-models": { + "Source": [".*:.*source.*/0"], + "Sink": [".*:.*sink.*/0"], + "Sanitizer": [".*:.*sanitizer.*/0"] + } +} diff --git a/infer/tests/codetoanalyze/erlang/annotreach/Makefile b/infer/tests/codetoanalyze/erlang/annotreach/Makefile new file mode 100644 index 0000000000..49f26eddca --- /dev/null +++ b/infer/tests/codetoanalyze/erlang/annotreach/Makefile @@ -0,0 +1,18 @@ +# Copyright (c) Facebook, Inc. and its affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +TESTS_DIR = ../../.. + +INFER_OPTIONS = \ + --annotation-reachability-only --debug-exceptions \ + --project-root $(TESTS_DIR) --print-types + +INFERPRINT_OPTIONS = --issues-tests + +SOURCES = $(wildcard *.erl) + +include $(TESTS_DIR)/erlc.make + +infer-out/report.json: $(MAKEFILE_LIST) diff --git a/infer/tests/codetoanalyze/erlang/annotreach/issues.exp b/infer/tests/codetoanalyze/erlang/annotreach/issues.exp new file mode 100644 index 0000000000..9ceccd7524 --- /dev/null +++ b/infer/tests/codetoanalyze/erlang/annotreach/issues.exp @@ -0,0 +1,2 @@ +codetoanalyze/erlang/annotreach/reach.erl, test_source2_Bad/0, 0, CHECKERS_ANNOTATION_REACHABILITY_ERROR, no_bucket, ERROR, [Method test_source2_Bad/0, marked as source @Source,calls sink/0,sink/0 defined here, marked as sink @Sink] +codetoanalyze/erlang/annotreach/reach.erl, test_source4_Bad/0, 0, CHECKERS_ANNOTATION_REACHABILITY_ERROR, no_bucket, ERROR, [Method test_source4_Bad/0, marked as source @Source,calls not_sani_tizer/0,not_sani_tizer/0 defined here,calls sink/0,sink/0 defined here, marked as sink @Sink] diff --git a/infer/tests/codetoanalyze/erlang/annotreach/reach.erl b/infer/tests/codetoanalyze/erlang/annotreach/reach.erl new file mode 100644 index 0000000000..5b6b1f7238 --- /dev/null +++ b/infer/tests/codetoanalyze/erlang/annotreach/reach.erl @@ -0,0 +1,33 @@ +% Copyright (c) Facebook, Inc. and its affiliates. +% +% This source code is licensed under the MIT license found in the +% LICENSE file in the root directory of this source tree. + +-module(reach). + +-export([ + test_source1_Ok/0, + test_source2_Bad/0, + test_source3_Ok/0, + test_source4_Bad/0, test_source5_Ok/0 +]). + +sink() -> ok. + +not_si_nk() -> ok. + +not_sink_because_arity(X) -> X. + +sanitizer() -> sink(). + +not_sani_tizer() -> sink(). + +test_source1_Ok() -> not_si_nk(). + +test_source2_Bad() -> sink(). + +test_source3_Ok() -> sanitizer(). + +test_source4_Bad() -> not_sani_tizer(). + +test_source5_Ok() -> not_sink_because_arity(1).