You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
573 lines
22 KiB
573 lines
22 KiB
(*
|
|
* Copyright (c) 2014 - 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.
|
|
*)
|
|
|
|
open! Utils
|
|
|
|
module L = Logging
|
|
|
|
(** Module for the checks called by Eradicate. *)
|
|
|
|
(* activate the condition redundant warnings *)
|
|
let activate_condition_redundant = Config.from_env_variable "ERADICATE_CONDITION_REDUNDANT"
|
|
|
|
(* activate check for @Present annotations *)
|
|
let activate_optional_present = Config.from_env_variable "ERADICATE_OPTIONAL_PRESENT"
|
|
|
|
(* activate the field not mutable warnings *)
|
|
let activate_field_not_mutable = Config.from_env_variable "ERADICATE_FIELD_NOT_MUTABLE"
|
|
|
|
(* activate the field over annotated warnings *)
|
|
let activate_field_over_annotated = Config.from_env_variable "ERADICATE_FIELD_OVER_ANNOTATED"
|
|
|
|
(* activate the return over annotated warning *)
|
|
let activate_return_over_annotated = Config.from_env_variable "ERADICATE_RETURN_OVER_ANNOTATED"
|
|
|
|
(* activate the propagation of nullable to the return value *)
|
|
let activate_propagate_return_nullable =
|
|
Config.from_env_variable "ERADICATE_PROPAGATE_RETURN_NULLABLE"
|
|
|
|
(* do not report RETURN_NOT_NULLABLE if the return is annotated @Nonnull *)
|
|
let return_nonnull_silent = true
|
|
|
|
(* if true, check calls to libraries (i.e. not modelled and source not available) *)
|
|
let check_library_calls = false
|
|
|
|
|
|
let get_field_annotation fn typ =
|
|
match Annotations.get_field_type_and_annotation fn typ with
|
|
| None -> None
|
|
| Some (t, ia) ->
|
|
let ia' =
|
|
(* TODO (t4968422) eliminate not !Config.eradicate check by marking fields as nullified *)
|
|
(* outside of Eradicate in some other way *)
|
|
if (Models.Inference.enabled || not Config.eradicate)
|
|
&& Models.Inference.field_is_marked fn
|
|
then Annotations.mk_ia Annotations.Nullable ia
|
|
else ia in
|
|
Some (t, ia')
|
|
|
|
let report_error =
|
|
TypeErr.report_error Checkers.ST.report_error
|
|
|
|
let explain_expr node e =
|
|
match Errdesc.exp_rv_dexp node e with
|
|
| Some de -> Some (Sil.dexp_to_string de)
|
|
| None -> None
|
|
|
|
(** Classify a procedure. *)
|
|
let classify_procedure proc_attributes =
|
|
let pn = proc_attributes.ProcAttributes.proc_name in
|
|
let unique_id = Procname.to_unique_id pn in
|
|
let classification =
|
|
if Models.is_modelled_nullable pn then "M" (* modelled *)
|
|
else if Specs.proc_is_library proc_attributes then "L" (* library *)
|
|
else if not proc_attributes.ProcAttributes.is_defined then "S" (* skip *)
|
|
else if string_is_prefix "com.facebook" unique_id then "F" (* FB *)
|
|
else "?" in
|
|
classification
|
|
|
|
|
|
let is_virtual = function
|
|
| (p, _, _):: _ when Mangled.to_string p = "this" -> true
|
|
| _ -> false
|
|
|
|
|
|
(** Check an access (read or write) to a field. *)
|
|
let check_field_access
|
|
find_canonical_duplicate curr_pname node instr_ref exp fname ta loc : unit =
|
|
if TypeAnnotation.get_value Annotations.Nullable ta = true then
|
|
let origin_descr = TypeAnnotation.descr_origin ta in
|
|
report_error
|
|
find_canonical_duplicate
|
|
node
|
|
(TypeErr.Null_field_access (explain_expr node exp, fname, origin_descr, false))
|
|
(Some instr_ref)
|
|
loc curr_pname
|
|
|
|
(** Check an access to an array *)
|
|
let check_array_access
|
|
find_canonical_duplicate
|
|
curr_pname
|
|
node
|
|
instr_ref
|
|
array_exp
|
|
fname
|
|
ta
|
|
loc
|
|
indexed =
|
|
if TypeAnnotation.get_value Annotations.Nullable ta = true then
|
|
let origin_descr = TypeAnnotation.descr_origin ta in
|
|
report_error
|
|
find_canonical_duplicate
|
|
node
|
|
(TypeErr.Null_field_access (explain_expr node array_exp, fname, origin_descr, indexed))
|
|
(Some instr_ref)
|
|
loc
|
|
curr_pname
|
|
|
|
(** Where the condition is coming from *)
|
|
type from_call =
|
|
| From_condition (** Direct condition *)
|
|
| From_instanceof (** x instanceof C *)
|
|
| From_is_false_on_null (** returns false on null *)
|
|
| From_is_true_on_null (** returns true on null *)
|
|
| From_optional_isPresent (** x.isPresent *)
|
|
| From_containsKey (** x.containsKey *)
|
|
|
|
(** Check the normalized "is zero" or "is not zero" condition of a prune instruction. *)
|
|
let check_condition case_zero find_canonical_duplicate curr_pname
|
|
node e typ ta true_branch from_call idenv linereader loc instr_ref : unit =
|
|
let is_fun_nonnull ta = match TypeAnnotation.get_origin ta with
|
|
| TypeOrigin.Proc proc_origin ->
|
|
let (ia, _) = proc_origin.TypeOrigin.annotated_signature.Annotations.ret in
|
|
Annotations.ia_is_nonnull ia
|
|
| _ -> false in
|
|
|
|
let contains_instanceof_throwable node =
|
|
(* Check if the current procedure has a catch Throwable. *)
|
|
(* That always happens in the bytecode generated by try-with-resources. *)
|
|
let loc = Cfg.Node.get_loc node in
|
|
let throwable_found = ref false in
|
|
let throwable_class = Mangled.from_string "java.lang.Throwable" in
|
|
let typ_is_throwable = function
|
|
| Typ.Tstruct { Typ.csu = Csu.Class _; struct_name = Some c } ->
|
|
Mangled.equal c throwable_class
|
|
| _ -> false in
|
|
let do_instr = function
|
|
| Sil.Call (_, Sil.Const (Sil.Cfun pn), [_; (Sil.Sizeof(t, _, _), _)], _, _) when
|
|
Procname.equal pn ModelBuiltins.__instanceof && typ_is_throwable t ->
|
|
throwable_found := true
|
|
| _ -> () in
|
|
let do_node n =
|
|
if Location.equal loc (Cfg.Node.get_loc n)
|
|
then IList.iter do_instr (Cfg.Node.get_instrs n) in
|
|
Cfg.Procdesc.iter_nodes do_node (Cfg.Node.get_proc_desc node);
|
|
!throwable_found in
|
|
|
|
let from_try_with_resources () : bool =
|
|
(* heuristic to check if the condition is the translation of try-with-resources *)
|
|
match Printer.LineReader.from_loc linereader loc with
|
|
| Some line ->
|
|
not (string_contains "==" line || string_contains "!=" line)
|
|
&& (string_contains "}" line)
|
|
&& contains_instanceof_throwable node
|
|
| None -> false in
|
|
|
|
let is_temp = Idenv.exp_is_temp idenv e in
|
|
let nonnull = is_fun_nonnull ta in
|
|
let should_report =
|
|
TypeAnnotation.get_value Annotations.Nullable ta = false &&
|
|
(activate_condition_redundant || nonnull) &&
|
|
true_branch &&
|
|
(not is_temp || nonnull) &&
|
|
PatternMatch.type_is_class typ &&
|
|
not (from_try_with_resources ()) &&
|
|
from_call = From_condition &&
|
|
not (TypeAnnotation.origin_is_fun_library ta) in
|
|
let is_always_true = not case_zero in
|
|
let nonnull = is_fun_nonnull ta in
|
|
if should_report then
|
|
report_error
|
|
find_canonical_duplicate
|
|
node
|
|
(TypeErr.Condition_redundant (is_always_true, explain_expr node e, nonnull))
|
|
(Some instr_ref)
|
|
loc curr_pname
|
|
|
|
(** Check an "is zero" condition. *)
|
|
let check_zero find_canonical_duplicate = check_condition true find_canonical_duplicate
|
|
|
|
(** Check an "is not zero" condition. *)
|
|
let check_nonzero find_canonical_duplicate = check_condition false find_canonical_duplicate
|
|
|
|
(** Check an assignment to a field. *)
|
|
let check_field_assignment
|
|
find_canonical_duplicate curr_pname node instr_ref typestate exp_lhs
|
|
exp_rhs typ loc fname t_ia_opt typecheck_expr : unit =
|
|
let (t_lhs, ta_lhs, _) =
|
|
typecheck_expr node instr_ref curr_pname typestate exp_lhs
|
|
(typ, TypeAnnotation.const Annotations.Nullable false TypeOrigin.ONone, [loc]) loc in
|
|
let (_, ta_rhs, _) =
|
|
typecheck_expr node instr_ref curr_pname typestate exp_rhs
|
|
(typ, TypeAnnotation.const Annotations.Nullable false TypeOrigin.ONone, [loc]) loc in
|
|
let should_report_nullable =
|
|
let field_is_field_injector_readwrite () = match t_ia_opt with
|
|
| Some (_, ia) ->
|
|
Annotations.ia_is_field_injector_readwrite ia
|
|
| _ ->
|
|
false in
|
|
TypeAnnotation.get_value Annotations.Nullable ta_lhs = false &&
|
|
TypeAnnotation.get_value Annotations.Nullable ta_rhs = true &&
|
|
PatternMatch.type_is_class t_lhs &&
|
|
not (Ident.java_fieldname_is_outer_instance fname) &&
|
|
not (field_is_field_injector_readwrite ()) in
|
|
let should_report_absent =
|
|
activate_optional_present &&
|
|
TypeAnnotation.get_value Annotations.Present ta_lhs = true &&
|
|
TypeAnnotation.get_value Annotations.Present ta_rhs = false &&
|
|
not (Ident.java_fieldname_is_outer_instance fname) in
|
|
let should_report_mutable =
|
|
let field_is_mutable () = match t_ia_opt with
|
|
| Some (_, ia) -> Annotations.ia_is_mutable ia
|
|
| _ -> false in
|
|
activate_field_not_mutable &&
|
|
not (Procname.is_constructor curr_pname) &&
|
|
not (Procname.is_class_initializer curr_pname) &&
|
|
not (field_is_mutable ()) in
|
|
if should_report_nullable || should_report_absent then
|
|
begin
|
|
let ann = if should_report_nullable then Annotations.Nullable else Annotations.Present in
|
|
if Models.Inference.enabled then Models.Inference.field_add_nullable_annotation fname;
|
|
let origin_descr = TypeAnnotation.descr_origin ta_rhs in
|
|
report_error
|
|
find_canonical_duplicate
|
|
node
|
|
(TypeErr.Field_annotation_inconsistent (ann, fname, origin_descr))
|
|
(Some instr_ref)
|
|
loc curr_pname
|
|
end;
|
|
if should_report_mutable then
|
|
begin
|
|
let origin_descr = TypeAnnotation.descr_origin ta_rhs in
|
|
report_error
|
|
find_canonical_duplicate
|
|
node
|
|
(TypeErr.Field_not_mutable (fname, origin_descr))
|
|
(Some instr_ref)
|
|
loc curr_pname
|
|
end
|
|
|
|
|
|
(** Check that nonnullable fields are initialized in constructors. *)
|
|
let check_constructor_initialization
|
|
find_canonical_duplicate
|
|
curr_pname
|
|
curr_pdesc
|
|
start_node
|
|
final_initializer_typestates
|
|
final_constructor_typestates
|
|
loc: unit =
|
|
State.set_node start_node;
|
|
if Procname.is_constructor curr_pname
|
|
then begin
|
|
match PatternMatch.get_this_type (Cfg.Procdesc.get_attributes curr_pdesc) with
|
|
| Some (Typ.Tptr (Typ.Tstruct { Typ.instance_fields; struct_name } as ts, _)) ->
|
|
let do_field (fn, ft, _) =
|
|
let annotated_with f = match get_field_annotation fn ts with
|
|
| None -> false
|
|
| Some (_, ia) -> f ia in
|
|
let nullable_annotated = annotated_with Annotations.ia_is_nullable in
|
|
let nonnull_annotated = annotated_with Annotations.ia_is_nonnull in
|
|
let injector_readonly_annotated =
|
|
annotated_with Annotations.ia_is_field_injector_readonly in
|
|
|
|
let final_type_annotation_with unknown list f =
|
|
let filter_range_opt = function
|
|
| Some (_, ta, _) -> f ta
|
|
| None -> unknown in
|
|
IList.exists
|
|
(function pname, typestate ->
|
|
let pvar = Pvar.mk
|
|
(Mangled.from_string (Ident.fieldname_to_string fn))
|
|
pname in
|
|
filter_range_opt (TypeState.lookup_pvar pvar typestate))
|
|
list in
|
|
|
|
let may_be_assigned_in_final_typestate =
|
|
final_type_annotation_with
|
|
false
|
|
(Lazy.force final_initializer_typestates)
|
|
(fun ta -> TypeAnnotation.get_origin ta <> TypeOrigin.Undef) in
|
|
|
|
let may_be_nullable_in_final_typestate () =
|
|
final_type_annotation_with
|
|
true
|
|
(Lazy.force final_constructor_typestates)
|
|
(fun ta -> TypeAnnotation.get_value Annotations.Nullable ta = true) in
|
|
|
|
let should_check_field_initialization =
|
|
let in_current_class =
|
|
let fld_cname = Ident.java_fieldname_get_class fn in
|
|
match struct_name with
|
|
| None -> false
|
|
| Some name -> Mangled.equal name (Mangled.from_string fld_cname) in
|
|
not injector_readonly_annotated &&
|
|
PatternMatch.type_is_class ft &&
|
|
in_current_class &&
|
|
not (Ident.java_fieldname_is_outer_instance fn) in
|
|
|
|
if should_check_field_initialization then
|
|
begin
|
|
if Models.Inference.enabled then Models.Inference.field_add_nullable_annotation fn;
|
|
|
|
(* Check if field is missing annotation. *)
|
|
if not (nullable_annotated || nonnull_annotated) &&
|
|
not may_be_assigned_in_final_typestate then
|
|
report_error
|
|
find_canonical_duplicate
|
|
start_node
|
|
(TypeErr.Field_not_initialized (fn, curr_pname))
|
|
None
|
|
loc
|
|
curr_pname;
|
|
|
|
(* Check if field is over-annotated. *)
|
|
if activate_field_over_annotated &&
|
|
nullable_annotated &&
|
|
not (may_be_nullable_in_final_typestate ()) then
|
|
report_error
|
|
find_canonical_duplicate
|
|
start_node
|
|
(TypeErr.Field_over_annotated (fn, curr_pname))
|
|
None
|
|
loc
|
|
curr_pname;
|
|
end in
|
|
|
|
IList.iter do_field instance_fields
|
|
| _ -> ()
|
|
end
|
|
|
|
(** Make the return type @Nullable by modifying the spec. *)
|
|
let spec_make_return_nullable curr_pname =
|
|
match Specs.get_summary curr_pname with
|
|
| Some summary ->
|
|
let proc_attributes = Specs.get_attributes summary in
|
|
let method_annotation = proc_attributes.ProcAttributes.method_annotation in
|
|
let method_annotation' = Annotations.method_annotation_mark_return
|
|
Annotations.Nullable method_annotation in
|
|
let proc_attributes' =
|
|
{ proc_attributes with
|
|
ProcAttributes.method_annotation = method_annotation' } in
|
|
let summary' =
|
|
{ summary with
|
|
Specs.attributes = proc_attributes' } in
|
|
Specs.add_summary curr_pname summary'
|
|
| None -> ()
|
|
|
|
(** Check the annotations when returning from a method. *)
|
|
let check_return_annotation
|
|
find_canonical_duplicate curr_pname exit_node ret_range
|
|
ret_ia ret_implicitly_nullable loc : unit =
|
|
let ret_annotated_nullable = Annotations.ia_is_nullable ret_ia in
|
|
let ret_annotated_present = Annotations.ia_is_present ret_ia in
|
|
let ret_annotated_nonnull = Annotations.ia_is_nonnull ret_ia in
|
|
match ret_range with
|
|
| Some (_, final_ta, _) ->
|
|
let final_nullable = TypeAnnotation.get_value Annotations.Nullable final_ta in
|
|
let final_present = TypeAnnotation.get_value Annotations.Present final_ta in
|
|
let origin_descr = TypeAnnotation.descr_origin final_ta in
|
|
let return_not_nullable =
|
|
final_nullable &&
|
|
not ret_annotated_nullable &&
|
|
not ret_implicitly_nullable &&
|
|
not (return_nonnull_silent && ret_annotated_nonnull) in
|
|
let return_value_not_present =
|
|
activate_optional_present &&
|
|
not final_present &&
|
|
ret_annotated_present in
|
|
let return_over_annotated =
|
|
not final_nullable &&
|
|
ret_annotated_nullable &&
|
|
activate_return_over_annotated in
|
|
|
|
if return_not_nullable && Models.Inference.enabled then
|
|
Models.Inference.proc_mark_return_nullable curr_pname;
|
|
|
|
if return_not_nullable &&
|
|
activate_propagate_return_nullable
|
|
then
|
|
spec_make_return_nullable curr_pname;
|
|
|
|
if return_not_nullable || return_value_not_present then
|
|
begin
|
|
let ann =
|
|
if return_not_nullable then Annotations.Nullable else Annotations.Present in
|
|
|
|
|
|
report_error
|
|
find_canonical_duplicate
|
|
exit_node
|
|
(TypeErr.Return_annotation_inconsistent (ann, curr_pname, origin_descr))
|
|
None
|
|
loc curr_pname
|
|
end;
|
|
|
|
if return_over_annotated then
|
|
begin
|
|
report_error
|
|
find_canonical_duplicate
|
|
exit_node
|
|
(TypeErr.Return_over_annotated curr_pname)
|
|
None
|
|
loc curr_pname
|
|
end
|
|
| None ->
|
|
()
|
|
|
|
(** Check the receiver of a virtual call. *)
|
|
let check_call_receiver
|
|
find_canonical_duplicate
|
|
curr_pname
|
|
node
|
|
typestate
|
|
call_params
|
|
callee_pname
|
|
(instr_ref : TypeErr.InstrRef.t)
|
|
loc
|
|
typecheck_expr
|
|
: unit =
|
|
match call_params with
|
|
| ((original_this_e, this_e), typ) :: _ ->
|
|
let (_, this_ta, _) =
|
|
typecheck_expr node instr_ref curr_pname typestate this_e
|
|
(typ, TypeAnnotation.const Annotations.Nullable false TypeOrigin.ONone, []) loc in
|
|
let null_method_call = TypeAnnotation.get_value Annotations.Nullable this_ta in
|
|
let optional_get_on_absent =
|
|
activate_optional_present &&
|
|
Models.is_optional_get callee_pname &&
|
|
not (TypeAnnotation.get_value Annotations.Present this_ta) in
|
|
if null_method_call || optional_get_on_absent then
|
|
begin
|
|
let ann = if null_method_call then Annotations.Nullable else Annotations.Present in
|
|
let descr = explain_expr node original_this_e in
|
|
let origin_descr = TypeAnnotation.descr_origin this_ta in
|
|
report_error
|
|
find_canonical_duplicate
|
|
node
|
|
(TypeErr.Call_receiver_annotation_inconsistent
|
|
(ann, descr, callee_pname, origin_descr))
|
|
(Some instr_ref)
|
|
loc curr_pname
|
|
end
|
|
| [] -> ()
|
|
|
|
(** Check the parameters of a call. *)
|
|
let check_call_parameters
|
|
find_canonical_duplicate curr_pname node typestate callee_attributes
|
|
sig_params call_params loc instr_ref typecheck_expr : unit =
|
|
let callee_pname = callee_attributes.ProcAttributes.proc_name in
|
|
let has_this = is_virtual sig_params in
|
|
let tot_param_num = IList.length sig_params - (if has_this then 1 else 0) in
|
|
let rec check sparams cparams = match sparams, cparams with
|
|
| (s1, ia1, t1) :: sparams', ((orig_e2, e2), t2) :: cparams' ->
|
|
let param_is_this = Mangled.to_string s1 = "this" in
|
|
let formal_is_nullable = Annotations.ia_is_nullable ia1 in
|
|
let formal_is_present = Annotations.ia_is_present ia1 in
|
|
let (_, ta2, _) =
|
|
typecheck_expr node instr_ref curr_pname typestate e2
|
|
(t2, TypeAnnotation.const Annotations.Nullable false TypeOrigin.ONone, []) loc in
|
|
let parameter_not_nullable =
|
|
not param_is_this &&
|
|
PatternMatch.type_is_class t1 &&
|
|
not formal_is_nullable &&
|
|
TypeAnnotation.get_value Annotations.Nullable ta2 in
|
|
let parameter_absent =
|
|
activate_optional_present &&
|
|
not param_is_this &&
|
|
PatternMatch.type_is_class t1 &&
|
|
formal_is_present &&
|
|
not (TypeAnnotation.get_value Annotations.Present ta2) in
|
|
if parameter_not_nullable || parameter_absent then
|
|
begin
|
|
let ann =
|
|
if parameter_not_nullable
|
|
then Annotations.Nullable
|
|
else Annotations.Present in
|
|
let description =
|
|
match explain_expr node orig_e2 with
|
|
| Some descr -> descr
|
|
| None -> "formal parameter " ^ (Mangled.to_string s1) in
|
|
let origin_descr = TypeAnnotation.descr_origin ta2 in
|
|
|
|
let param_num = IList.length sparams' + (if has_this then 0 else 1) in
|
|
let callee_loc = callee_attributes.ProcAttributes.loc in
|
|
report_error
|
|
find_canonical_duplicate
|
|
node
|
|
(TypeErr.Parameter_annotation_inconsistent (
|
|
ann,
|
|
description,
|
|
param_num,
|
|
callee_pname,
|
|
callee_loc,
|
|
origin_descr))
|
|
(Some instr_ref)
|
|
loc curr_pname;
|
|
if Models.Inference.enabled then
|
|
Models.Inference.proc_add_parameter_nullable callee_pname param_num tot_param_num
|
|
end;
|
|
check sparams' cparams'
|
|
| _ -> () in
|
|
let should_check_parameters =
|
|
if check_library_calls then true
|
|
else
|
|
Models.is_modelled_nullable callee_pname ||
|
|
callee_attributes.ProcAttributes.is_defined ||
|
|
Specs.get_summary callee_pname <> None in
|
|
if should_check_parameters then
|
|
(* left to right to avoid guessing the different lengths *)
|
|
check (IList.rev sig_params) (IList.rev call_params)
|
|
|
|
(** Checks if the annotations are consistent with the inherited class or with the
|
|
implemented interfaces *)
|
|
let check_overridden_annotations
|
|
find_canonical_duplicate tenv proc_name proc_desc annotated_signature =
|
|
let start_node = Cfg.Procdesc.get_start_node proc_desc in
|
|
let loc = Cfg.Node.get_loc start_node in
|
|
|
|
let check_return overriden_proc_name overriden_signature =
|
|
let ret_is_nullable =
|
|
let ia, _ = annotated_signature.Annotations.ret in
|
|
Annotations.ia_is_nullable ia
|
|
and ret_overridden_nullable =
|
|
let overriden_ia, _ = overriden_signature.Annotations.ret in
|
|
Annotations.ia_is_nullable overriden_ia in
|
|
if ret_is_nullable && not ret_overridden_nullable then
|
|
report_error
|
|
find_canonical_duplicate
|
|
start_node
|
|
(TypeErr.Inconsistent_subclass_return_annotation (proc_name, overriden_proc_name))
|
|
None
|
|
loc proc_name
|
|
|
|
and check_params overriden_proc_name overriden_signature =
|
|
let compare pos current_param overriden_param : int =
|
|
let current_name, current_ia, _ = current_param in
|
|
let _, overriden_ia, _ = overriden_param in
|
|
let () =
|
|
if not (Annotations.ia_is_nullable current_ia)
|
|
&& Annotations.ia_is_nullable overriden_ia then
|
|
report_error
|
|
find_canonical_duplicate
|
|
start_node
|
|
(TypeErr.Inconsistent_subclass_parameter_annotation
|
|
(Mangled.to_string current_name, pos, proc_name, overriden_proc_name))
|
|
None
|
|
loc proc_name in
|
|
(pos + 1) in
|
|
|
|
(* TODO (#5280249): investigate why argument lists can be of different length *)
|
|
let current_params = annotated_signature.Annotations.params
|
|
and overridden_params = overriden_signature.Annotations.params in
|
|
let initial_pos = if is_virtual current_params then 0 else 1 in
|
|
if (IList.length current_params) = (IList.length overridden_params) then
|
|
ignore (IList.fold_left2 compare initial_pos current_params overridden_params) in
|
|
|
|
let check overriden_proc_name =
|
|
match Specs.proc_resolve_attributes overriden_proc_name with
|
|
| Some attributes ->
|
|
let overridden_signature = Models.get_modelled_annotated_signature attributes in
|
|
check_return overriden_proc_name overridden_signature;
|
|
check_params overriden_proc_name overridden_signature
|
|
| None ->
|
|
() in
|
|
|
|
PatternMatch.proc_iter_overridden_methods check tenv proc_name
|