@ -6,7 +6,6 @@
* )
open ! IStd
open PolyVariantEqual
module MF = MarkupFormatter
let get_source_range an =
@ -163,101 +162,6 @@ let mutable_local_vars_advice context an =
with CFrontend_errors . IncorrectAssumption _ -> None
(* Should only be called with a VarDecl *)
(* * Catches functions that should be composite components.
http : // componentkit . org / docs / break - out - composites . html
Any static function that returns a subclass of CKComponent will be flagged . * )
let component_factory_function_advice context an =
let is_component_if decl =
CAst_utils . is_objc_if_descendant decl [ CFrontend_config . ckcomponent_cl ]
in
if is_ck_context context an then
match an with
| Ctl_parser_types . Decl
( Clang_ast_t . FunctionDecl ( decl_info , _ , ( qual_type : Clang_ast_t . qual_type ) , _ ) ) ->
let objc_interface = CAst_utils . qual_type_to_objc_interface qual_type in
if is_component_if objc_interface then
Some
{ CIssue . issue_type = IssueType . component_factory_function
; severity = Advice
; mode = Off
; description = " Break out composite components "
; suggestion =
Some
" Prefer subclassing CKCompositeComponent to static helper functions that return \
a CKComponent subclass . "
; loc = ALUtils . location_from_dinfo context decl_info }
else None
| _ ->
None
else None
(* Should only be called with FunctionDecl *)
(* * Components should not inherit from each other. They should instead inherit from CKComponent,
CKCompositeComponent , or CKStatefulViewComponent . ( Similar rule applies to component
controllers . ) * )
let component_with_unconventional_superclass_advice context an =
let check_interface if_decl =
match if_decl with
| Clang_ast_t . ObjCInterfaceDecl ( _ , _ , _ , _ , _ ) ->
if is_component_or_controller_if ( Some if_decl ) then
let superclass_name =
match CAst_utils . get_super_if ( Some if_decl ) with
| Some ( Clang_ast_t . ObjCInterfaceDecl ( _ , named_decl_info , _ , _ , _ ) ) ->
Some named_decl_info . ni_name
| _ ->
None
in
let has_conventional_superclass =
let open CFrontend_config in
match superclass_name with
| Some name
when List . mem ~ equal : String . equal
[ ckcomponent_cl
; ckcomponentcontroller_cl
; " CKCompositeComponent "
; " CKLayoutComponent "
; " CKRenderComponent "
; " CKStatefulViewComponent "
; " CKStatefulViewComponentController "
; " NTNativeTemplateComponent " ]
name ->
true
| _ ->
false
in
let condition =
is_component_or_controller_if ( Some if_decl ) && not has_conventional_superclass
in
if condition then
Some
{ CIssue . issue_type = IssueType . component_with_unconventional_superclass
; severity = Advice
; mode = On
; description = " Never Subclass Components "
; suggestion = Some " Instead, create a new subclass of CKCompositeComponent. "
; loc = ALUtils . location_from_decl context if_decl }
else None
else None
| _ ->
assert false
in
match an with
| Ctl_parser_types . Decl ( Clang_ast_t . ObjCImplementationDecl ( _ , _ , _ , _ , impl_decl_info ) ) ->
let if_decl_opt =
CAst_utils . get_decl_opt_with_decl_ref_opt impl_decl_info . oidi_class_interface
in
if Option . is_some if_decl_opt && is_ck_context context an then
check_interface ( Option . value_exn if_decl_opt )
else None
| _ ->
None
(* * Components should only have one factory method.
( They could technically have none if they re - use the parent class ' s factory method . )
@ -310,152 +214,3 @@ let component_with_multiple_factory_methods_advice context an =
match if_decl_opt with Some d when is_ck_context context an -> check_interface d | _ -> [] )
| _ ->
[]
let in_ck_class ( context : CLintersContext . context ) =
Option . value_map ~ f : is_component_or_controller_descendant_impl ~ default : false
context . current_objc_class
&& CGeneral_utils . is_objc_extension context . translation_unit_context
let is_in_factory_method ( context : CLintersContext . context ) =
let interface_decl_opt =
match context . current_objc_class with
| Some ( ObjCImplementationDecl ( _ , _ , _ , _ , impl_decl_info ) ) ->
CAst_utils . get_decl_opt_with_decl_ref_opt impl_decl_info . oidi_class_interface
| _ ->
None
in
let methods_to_check =
match context . current_method with
| Some current_method ->
current_method :: context . parent_methods
| None ->
context . parent_methods
in
List . exists methods_to_check ~ f : ( fun method_decl ->
CAst_utils . is_objc_factory_method ~ class_decl : interface_decl_opt
~ method_decl : ( Some method_decl ) )
(* * Components shouldn't have side-effects in its initializer.
http : // componentkit . org / docs / no - side - effects . html
The only current way we look for side - effects is by looking for asynchronous execution
( dispatch_async , dispatch_after ) and execution that relies on other threads ( dispatch_sync ) .
Other side - effects , like reading of global variables , is not checked by this analyzer , although
still an infraction of the rule . * )
let rec component_initializer_with_side_effects_advice_ ( context : CLintersContext . context )
call_stmt =
let condition =
in_ck_class context && is_in_factory_method context
&&
match context . current_objc_class with
| Some d ->
is_in_main_file context . translation_unit_context ( Ctl_parser_types . Decl d )
| None ->
false
in
if condition then
match call_stmt with
| Clang_ast_t . ImplicitCastExpr ( _ , stmt :: _ , _ , _ ) ->
component_initializer_with_side_effects_advice_ context stmt
| Clang_ast_t . DeclRefExpr ( _ , _ , _ , decl_ref_expr_info ) -> (
let refs = [ decl_ref_expr_info . drti_decl_ref ; decl_ref_expr_info . drti_found_decl_ref ] in
match List . find_map ~ f : CAst_utils . name_of_decl_ref_opt refs with
| Some " dispatch_after " | Some " dispatch_async " | Some " dispatch_sync " ->
Some
{ CIssue . issue_type = IssueType . component_initializer_with_side_effects
; severity = Advice
; mode = On
; description = " No Side-effects "
; suggestion =
Some " Your +new method should not modify any global variables or global state. "
; loc = ALUtils . location_from_stmt context call_stmt }
| _ ->
None )
| _ ->
None
else None
let component_initializer_with_side_effects_advice ( context : CLintersContext . context ) an =
match an with
| Ctl_parser_types . Stmt ( CallExpr ( _ , called_func_stmt :: _ , _ ) ) ->
component_initializer_with_side_effects_advice_ context called_func_stmt
| _ ->
None
(* only to be called in CallExpr *)
(* * Returns one issue per line of code, with the column set to 0.
This still needs to be in infer b / c only files that have a valid component kit class impl should
be analyzed . * )
let component_file_line_count_info ( context : CLintersContext . context ) dec =
let condition = Config . compute_analytics && context . is_ck_translation_unit in
match dec with
| Ctl_parser_types . Decl ( Clang_ast_t . TranslationUnitDecl _ ) when condition ->
let source_file = context . translation_unit_context . CFrontend_config . source_file in
let line_count = SourceFile . line_count source_file in
List . map
~ f : ( fun i ->
{ CIssue . issue_type = IssueType . component_file_line_count
; severity = Info
; mode = Off
; description = " Line count analytics "
; suggestion = None
; loc = { Location . line = i ; Location . col = 0 ; Location . file = source_file } } )
( List . range 1 line_count ~ start : ` inclusive ~ stop : ` inclusive )
| _ ->
[]
(* * Computes a component file's cyclomatic complexity.
Somewhat borrowed from
https : // github . com / oclint / oclint / blob / 5889 b5ec168185513ba69ce83821ea1cc8e63fbe
/ oclint - metrics / lib / CyclomaticComplexityMetric . cpp * )
let component_file_cyclomatic_complexity_info ( context : CLintersContext . context ) an =
let is_cyclo_stmt stmt =
match stmt with
| Clang_ast_t . IfStmt _
| Clang_ast_t . ForStmt _
| Clang_ast_t . ObjCForCollectionStmt _
| Clang_ast_t . CXXForRangeStmt _
| Clang_ast_t . WhileStmt _
| Clang_ast_t . DoStmt _
| Clang_ast_t . CaseStmt _
| Clang_ast_t . ObjCAtCatchStmt _
| Clang_ast_t . CXXCatchStmt _
| Clang_ast_t . ConditionalOperator _ ->
true
| Clang_ast_t . BinaryOperator ( _ , _ , _ , boi ) ->
List . mem ~ equal : ( = ) [ ` LAnd ; ` LOr ] boi . Clang_ast_t . boi_kind
| _ ->
false
in
let cyclo_loc_opt an =
match an with
| Ctl_parser_types . Stmt stmt
when Config . compute_analytics && is_cyclo_stmt stmt && is_ck_context context an ->
Some ( ALUtils . location_from_stmt context stmt )
| Ctl_parser_types . Decl ( Clang_ast_t . TranslationUnitDecl _ as d )
when Config . compute_analytics && context . is_ck_translation_unit ->
Some ( ALUtils . location_from_decl context d )
| _ ->
None
in
match cyclo_loc_opt an with
| Some loc ->
Some
{ CIssue . issue_type = IssueType . component_file_cyclomatic_complexity
; severity = Info
; mode = Off
; description = " Cyclomatic Complexity Incremental Marker "
; suggestion = None
; loc }
| _ ->
None