From dfca0cdcf7497cb1144fcf9c3bb5e7663f65024d Mon Sep 17 00:00:00 2001 From: Dulma Churchill Date: Mon, 12 Jun 2017 04:55:35 -0700 Subject: [PATCH] [linters] Introduce whitelist and blacklist path, also multiple paths Reviewed By: ddino Differential Revision: D5209648 fbshipit-source-id: dbb77c2 --- infer/src/clang/cFrontend_errors.ml | 42 ++++++++++++------- infer/src/clang/cFrontend_errors.mli | 3 +- infer/src/clang/cTL.ml | 11 ++++- infer/src/clang/cTL.mli | 1 + infer/src/clang/ctl_lexer.mll | 2 + infer/src/clang/ctl_parser.mly | 14 ++++++- .../objc/linters-for-test-only/issues.exp | 2 + .../linters-for-test-only/linters_example.al | 21 +++++++++- 8 files changed, 74 insertions(+), 22 deletions(-) diff --git a/infer/src/clang/cFrontend_errors.ml b/infer/src/clang/cFrontend_errors.ml index 68959978f..9148bb266 100644 --- a/infer/src/clang/cFrontend_errors.ml +++ b/infer/src/clang/cFrontend_errors.ml @@ -18,7 +18,8 @@ type linter = { condition : CTL.t; issue_desc : CIssue.issue_desc; def_file : string option; - path : string option; + whitelist_paths : ALVar.t list; + blacklist_paths : ALVar.t list; } (* If in linter developer mode and if current linter was passed, filter it out *) @@ -37,9 +38,16 @@ let filter_parsed_linters_developer parsed_linters = let filter_parsed_linters_by_path parsed_linters source_file = let filter_parsed_linter_by_path linter = - match linter.path with - | Some path -> ALVar.str_match_regex (SourceFile.to_rel_path source_file) path - | None -> true in + let should_lint paths = + List.exists + ~f:(fun path -> ALVar.compare_str_with_alexp (SourceFile.to_rel_path source_file) path) + paths in + let whitelist_ok = + List.is_empty linter.whitelist_paths || should_lint linter.whitelist_paths in + let blacklist_ok = + List.is_empty linter.blacklist_paths || not (should_lint linter.blacklist_paths) in + whitelist_ok && blacklist_ok + in List.filter ~f:filter_parsed_linter_by_path parsed_linters let filter_parsed_linters parsed_linters source_file = @@ -162,30 +170,32 @@ let create_parsed_linters linters_def_file checkers : linter list = severity = Exceptions.Kwarning; mode = CIssue.On; } in - let issue_desc, condition, path = - let process_linter_definitions (issue, cond, path) description = + let issue_desc, condition, whitelist_paths, blacklist_paths = + let process_linter_definitions (issue, cond, wl_paths, bl_paths) description = match description with | CSet (av, phi) when ALVar.is_report_when_keyword av -> - issue, phi, path + issue, phi, wl_paths, bl_paths | CDesc (av, msg) when ALVar.is_message_keyword av -> - {issue with description = msg}, cond, path + {issue with description = msg}, cond, wl_paths, bl_paths | CDesc (av, sugg) when ALVar.is_suggestion_keyword av -> - {issue with suggestion = Some sugg}, cond, path + {issue with suggestion = Some sugg}, cond, wl_paths, bl_paths | CDesc (av, sev) when ALVar.is_severity_keyword av -> - {issue with severity = string_to_err_kind sev}, cond, path + {issue with severity = string_to_err_kind sev}, cond, wl_paths, bl_paths | CDesc (av, m) when ALVar.is_mode_keyword av -> - {issue with mode = string_to_issue_mode m }, cond, path - | CDesc (av, path') when ALVar.is_path_keyword av -> - issue, cond, string_to_path path' - | _ -> issue, cond, path in + {issue with mode = string_to_issue_mode m }, cond, wl_paths, bl_paths + | CPath (`WhitelistPath, paths) -> + issue, cond, paths, bl_paths + | CPath (`BlacklistPath, paths) -> + issue, cond, wl_paths, paths + | _ -> issue, cond, wl_paths, bl_paths in List.fold ~f:process_linter_definitions - ~init:(dummy_issue, CTL.False, None) + ~init:(dummy_issue, CTL.False, [], []) checker.definitions in L.(debug Linters Medium) "@\nMaking condition and issue desc for checker '%s'@\n" checker.name; L.(debug Linters Medium) "@\nCondition =@\n %a@\n" CTL.Debug.pp_formula condition; L.(debug Linters Medium) "@\nIssue_desc = %a@\n" CIssue.pp_issue issue_desc; - {condition; issue_desc; def_file = Some linters_def_file; path;} in + {condition; issue_desc; def_file = Some linters_def_file; whitelist_paths; blacklist_paths;} in List.map ~f:do_one_checker checkers let rec apply_substitution f sub = diff --git a/infer/src/clang/cFrontend_errors.mli b/infer/src/clang/cFrontend_errors.mli index 18b213abf..63f7016c3 100644 --- a/infer/src/clang/cFrontend_errors.mli +++ b/infer/src/clang/cFrontend_errors.mli @@ -13,7 +13,8 @@ type linter = { condition : CTL.t; issue_desc : CIssue.issue_desc; def_file : string option; - path : string option; + whitelist_paths : ALVar.t list; + blacklist_paths : ALVar.t list; } val filter_parsed_linters : linter list -> SourceFile.t -> linter list diff --git a/infer/src/clang/cTL.ml b/infer/src/clang/cTL.ml index 49b0f52f5..dd32d6d50 100644 --- a/infer/src/clang/cTL.ml +++ b/infer/src/clang/cTL.ml @@ -77,10 +77,12 @@ let has_transition phi = match phi with set message = "bla" *) + type clause = | CLet of ALVar.formula_id * ALVar.t list * t (* Let clause: let id = definifion; *) | CSet of ALVar.keyword * t (* Set clause: set id = definition *) | CDesc of ALVar.keyword * string (* Description clause eg: set message = "..." *) + | CPath of [ `WhitelistPath | `BlacklistPath ] * ALVar.t list type ctl_checker = { name : string; (* Checker's name *) @@ -441,7 +443,14 @@ let print_checker c = cn_str Debug.pp_formula phi | CDesc (keyword, s) -> let cn_str = ALVar.keyword_to_string keyword in - L.(debug Linters Medium) " %s= @\n %s@\n@\n" cn_str s) + L.(debug Linters Medium) " %s= @\n %s@\n@\n" cn_str s + | CPath (paths_keyword, paths) -> + let keyword = + (match paths_keyword with + | `WhitelistPath -> "whitelist_path" + | _ -> "blacklist_path") in + let paths_str = String.concat ~sep:"," (List.map ~f:ALVar.alexp_to_string paths) in + L.(debug Linters Medium) " %s= @\n %s@\n@\n" keyword paths_str) ) c.definitions; L.(debug Linters Medium) "@\n-------------------- @\n" diff --git a/infer/src/clang/cTL.mli b/infer/src/clang/cTL.mli index b3dbc3ba5..c5c550f69 100644 --- a/infer/src/clang/cTL.mli +++ b/infer/src/clang/cTL.mli @@ -80,6 +80,7 @@ type clause = | CLet of ALVar.formula_id * ALVar.t list * t (* Let clause: let id = definifion; *) | CSet of ALVar.keyword * t (* Set clause: set id = definition *) | CDesc of ALVar.keyword * string (* Description clause eg: set message = "..." *) + | CPath of [ `WhitelistPath | `BlacklistPath ] * ALVar.t list type ctl_checker = { name : string; (* Checker's name *) diff --git a/infer/src/clang/ctl_lexer.mll b/infer/src/clang/ctl_lexer.mll index 5584d1275..306b1f473 100644 --- a/infer/src/clang/ctl_lexer.mll +++ b/infer/src/clang/ctl_lexer.mll @@ -54,6 +54,8 @@ rule token = parse | "LET" { LET } | "TRUE" { TRUE } | "FALSE" { FALSE } + | "whitelist_path" { WHITELIST_PATH } + | "blacklist_path" { BLACKLIST_PATH } | "{" { LEFT_BRACE } | "}" { RIGHT_BRACE } | "(" { LEFT_PAREN } diff --git a/infer/src/clang/ctl_parser.mly b/infer/src/clang/ctl_parser.mly index fab84e211..a27dbc380 100644 --- a/infer/src/clang/ctl_parser.mly +++ b/infer/src/clang/ctl_parser.mly @@ -67,6 +67,8 @@ %token IDENTIFIER %token FILE_IDENTIFIER %token STRING +%token WHITELIST_PATH +%token BLACKLIST_PATH %token EOF /* associativity and priority (lower to higher) of operators */ @@ -125,6 +127,11 @@ checker: } ; +path_list: + | alexp { [$1] } + | alexp COMMA path_list { $1 :: $3 } +; + clause_list: | clause SEMICOLON { [$1] } | clause SEMICOLON clause_list { $1 :: $3 } @@ -143,6 +150,10 @@ clause: | _ -> failwith "string '%s' cannot be set to a variable. \ Use the reserverd variable 'report_when'" in CTL.CSet (alvar, $4) } + | SET WHITELIST_PATH ASSIGNMENT LEFT_BRACE path_list RIGHT_BRACE + { CTL.CPath (`WhitelistPath, $5) } + | SET BLACKLIST_PATH ASSIGNMENT LEFT_BRACE path_list RIGHT_BRACE + { CTL.CPath (`BlacklistPath, $5) } | SET identifier ASSIGNMENT STRING { L.(debug Linters Verbose) "\tParsed SET clause@\n"; let alvar = match $2 with @@ -150,10 +161,9 @@ clause: | "suggestion" -> ALVar.Suggestion | "severity" -> ALVar.Severity | "mode" -> ALVar.Mode - | "path" -> ALVar.Path | _ -> failwithf "string '%s' cannot be set in a SET clause. \ Use either of: \ - 'message', 'mode', 'severity', 'suggestion' or 'path'" $2 in + 'message', 'mode', 'severity' or 'suggestion'" $2 in CTL.CDesc (alvar, $4) } | let_clause { $1 } ; diff --git a/infer/tests/codetoanalyze/objc/linters-for-test-only/issues.exp b/infer/tests/codetoanalyze/objc/linters-for-test-only/issues.exp index a43281812..e79b27af1 100644 --- a/infer/tests/codetoanalyze/objc/linters-for-test-only/issues.exp +++ b/infer/tests/codetoanalyze/objc/linters-for-test-only/issues.exp @@ -2,7 +2,9 @@ codetoanalyze/objc/linters-for-test-only/filter_by_path/include_file.m, main, 9, codetoanalyze/objc/linters-for-test-only/filter_by_path/include_file.m, main, 9, FILTER_BY_ALL_PATH_EXAMPLE, [] codetoanalyze/objc/linters-for-test-only/filter_by_path/include_file.m, main, 9, FILTER_BY_PATH_EXAMPLE, [] codetoanalyze/objc/linters-for-test-only/implicit_cast.c, main, 9, ALL_PATH_NO_FILTER_EXAMPLE, [] +codetoanalyze/objc/linters-for-test-only/implicit_cast.c, main, 9, BLACKLIST_PATH_EXAMPLE, [] codetoanalyze/objc/linters-for-test-only/implicit_cast.c, main, 9, FILTER_BY_ALL_PATH_EXAMPLE, [] +codetoanalyze/objc/linters-for-test-only/implicit_cast.c, main, 9, WHITE_BLACKLIST_PATH_EXAMPLE, [] codetoanalyze/objc/linters-for-test-only/implicit_cast.c, main, 10, TEST_VAR_TYPE_CHECK, [] codetoanalyze/objc/linters-for-test-only/implicit_cast.c, main, 11, TEST_IMPLICIT_CAST_CHECK, [] codetoanalyze/objc/linters-for-test-only/implicit_cast.c, main, 11, TEST_VAR_TYPE_CHECK, [] diff --git a/infer/tests/codetoanalyze/objc/linters-for-test-only/linters_example.al b/infer/tests/codetoanalyze/objc/linters-for-test-only/linters_example.al index 20cfe0fc4..2f99ba059 100644 --- a/infer/tests/codetoanalyze/objc/linters-for-test-only/linters_example.al +++ b/infer/tests/codetoanalyze/objc/linters-for-test-only/linters_example.al @@ -242,7 +242,7 @@ DEFINE-CHECKER FILTER_BY_PATH_EXAMPLE = { WHEN declaration_has_name("main") HOLDS-IN-NODE FunctionDecl; SET message = "Found main method"; - SET path = "codetoanalyze/objc/linters-for-test-only/filter_by_path/.*"; + SET whitelist_path = { REGEXP("codetoanalyze/objc/linters-for-test-only/filter_by_path/.*"), "A.m" }; }; DEFINE-CHECKER ALL_PATH_NO_FILTER_EXAMPLE = { @@ -257,5 +257,22 @@ DEFINE-CHECKER FILTER_BY_ALL_PATH_EXAMPLE = { WHEN declaration_has_name("main") HOLDS-IN-NODE FunctionDecl; SET message = "Found main method"; - SET path = ".*"; + SET whitelist_path = { REGEXP(".*") }; +}; + +DEFINE-CHECKER BLACKLIST_PATH_EXAMPLE = { + SET report_when = + WHEN declaration_has_name("main") + HOLDS-IN-NODE FunctionDecl; + SET message = "Found main method"; + SET blacklist_path = { REGEXP("codetoanalyze/objc/linters-for-test-only/filter_by_path/.*") }; +}; + +DEFINE-CHECKER WHITE_BLACKLIST_PATH_EXAMPLE = { + SET report_when = + WHEN declaration_has_name("main") + HOLDS-IN-NODE FunctionDecl; + SET message = "Found main method"; + SET whitelist_path = { REGEXP(".*") }; + SET blacklist_path = { REGEXP("codetoanalyze/objc/linters-for-test-only/filter_by_path/.*") }; };