[linters] Add an optional doc_url field to linters to specify the url to some documentation online.

Reviewed By: jvillard

Differential Revision: D5301979

fbshipit-source-id: b3ab268
master
Dulma Churchill 8 years ago committed by Facebook Github Bot
parent ee67e09cdf
commit 7d680b3b0c

@ -229,4 +229,5 @@ DEFINE-CHECKER POINTER_TO_INTEGRAL_IMPLICIT_CAST = {
WHEN has_cast_kind("PointerToIntegral") WHEN has_cast_kind("PointerToIntegral")
HOLDS-IN-NODE ImplicitCastExpr; HOLDS-IN-NODE ImplicitCastExpr;
SET message = "Implicit conversion from %child_type% to %type% in usage of %name%"; SET message = "Implicit conversion from %child_type% to %type% in usage of %name%";
SET doc_url = "https://clang.llvm.org/docs/DiagnosticsReference.html#wint-conversion";
}; };

@ -82,7 +82,8 @@ type err_data = {
loc_trace : loc_trace; loc_trace : loc_trace;
err_class : Exceptions.err_class; err_class : Exceptions.err_class;
visibility : Exceptions.visibility; visibility : Exceptions.visibility;
linters_def_file : string option linters_def_file : string option;
doc_url : string option;
} }
let compare_err_data err_data1 err_data2 = let compare_err_data err_data1 err_data2 =
@ -216,7 +217,7 @@ let update errlog_old errlog_new =
(fun err_key l -> (fun err_key l ->
ignore (add_issue errlog_old err_key l)) errlog_new ignore (add_issue errlog_old err_key l)) errlog_new
let log_issue err_kind err_log loc (node_id, node_key) session ltr ?linters_def_file exn = let log_issue err_kind err_log loc (node_id, node_key) session ltr ?linters_def_file ?doc_url exn =
let err_name, err_desc, ml_loc_opt, visibility, severity, force_kind, eclass = let err_name, err_desc, ml_loc_opt, visibility, severity, force_kind, eclass =
Exceptions.recognize_exception exn in Exceptions.recognize_exception exn in
let err_kind = match force_kind with let err_kind = match force_kind with
@ -247,6 +248,7 @@ let log_issue err_kind err_log loc (node_id, node_key) session ltr ?linters_def_
err_class = eclass; err_class = eclass;
visibility; visibility;
linters_def_file; linters_def_file;
doc_url;
} in } in
let err_key = { let err_key = {
err_kind; err_kind;

@ -59,7 +59,8 @@ type err_data = private {
loc_trace : loc_trace; loc_trace : loc_trace;
err_class : Exceptions.err_class; err_class : Exceptions.err_class;
visibility : Exceptions.visibility; visibility : Exceptions.visibility;
linters_def_file : string option linters_def_file : string option;
doc_url : string option; (* url to documentation of the issue type *)
} }
(** Type of the error log *) (** Type of the error log *)
@ -95,7 +96,7 @@ val update : t -> t -> unit
val log_issue : val log_issue :
Exceptions.err_kind -> t -> Location.t -> (int * int) -> int -> loc_trace -> Exceptions.err_kind -> t -> Location.t -> (int * int) -> int -> loc_trace ->
?linters_def_file:string -> exn -> unit ?linters_def_file:string -> ?doc_url:string -> exn -> unit
(** {2 Functions for manipulating per-file error tables} *) (** {2 Functions for manipulating per-file error tables} *)

@ -503,6 +503,7 @@ module IssuesJson = {
infer_source_loc: json_ml_loc, infer_source_loc: json_ml_loc,
bug_type_hum: Localise.to_human_readable_string key.err_name, bug_type_hum: Localise.to_human_readable_string key.err_name,
linters_def_file: err_data.linters_def_file, linters_def_file: err_data.linters_def_file,
doc_url: err_data.doc_url,
traceview_id: None traceview_id: None
}; };
if (not !is_first_item) { if (not !is_first_item) {

@ -23,6 +23,7 @@ type jsonbug = {
bug_class : string; bug_class : string;
kind : string; kind : string;
bug_type : string; bug_type : string;
?doc_url : string option;
qualifier : string; qualifier : string;
severity : string; severity : string;
visibility : string; visibility : string;

@ -17,12 +17,14 @@ type log_t =
?session: int -> ?session: int ->
?ltr: Errlog.loc_trace -> ?ltr: Errlog.loc_trace ->
?linters_def_file:string -> ?linters_def_file:string ->
?doc_url:string ->
exn -> exn ->
unit unit
type log_issue_from_errlog = Errlog.t -> log_t type log_issue_from_errlog = Errlog.t -> log_t
let log_issue_from_errlog err_kind err_log ?loc ?node_id ?session ?ltr ?linters_def_file exn = let log_issue_from_errlog err_kind err_log ?loc ?node_id ?session ?ltr ?linters_def_file
?doc_url exn =
let loc = match loc with let loc = match loc with
| None -> State.get_loc () | None -> State.get_loc ()
| Some loc -> loc in | Some loc -> loc in
@ -40,10 +42,11 @@ let log_issue_from_errlog err_kind err_log ?loc ?node_id ?session ?ltr ?linters_
| _ -> let err_name, _, _, _, _, _, _ = Exceptions.recognize_exception exn in | _ -> let err_name, _, _, _, _, _, _ = Exceptions.recognize_exception exn in
(Localise.to_issue_id err_name) in (Localise.to_issue_id err_name) in
if (Inferconfig.is_checker_enabled err_name) then if (Inferconfig.is_checker_enabled err_name) then
Errlog.log_issue err_kind err_log loc node_id session ltr ?linters_def_file exn Errlog.log_issue err_kind err_log loc node_id session ltr ?linters_def_file ?doc_url exn
let log_issue_from_summary err_kind summary ?loc ?node_id ?session ?ltr ?linters_def_file exn = let log_issue_from_summary err_kind summary ?loc ?node_id ?session ?ltr ?linters_def_file
?doc_url exn =
let is_generated_method = let is_generated_method =
Typ.Procname.java_is_generated (Specs.get_proc_name summary) in Typ.Procname.java_is_generated (Specs.get_proc_name summary) in
let should_suppress_lint = let should_suppress_lint =
@ -55,7 +58,8 @@ let log_issue_from_summary err_kind summary ?loc ?node_id ?session ?ltr ?linters
() (* Skip the reporting *) () (* Skip the reporting *)
else else
let err_log = summary.Specs.attributes.ProcAttributes.err_log in let err_log = summary.Specs.attributes.ProcAttributes.err_log in
log_issue_from_errlog err_kind err_log ?loc ?node_id ?session ?ltr ?linters_def_file exn log_issue_from_errlog err_kind err_log ?loc ?node_id ?session ?ltr ?linters_def_file
?doc_url exn
let log_issue_deprecated let log_issue_deprecated
?(store_summary=false) ?(store_summary=false)
@ -66,10 +70,12 @@ let log_issue_deprecated
?session ?session
?ltr ?ltr
?linters_def_file ?linters_def_file
?doc_url
exn = exn =
match Specs.get_summary proc_name with match Specs.get_summary proc_name with
| Some summary -> | Some summary ->
log_issue_from_summary err_kind summary ?loc ?node_id ?session ?ltr ?linters_def_file exn; log_issue_from_summary err_kind summary ?loc ?node_id ?session ?ltr ?linters_def_file
?doc_url exn;
if store_summary then if store_summary then
(* TODO (#16348004): This is currently needed as ThreadSafety works as a cluster checker *) (* TODO (#16348004): This is currently needed as ThreadSafety works as a cluster checker *)
Specs.store_summary summary Specs.store_summary summary

@ -17,6 +17,7 @@ type log_t =
?session: int -> ?session: int ->
?ltr: Errlog.loc_trace -> ?ltr: Errlog.loc_trace ->
?linters_def_file:string -> ?linters_def_file:string ->
?doc_url:string ->
exn -> exn ->
unit unit

@ -314,6 +314,7 @@ type log_issue =
?session: int -> ?session: int ->
?ltr: Errlog.loc_trace -> ?ltr: Errlog.loc_trace ->
?linters_def_file:string -> ?linters_def_file:string ->
?doc_url:string ->
exn -> exn ->
unit unit

@ -92,6 +92,7 @@ type log_issue =
?session: int -> ?session: int ->
?ltr: Errlog.loc_trace -> ?ltr: Errlog.loc_trace ->
?linters_def_file:string -> ?linters_def_file:string ->
?doc_url:string ->
exn -> exn ->
unit unit

@ -16,6 +16,7 @@ type keyword =
| Suggestion | Suggestion
| Severity | Severity
| Mode | Mode
| Doc_url
type formula_id = Formula_id of string[@@deriving compare] type formula_id = Formula_id of string[@@deriving compare]
@ -48,6 +49,7 @@ let keyword_to_string k =
| Suggestion -> "suggestion" | Suggestion -> "suggestion"
| Severity -> "severity" | Severity -> "severity"
| Mode -> "mode" | Mode -> "mode"
| Doc_url -> "doc_url"
let is_report_when_keyword k = let is_report_when_keyword k =
match k with match k with
@ -74,6 +76,11 @@ let is_mode_keyword k =
| Mode -> true | Mode -> true
| _ -> false | _ -> false
let is_doc_url_keyword k =
match k with
| Doc_url -> true
| _ -> false
(* true if and only if a substring of container matches the regular (* true if and only if a substring of container matches the regular
expression defined by contained expression defined by contained
*) *)

@ -14,6 +14,7 @@ type keyword =
| Suggestion | Suggestion
| Severity | Severity
| Mode | Mode
| Doc_url
type formula_id = Formula_id of string type formula_id = Formula_id of string
@ -43,6 +44,8 @@ val is_severity_keyword : keyword -> bool
val is_mode_keyword : keyword -> bool val is_mode_keyword : keyword -> bool
val is_doc_url_keyword : keyword -> bool
val str_match_regex : string -> string -> bool val str_match_regex : string -> string -> bool
val compare_str_with_alexp : string -> alexp -> bool val compare_str_with_alexp : string -> alexp -> bool

@ -132,6 +132,7 @@ let mutable_local_vars_advice context an =
"Local variable " ^ MF.monospaced_to_string named_decl_info.ni_name ^ "Local variable " ^ MF.monospaced_to_string named_decl_info.ni_name ^
" should be const to avoid reassignment"; " should be const to avoid reassignment";
suggestion = Some "Add a const (after the asterisk for pointer types)."; suggestion = Some "Add a const (after the asterisk for pointer types).";
doc_url = None;
loc = CFrontend_checkers.location_from_dinfo context decl_info loc = CFrontend_checkers.location_from_dinfo context decl_info
} }
else None else None
@ -162,6 +163,7 @@ let component_factory_function_advice context an =
"Prefer subclassing CKCompositeComponent to static helper functions \ "Prefer subclassing CKCompositeComponent to static helper functions \
that return a CKComponent subclass." that return a CKComponent subclass."
); );
doc_url = None;
loc = CFrontend_checkers.location_from_dinfo context decl_info loc = CFrontend_checkers.location_from_dinfo context decl_info
} }
else None else None
@ -205,6 +207,7 @@ let component_with_unconventional_superclass_advice context an =
suggestion = Some ( suggestion = Some (
"Instead, create a new subclass of CKCompositeComponent." "Instead, create a new subclass of CKCompositeComponent."
); );
doc_url = None;
loc = CFrontend_checkers.location_from_decl context if_decl loc = CFrontend_checkers.location_from_decl context if_decl
} }
else else
@ -259,6 +262,7 @@ let component_with_multiple_factory_methods_advice context an =
suggestion = suggestion =
Some "Instead, always expose all parameters in a single \ Some "Instead, always expose all parameters in a single \
designated initializer and document which are optional."; designated initializer and document which are optional.";
doc_url = None;
loc = CFrontend_checkers.location_from_decl context meth_decl loc = CFrontend_checkers.location_from_decl context meth_decl
}) (List.drop factory_methods 1) }) (List.drop factory_methods 1)
| _ -> assert false in | _ -> assert false in
@ -311,6 +315,7 @@ let rec _component_initializer_with_side_effects_advice
description = "No Side-effects"; description = "No Side-effects";
suggestion = Some "Your +new method should not modify any \ suggestion = Some "Your +new method should not modify any \
global variables or global state."; global variables or global state.";
doc_url = None;
loc = CFrontend_checkers.location_from_stmt context call_stmt loc = CFrontend_checkers.location_from_stmt context call_stmt
} }
| _ -> | _ ->
@ -344,6 +349,7 @@ let component_file_line_count_info (context: CLintersContext.context) dec =
mode = CIssue.Off; mode = CIssue.Off;
description = "Line count analytics"; description = "Line count analytics";
suggestion = None; suggestion = None;
doc_url = None;
loc = { loc = {
Location.line = i; Location.line = i;
Location.col = 0; Location.col = 0;
@ -390,6 +396,7 @@ let component_file_cyclomatic_complexity_info (context: CLintersContext.context)
mode = CIssue.Off; mode = CIssue.Off;
description = "Cyclomatic Complexity Incremental Marker"; description = "Cyclomatic Complexity Incremental Marker";
suggestion = None; suggestion = None;
doc_url = None;
loc = loc loc = loc
} }
| _ -> None | _ -> None

@ -169,6 +169,7 @@ let create_parsed_linters linters_def_file checkers : linter list =
suggestion = None; suggestion = None;
loc = Location.dummy; loc = Location.dummy;
severity = Exceptions.Kwarning; severity = Exceptions.Kwarning;
doc_url = None;
mode = CIssue.On; mode = CIssue.On;
} in } in
let issue_desc, condition, whitelist_paths, blacklist_paths = let issue_desc, condition, whitelist_paths, blacklist_paths =
@ -184,6 +185,8 @@ let create_parsed_linters linters_def_file checkers : linter list =
{issue with severity = string_to_err_kind sev}, cond, wl_paths, bl_paths {issue with severity = string_to_err_kind sev}, cond, wl_paths, bl_paths
| CDesc (av, m) when ALVar.is_mode_keyword av -> | CDesc (av, m) when ALVar.is_mode_keyword av ->
{issue with mode = string_to_issue_mode m }, cond, wl_paths, bl_paths {issue with mode = string_to_issue_mode m }, cond, wl_paths, bl_paths
| CDesc (av, doc) when ALVar.is_doc_url_keyword av ->
{issue with doc_url = Some doc }, cond, wl_paths, bl_paths
| CPath (`WhitelistPath, paths) -> | CPath (`WhitelistPath, paths) ->
issue, cond, paths, bl_paths issue, cond, paths, bl_paths
| CPath (`BlacklistPath, paths) -> | CPath (`BlacklistPath, paths) ->
@ -360,7 +363,7 @@ let log_frontend_issue translation_unit_context method_decl_opt key issue_desc l
|> QualifiedCppName.to_qual_string in |> QualifiedCppName.to_qual_string in
let key = Hashtbl.hash (key ^ method_name) in let key = Hashtbl.hash (key ^ method_name) in
Reporting.log_issue_from_errlog err_kind errlog exn ~loc ~ltr:trace Reporting.log_issue_from_errlog err_kind errlog exn ~loc ~ltr:trace
~node_id:(0, key) ?linters_def_file ~node_id:(0, key) ?linters_def_file ?doc_url:issue_desc.CIssue.doc_url
let get_current_method context (an : Ctl_parser_types.ast_node) = let get_current_method context (an : Ctl_parser_types.ast_node) =
match an with match an with

@ -17,6 +17,7 @@ type issue_desc = {
mode : mode; mode : mode;
description : string; (* Description in the error message *) description : string; (* Description in the error message *)
suggestion : string option; (* an optional suggestion or correction *) suggestion : string option; (* an optional suggestion or correction *)
doc_url : string option;
loc : Location.t; (* location in the code *) loc : Location.t; (* location in the code *)
} }

@ -17,6 +17,7 @@ type issue_desc = {
mode : mode; mode : mode;
description : string; (* Description in the error message *) description : string; (* Description in the error message *)
suggestion : string option; (* an optional suggestion or correction *) suggestion : string option; (* an optional suggestion or correction *)
doc_url : string option;
loc : Location.t; (* location in the code *) loc : Location.t; (* location in the code *)
} }

@ -178,9 +178,10 @@ clause:
| "suggestion" -> ALVar.Suggestion | "suggestion" -> ALVar.Suggestion
| "severity" -> ALVar.Severity | "severity" -> ALVar.Severity
| "mode" -> ALVar.Mode | "mode" -> ALVar.Mode
| "doc_url" -> ALVar.Doc_url
| _ -> failwithf "string '%s' cannot be set in a SET clause. \ | _ -> failwithf "string '%s' cannot be set in a SET clause. \
Use either of: \ Use either of: \
'message', 'mode', 'severity' or 'suggestion'" $2 in 'doc_url', 'message', 'mode', 'severity' or 'suggestion'" $2 in
CTL.CDesc (alvar, $4) } CTL.CDesc (alvar, $4) }
| let_clause { $1 } | let_clause { $1 }
; ;

@ -28,7 +28,8 @@ let create_fake_jsonbug
?(hash=1) ?(hash=1)
?(dotty=None) ?(dotty=None)
?(infer_source_loc=None) ?(infer_source_loc=None)
?(linters_def_file=Some "file/at/certain/path.al") () : Jsonbug_t.jsonbug = ?(linters_def_file=Some "file/at/certain/path.al")
?doc_url () : Jsonbug_t.jsonbug =
{ {
bug_class; bug_class;
kind; kind;
@ -50,6 +51,7 @@ let create_fake_jsonbug
infer_source_loc; infer_source_loc;
bug_type_hum = kind; bug_type_hum = kind;
linters_def_file; linters_def_file;
doc_url;
traceview_id = None; traceview_id = None;
} }

@ -279,4 +279,5 @@ DEFINE-CHECKER WHITE_BLACKLIST_PATH_EXAMPLE = {
SET message = "Found main method"; SET message = "Found main method";
SET whitelist_path = { all_files }; SET whitelist_path = { all_files };
SET blacklist_path = { filtered_files }; SET blacklist_path = { filtered_files };
SET doc_url = "www.example.com";
}; };

Loading…
Cancel
Save