diff --git a/infer/src/absint/PatternMatch.ml b/infer/src/absint/PatternMatch.ml index 414e53cef..6d552096e 100644 --- a/infer/src/absint/PatternMatch.ml +++ b/infer/src/absint/PatternMatch.ml @@ -312,23 +312,24 @@ let proc_calls resolve_attributes pdesc filter : (Typ.Procname.t * ProcAttribute let override_find ?(check_current_type = true) f tenv proc_name = let method_name = Typ.Procname.get_method proc_name in + let parameter_length = List.length (Typ.Procname.get_parameters proc_name) in let is_override pname = - (* Note: very coarse! TODO: match parameter names/types to get an exact match *) - String.equal (Typ.Procname.get_method pname) method_name - && not (Typ.Procname.is_constructor pname) + (not (Typ.Procname.is_constructor pname)) + && String.equal (Typ.Procname.get_method pname) method_name + (* TODO (T32979782): match parameter types, taking subtyping and type erasure into account *) + && Int.equal (List.length (Typ.Procname.get_parameters pname)) parameter_length in - let rec find_super_type_ super_class_name = + let rec find_super_type super_class_name = Tenv.lookup tenv super_class_name |> Option.bind ~f:(fun {Typ.Struct.methods; supers} -> match List.find ~f:(fun pname -> is_override pname && f pname) methods with | None -> - List.find_map ~f:find_super_type_ supers + List.find_map ~f:find_super_type supers | pname_opt -> pname_opt ) in let find_super_type type_name = - List.find_map ~f:find_super_type_ - (type_get_direct_supertypes tenv (Typ.mk (Tstruct type_name))) + List.find_map ~f:find_super_type (type_get_direct_supertypes tenv (Typ.mk (Tstruct type_name))) in if check_current_type && f proc_name then Some proc_name else diff --git a/infer/tests/codetoanalyze/java/eradicate/InconsistentSubclassAnnotation.java b/infer/tests/codetoanalyze/java/eradicate/InconsistentSubclassAnnotation.java index 3e507dfec..a38235181 100644 --- a/infer/tests/codetoanalyze/java/eradicate/InconsistentSubclassAnnotation.java +++ b/infer/tests/codetoanalyze/java/eradicate/InconsistentSubclassAnnotation.java @@ -112,4 +112,12 @@ public class InconsistentSubclassAnnotation implements InconsistentSubclassAnnot public String implementInAnotherFile(String s) { return ""; } + + public @Nullable Object overloadedMethod() { + return null; + } + + public Object overloadedMethod(Object object) { + return object; + } } diff --git a/infer/tests/codetoanalyze/java/eradicate/InconsistentSubclassAnnotationInterface.java b/infer/tests/codetoanalyze/java/eradicate/InconsistentSubclassAnnotationInterface.java index f5c61254c..d78385b4e 100644 --- a/infer/tests/codetoanalyze/java/eradicate/InconsistentSubclassAnnotationInterface.java +++ b/infer/tests/codetoanalyze/java/eradicate/InconsistentSubclassAnnotationInterface.java @@ -10,5 +10,10 @@ package codetoanalyze.java.eradicate; import javax.annotation.Nullable; public interface InconsistentSubclassAnnotationInterface { + public String implementInAnotherFile(@Nullable String s); + + Object overloadedMethod(); + + Object overloadedMethod(Object object); } diff --git a/infer/tests/codetoanalyze/java/eradicate/issues.exp b/infer/tests/codetoanalyze/java/eradicate/issues.exp index 34e37a6dd..7db68d31c 100644 --- a/infer/tests/codetoanalyze/java/eradicate/issues.exp +++ b/infer/tests/codetoanalyze/java/eradicate/issues.exp @@ -32,6 +32,7 @@ codetoanalyze/java/eradicate/FieldNotNullable.java, codetoanalyze.java.eradicate codetoanalyze/java/eradicate/FieldNotNullable.java, codetoanalyze.java.eradicate.TestInitializerBuilder.build():codetoanalyze.java.eradicate.TestInitializer, 2, ERADICATE_CONDITION_REDUNDANT, no_bucket, WARNING, [The condition TestInitializerBuilder.required2 is always true according to the existing annotations.] codetoanalyze/java/eradicate/InconsistentSubclassAnnotation.java, codetoanalyze.java.eradicate.ExtendsExternalLibrary.externalMethod2(java.lang.Object):void, 0, ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION, no_bucket, WARNING, [First parameter `object` of method `ExtendsExternalLibrary.externalMethod2(...)` is not `@Nullable` but is declared `@Nullable`in the parent class method `SomeExternalClass.externalMethod2(...)`.] codetoanalyze/java/eradicate/InconsistentSubclassAnnotation.java, codetoanalyze.java.eradicate.InconsistentSubclassAnnotation.implementInAnotherFile(java.lang.String):java.lang.String, 0, ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION, no_bucket, WARNING, [First parameter `s` of method `InconsistentSubclassAnnotation.implementInAnotherFile(...)` is not `@Nullable` but is declared `@Nullable`in the parent class method `InconsistentSubclassAnnotationInterface.implementInAnotherFile(...)`.] +codetoanalyze/java/eradicate/InconsistentSubclassAnnotation.java, codetoanalyze.java.eradicate.InconsistentSubclassAnnotation.overloadedMethod():java.lang.Object, 0, ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION, no_bucket, WARNING, [Method `InconsistentSubclassAnnotation.overloadedMethod()` is annotated with `@Nullable` but overrides unannotated method `InconsistentSubclassAnnotationInterface.overloadedMethod()`.] codetoanalyze/java/eradicate/InconsistentSubclassAnnotation.java, codetoanalyze.java.eradicate.SubclassExample$B.foo():codetoanalyze.java.eradicate.SubclassExample$T, 0, ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION, no_bucket, WARNING, [Method `SubclassExample$B.foo()` is annotated with `@Nullable` but overrides unannotated method `SubclassExample$A.foo()`.] codetoanalyze/java/eradicate/InconsistentSubclassAnnotation.java, codetoanalyze.java.eradicate.SubclassExample$C.baz():codetoanalyze.java.eradicate.SubclassExample$T, 0, ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION, no_bucket, WARNING, [Method `SubclassExample$C.baz()` is annotated with `@Nullable` but overrides unannotated method `SubclassExample$I.baz()`.] codetoanalyze/java/eradicate/InconsistentSubclassAnnotation.java, codetoanalyze.java.eradicate.SubclassExample$D.deref(codetoanalyze.java.eradicate.SubclassExample$T):void, 0, ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION, no_bucket, WARNING, [First parameter `t` of method `SubclassExample$D.deref(...)` is not `@Nullable` but is declared `@Nullable`in the parent class method `SubclassExample$A.deref(...)`.]