diff --git a/infer/models/java/src/android/view/View.java b/infer/models/java/src/android/view/View.java new file mode 100644 index 000000000..659ae868f --- /dev/null +++ b/infer/models/java/src/android/view/View.java @@ -0,0 +1,19 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package android.view; + +public class View { + public T findViewById(int id) { + // STUB: we need signature for tests; implementation is not tested + return null; + } + + public void setId(int id) { + // STUB: we need signature for tests; implementation is not tested + } +} diff --git a/infer/src/nullsafe/AssignmentRule.ml b/infer/src/nullsafe/AssignmentRule.ml index a40f66726..70309ce9a 100644 --- a/infer/src/nullsafe/AssignmentRule.ml +++ b/infer/src/nullsafe/AssignmentRule.ml @@ -65,7 +65,7 @@ let pp_param_name fmt mangled = let mk_description_for_bad_param_passed {model_source; param_signature; actual_param_expression; param_position; function_procname} - ~param_nullability nullability_evidence = + ~param_nullability ~nullability_evidence = let nullability_evidence_as_suffix = Option.value_map nullability_evidence ~f:(fun evidence -> ": " ^ evidence) ~default:"" in @@ -161,11 +161,22 @@ let violation_description {nullsafe_mode; rhs} ~assignment_location assignment_t Option.value_map nullability_evidence ~f:(fun evidence -> ": " ^ evidence) ~default:"" in let module MF = MarkupFormatter in + let alternative_method_description = + ErrorRenderingUtils.find_alternative_nonnull_method_description rhs_origin + in + let alternative_recommendation = + Option.value_map alternative_method_description + ~f:(fun descr -> + Format.asprintf " If you don't expect null, use %a instead." MF.pp_monospaced descr ) + ~default:"" + in let error_message = match assignment_type with | PassingParamToFunction function_info -> - mk_description_for_bad_param_passed function_info nullability_evidence - ~param_nullability:rhs + Format.sprintf "%s%s" + (mk_description_for_bad_param_passed function_info ~nullability_evidence + ~param_nullability:rhs) + alternative_recommendation | AssigningToField field_name -> let rhs_description = Nullability.( @@ -180,9 +191,9 @@ let violation_description {nullsafe_mode; rhs} ~assignment_location assignment_t nullability %a" Nullability.pp other) in - Format.asprintf "%a is declared non-nullable but is assigned %s%s." MF.pp_monospaced + Format.asprintf "%a is declared non-nullable but is assigned %s%s.%s" MF.pp_monospaced (Fieldname.get_field_name field_name) - rhs_description nullability_evidence_as_suffix + rhs_description nullability_evidence_as_suffix alternative_recommendation | ReturningFromFunction function_proc_name -> let return_description = Nullability.( @@ -198,10 +209,11 @@ let violation_description {nullsafe_mode; rhs} ~assignment_location assignment_t nullability %a" Nullability.pp other) in - Format.asprintf "%a: return type is declared non-nullable but the method returns %s%s." + Format.asprintf + "%a: return type is declared non-nullable but the method returns %s%s.%s" MF.pp_monospaced (Procname.to_simplified_string ~withclass:false function_proc_name) - return_description nullability_evidence_as_suffix + return_description nullability_evidence_as_suffix alternative_recommendation in let issue_type = get_issue_type assignment_type in (error_message, issue_type, assignment_location) diff --git a/infer/src/nullsafe/DereferenceRule.ml b/infer/src/nullsafe/DereferenceRule.ml index 2e40e0c2f..321475637 100644 --- a/infer/src/nullsafe/DereferenceRule.ml +++ b/infer/src/nullsafe/DereferenceRule.ml @@ -79,21 +79,30 @@ let violation_description {nullsafe_mode; nullability} ~dereference_location der | ArrayLengthAccess -> "accessing its length" in - let suffix = + let origin_descr = get_origin_opt ~nullable_object_descr nullable_object_origin |> Option.bind ~f:(fun origin -> TypeOrigin.get_description origin) |> Option.value_map ~f:(fun origin -> ": " ^ origin) ~default:"" in + let alternative_method_description = + ErrorRenderingUtils.find_alternative_nonnull_method_description nullable_object_origin + in + let alternative_recommendation = + Option.value_map alternative_method_description + ~f:(fun descr -> + Format.asprintf " If this is intentional, use %a instead." MF.pp_monospaced descr ) + ~default:"" + in let description = match nullability with | Nullability.Null -> Format.sprintf "NullPointerException will be thrown at this line! %s is `null` and is dereferenced \ via %s%s." - what_is_dereferred_str action_descr suffix + what_is_dereferred_str action_descr origin_descr | Nullability.Nullable -> - Format.sprintf "%s is nullable and is not locally checked for null when %s%s." - what_is_dereferred_str action_descr suffix + Format.sprintf "%s is nullable and is not locally checked for null when %s%s.%s" + what_is_dereferred_str action_descr origin_descr alternative_recommendation | other -> Logging.die InternalError "violation_description:: invariant violation: unexpected nullability %a" diff --git a/infer/src/nullsafe/ErrorRenderingUtils.ml b/infer/src/nullsafe/ErrorRenderingUtils.ml index 7ca49d2a9..f762df279 100644 --- a/infer/src/nullsafe/ErrorRenderingUtils.ml +++ b/infer/src/nullsafe/ErrorRenderingUtils.ml @@ -215,3 +215,20 @@ let mk_special_nullsafe_issue ~nullsafe_mode ~bad_nullability ~bad_usage_locatio bad_usage_location.Location.line recommendation in (description, issue_type, object_loc) + + +let find_alternative_nonnull_method_description nullable_origin = + let open IOption.Let_syntax in + match nullable_origin with + | TypeOrigin.MethodCall {pname= Procname.Java java_pname as pname} -> + let* ModelTables.{package_name; class_name; method_name} = + Models.find_nonnullable_alternative pname + in + let+ original_package_name = Procname.Java.get_package java_pname in + if String.equal original_package_name package_name then + (* The same package that is from origin - omit name for simplicity *) + class_name ^ "." ^ method_name ^ "()" + else (* Fully qualified name *) + package_name ^ "." ^ class_name ^ "." ^ method_name ^ "()" + | _ -> + None diff --git a/infer/src/nullsafe/ErrorRenderingUtils.mli b/infer/src/nullsafe/ErrorRenderingUtils.mli index 57f45c167..20b40a911 100644 --- a/infer/src/nullsafe/ErrorRenderingUtils.mli +++ b/infer/src/nullsafe/ErrorRenderingUtils.mli @@ -23,3 +23,8 @@ val mk_special_nullsafe_issue : strict and local mode. Returns a tuple (error message, issue type, error location). NOTE: Location of the error will be NOT in the place when the value is used (that is [bad_usage_location]), but where the value is first obtained from. *) + +val find_alternative_nonnull_method_description : TypeOrigin.t -> string option +(** If type origin is the result of a nullable method call that have a known nonnullable alternative + (the one that does the check inside), return the string representation of that alternative + suitable for error messaging. *) diff --git a/infer/src/nullsafe/modelTables.ml b/infer/src/nullsafe/modelTables.ml index fcda1c5a0..8bc2f9e9f 100644 --- a/infer/src/nullsafe/modelTables.ml +++ b/infer/src/nullsafe/modelTables.ml @@ -224,373 +224,405 @@ let mapPut_list = ; (cp, "java.util.Map.put(java.lang.Object,java.lang.Object):java.lang.Object") ] +type nonnull_alternative_method = {package_name: string; class_name: string; method_name: string} + +(* Nullable methods that have non-nullable signatures. + Format is a triple: (, , ), *) +let nullable_methods_with_nonnull_alternatives_list = + [ ( (n, [o]) + , "android.view.View.findViewById(int):android.view.View" + , {package_name= "android.view"; class_name= "View"; method_name= "requireViewById"} ) + ; ( (n, [o]) + , "android.app.Activity.findViewById(int):android.view.View" + , {package_name= "android.app"; class_name= "Activity"; method_name= "requireViewById"} ) ] + + +let nullable_method_with_nonnull_alternatives_nullability_list = + let result = + List.map nullable_methods_with_nonnull_alternatives_list + ~f:(fun (nullability, method_descr, _) -> (nullability, method_descr)) + in + List.iter result ~f:(fun ((ret_nullability, _param_nullability), _) -> + if not (ret_nullability == n) then + Logging.die Logging.InternalError "Function is expected to be nullable" ) ; + result + + +(* Methods with signatures that are not special enough to be described in one of lists above *) +let annotated_list_nullability_other = + [ (o1, "android.text.SpannableString.valueOf(java.lang.CharSequence):android.text.SpannableString") + ; (o1, "android.app.AlarmManager.cancel(android.app.PendingIntent):void") + ; (o1, "android.net.Uri.parse(java.lang.String):android.net.Uri") + ; (n1, "android.os.Parcel.writeList(java.util.List):void") + ; (n2, "android.os.Parcel.writeParcelable(android.os.Parcelable,int):void") + ; (n1, "android.os.Parcel.writeString(java.lang.String):void") + ; ( (o, [o; o; n; n; n]) + , "com.android.sdklib.build.ApkBuilder.(java.io.File,java.io.File,java.io.File,java.lang.String,java.io.PrintStream)" + ) + ; ( (o, [n]) + , "com.android.manifmerger.ManifestMerger.xmlFileAndLine(org.w3c.dom.Node):com.android.manifmerger.IMergerLog$FileAndLine" + ) + ; ( on + , "com.android.util.CommandLineParser$Mode.process(com.android.util.CommandLineParser$Arg,java.lang.String):java.lang.Object" + ) + ; ( on + , "com.google.common.base.Objects$ToStringHelper.add(java.lang.String,java.lang.Object):com.google.common.base.Objects$ToStringHelper" + ) + ; (n2, "com.google.common.base.Objects.equal(java.lang.Object,java.lang.Object):boolean") + ; ( n1 + , "com.google.common.base.Optional.fromNullable(java.lang.Object):com.google.common.base.Optional" + ) + ; ((n, []), "com.google.common.base.Optional.orNull():java.lang.Object") + ; (n1, "com.google.common.base.Strings.nullToEmpty(java.lang.String):java.lang.String") + ; (cg, "com.google.common.collect.ImmutableMap.get(java.lang.Object):java.lang.Object") + ; (* container get *) + ( o1 + , "com.google.common.collect.ImmutableList$Builder.add(java.lang.Object):com.google.common.collect.ImmutableList$Builder" + ) + ; ( o1 + , "com.google.common.collect.ImmutableList$Builder.addAll(java.lang.Iterable):com.google.common.collect.ImmutableList$Builder" + ) + ; ( o1 + , "com.google.common.collect.ImmutableList.of(java.lang.Object):com.google.common.collect.ImmutableList" + ) + ; ( o2 + , "com.google.common.collect.ImmutableList.of(java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableList" + ) + ; ( o3 + , "com.google.common.collect.ImmutableList.of(java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableList" + ) + ; ( o4 + , "com.google.common.collect.ImmutableList.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableList" + ) + ; ( o5 + , "com.google.common.collect.ImmutableList.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableList" + ) + ; ( o6 + , "com.google.common.collect.ImmutableList.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableList" + ) + ; ( o7 + , "com.google.common.collect.ImmutableList.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableList" + ) + ; ( o8 + , "com.google.common.collect.ImmutableList.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableList" + ) + ; ( o9 + , "com.google.common.collect.ImmutableList.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableList" + ) + ; ( o10 + , "com.google.common.collect.ImmutableList.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableList" + ) + ; ( o11 + , "com.google.common.collect.ImmutableList.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableList" + ) + ; ( o12 + , "com.google.common.collect.ImmutableList.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableList" + ) + ; ( o1 + , "com.google.common.collect.ImmutableList.copyOf(java.lang.Iterable):com.google.common.collect.ImmutableList" + ) + ; ( o1 + , "com.google.common.collect.ImmutableList.copyOf(java.util.Collection):com.google.common.collect.ImmutableList" + ) + ; ( o1 + , "com.google.common.collect.ImmutableList.copyOf(java.util.Iterator):com.google.common.collect.ImmutableList" + ) + ; ( o1 + , "com.google.common.collect.ImmutableList.copyOf(java.lang.Object):com.google.common.collect.ImmutableList" + ) + ; ( o2 + , "com.google.common.collect.ImmutableList.sortedCopyOf(java.util.Comparator,java.lang.Iterable):com.google.common.collect.ImmutableList" + ) + ; ( o1 + , "com.google.common.collect.ImmutableSet.of(java.lang.Object):com.google.common.collect.ImmutableSet" + ) + ; ( o2 + , "com.google.common.collect.ImmutableSet.of(java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableSet" + ) + ; ( o3 + , "com.google.common.collect.ImmutableSet.of(java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableSet" + ) + ; ( o4 + , "com.google.common.collect.ImmutableSet.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableSet" + ) + ; ( o5 + , "com.google.common.collect.ImmutableSet.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableSet" + ) + ; ( o1 + , "com.google.common.collect.ImmutableSet.copyOf(java.lang.Iterable):com.google.common.collect.ImmutableSet" + ) + ; ( o1 + , "com.google.common.collect.ImmutableSet.copyOf(java.util.Collection):com.google.common.collect.ImmutableSet" + ) + ; ( o1 + , "com.google.common.collect.ImmutableSet.copyOf(java.util.Iterator):com.google.common.collect.ImmutableSet" + ) + ; ( o1 + , "com.google.common.collect.ImmutableSet.copyOf(java.lang.Object):com.google.common.collect.ImmutableSet" + ) + ; ( o1 + , "com.google.common.collect.ImmutableSortedSet$Builder.add(java.lang.Object):com.google.common.collect.ImmutableSortedSet$Builder" + ) + ; ( o2 + , "com.google.common.collect.ImmutableMap.of(java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableMap" + ) + ; ( o4 + , "com.google.common.collect.ImmutableMap.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableMap" + ) + ; ( o6 + , "com.google.common.collect.ImmutableMap.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableMap" + ) + ; ( o8 + , "com.google.common.collect.ImmutableMap.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableMap" + ) + ; ( o10 + , "com.google.common.collect.ImmutableMap.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableMap" + ) + ; ( o1 + , "com.google.common.collect.ImmutableMap.copyOf(java.util.Map):com.google.common.collect.ImmutableMap" + ) + ; ( o1 + , "com.google.common.collect.ImmutableMap.copyOf(java.lang.Iterable):com.google.common.collect.ImmutableMap" + ) + ; ( on + , "com.google.common.collect.Iterables.getFirst(java.lang.Iterable,java.lang.Object):java.lang.Object" + ) + ; ( o1 + , "com.google.common.util.concurrent.SettableFuture.setException(java.lang.Throwable):boolean" + ) + ; (o1, "java.io.File.(java.lang.String)") + ; (n1, "java.io.PrintStream.print(java.lang.String):void") + ; ((n, [o]), "java.lang.Class.getResource(java.lang.String):java.net.URL") + ; (o1, "java.lang.Class.isAssignableFrom(java.lang.Class):boolean") + ; (n1, "java.lang.Integer.equals(java.lang.Object):boolean") + ; (o1, "java.lang.Integer.parseInt(java.lang.String):int") + ; (o1, "java.lang.Long.parseLong(java.lang.String):long") + ; (n1, "java.lang.Object.equals(java.lang.Object):boolean") + ; (n2, "java.lang.RuntimeException.(java.lang.String,java.lang.Throwable)") + ; (n1, "java.lang.String.equals(java.lang.Object):boolean") + ; (n1, "java.lang.StringBuilder.append(java.lang.String):java.lang.StringBuilder") + ; ((n, [o]), "java.lang.System.getProperty(java.lang.String):java.lang.String") + ; ((n, [o]), "java.lang.System.getenv(java.lang.String):java.lang.String") + ; ( on + , "java.net.URLClassLoader.newInstance(java.net.URL[],java.lang.ClassLoader):java.net.URLClassLoader" + ) + ; (ng, "java.nio.file.Path.getParent():java.nio.file.Path") + ; (n1, "java.util.AbstractList.equals(java.lang.Object):boolean") + ; (ca, "java.util.ArrayList.add(java.lang.Object):boolean") + ; (ca, "java.util.List.add(java.lang.Object):boolean") + ; (cg, "java.util.Map.get(java.lang.Object):java.lang.Object") + ; (cr, "java.util.Map.remove(java.lang.Object):java.lang.Object") + ; (cp, "java.util.Map.put(java.lang.Object,java.lang.Object):java.lang.Object") + ; (cg, "java.util.HashMap.get(java.lang.Object):java.lang.Object") + ; (cr, "java.util.HashMap.remove(java.lang.Object):java.lang.Object") + ; (cp, "java.util.HashMap.put(java.lang.Object,java.lang.Object):java.lang.Object") + ; (cg, "java.util.concurrent.ConcurrentHashMap.get(java.lang.Object):java.lang.Object") + ; (cr, "java.util.concurrent.ConcurrentHashMap.remove(java.lang.Object):java.lang.Object") + ; (cg, "java.util.AbstractMap.get(java.lang.Object):java.lang.Object") + ; (cr, "java.util.AbstractMap.remove(java.lang.Object):java.lang.Object") + ; (cg, "java.util.concurrent.ConcurrentSkipListMap.get(java.lang.Object):java.lang.Object") + ; (cr, "java.util.concurrent.ConcurrentSkipListMap.remove(java.lang.Object):java.lang.Object") + ; (cg, "java.util.EnumMap.get(java.lang.Object):java.lang.Object") + ; (cr, "java.util.EnumMap.remove(java.lang.Object):java.lang.Object") + ; (cg, "java.util.Hashtable.get(java.lang.Object):java.lang.Object") + ; (cr, "java.util.Hashtable.remove(java.lang.Object):java.lang.Object") + ; (cg, "java.util.IdentityHashMap.get(java.lang.Object):java.lang.Object") + ; (cr, "java.util.IdentityHashMap.remove(java.lang.Object):java.lang.Object") + ; (cg, "java.util.LinkedHashMap.get(java.lang.Object):java.lang.Object") + ; (cr, "java.util.LinkedHashMap.remove(java.lang.Object):java.lang.Object") + ; (cg, "java.util.TreeMap.get(java.lang.Object):java.lang.Object") + ; (cr, "java.util.TreeMap.remove(java.lang.Object):java.lang.Object") + ; (cg, "java.util.WeakHashMap.get(java.lang.Object):java.lang.Object") + ; (cr, "java.util.WeakHashMap.remove(java.lang.Object):java.lang.Object") + ; ( (n, [o]) + , "javax.lang.model.element.Element.getAnnotation(java.lang.Class):java.lang.annotation.Annotation" + ) + ; (ng, "javax.lang.model.element.Element.getEnclosingElement():javax.lang.model.element.Element") + ; ( ng + , "javax.lang.model.element.ExecutableElement.getDefaultValue():javax.lang.model.element.AnnotationValue" + ) + ; ( ng + , "javax.lang.model.element.PackageElement.getEnclosingElement():javax.lang.model.element.Element" + ) + ; (ng, "javax.lang.model.element.VariableElement.getConstantValue():java.lang.Object") + ; (ng, "javax.lang.model.type.WildcardType.getSuperBound():javax.lang.model.type.TypeMirror") + ; ( (n, [o]) + , "javax.lang.model.util.Elements.getPackageElement(java.lang.CharSequence):javax.lang.model.element.PackageElement" + ) + ; ( (n, [o]) + , "javax.lang.model.util.Elements.getTypeElement(java.lang.CharSequence):javax.lang.model.element.TypeElement" + ) + ; ( (n, [o]) + , "javax.lang.model.util.Elements.getDocComment(javax.lang.model.element.Element):java.lang.String" + ) + ; ( o1 + , "javax.lang.model.util.Elements.getElementValuesWithDefaults(javax.lang.model.element.AnnotationMirror):java.util.Map" + ) + ; (o1, "javax.lang.model.util.Elements.isDeprecated(javax.lang.model.element.Element):boolean") + ; ( o1 + , "javax.lang.model.util.Elements.getBinaryName(javax.lang.model.element.TypeElement):javax.lang.model.element.Name" + ) + ; ( o1 + , "javax.lang.model.util.Elements.getPackageOf(javax.lang.model.element.Element):javax.lang.model.element.PackageElement" + ) + ; ( o1 + , "javax.lang.model.util.Elements.getAllMembers(javax.lang.model.element.TypeElement):java.util.List" + ) + ; ( o1 + , "javax.lang.model.util.Elements.getAllAnnotationMirrors(javax.lang.model.element.Element):java.util.List" + ) + ; ( o2 + , "javax.lang.model.util.Elements.hides(javax.lang.model.element.Element, \ + javax.lang.model.element.Element):boolean" ) + ; ( o3 + , "javax.lang.model.util.Elements.overrides(javax.lang.model.element.ExecutableElement, \ + javax.lang.model.element.ExecutableElement, javax.lang.model.element.TypeElement):boolean" ) + ; ( o1 + , "javax.lang.model.util.Types.asElement(javax.lang.model.type.TypeMirror):javax.lang.model.element.Element" + ) + ; ( o2 + , "javax.lang.model.util.Types.isSameType(javax.lang.model.type.TypeMirror, \ + javax.lang.model.type.TypeMirror):boolean" ) + ; ( o2 + , "javax.lang.model.util.Types.isSubtype(javax.lang.model.type.TypeMirror, \ + javax.lang.model.type.TypeMirror):boolean" ) + ; ( o2 + , "javax.lang.model.util.Types.isAssignable(javax.lang.model.type.TypeMirror, \ + javax.lang.model.type.TypeMirror):boolean" ) + ; ( o2 + , "javax.lang.model.util.Types.contains(javax.lang.model.type.TypeMirror, \ + javax.lang.model.type.TypeMirror):boolean" ) + ; ( o2 + , "javax.lang.model.util.Types.isSubsignature(javax.lang.model.type.ExecutableType, \ + javax.lang.model.type.ExecutableType):boolean" ) + ; ( o1 + , "javax.lang.model.util.Types.directSupertypes(javax.lang.model.type.TypeMirror):java.util.List" + ) + ; ( o1 + , "javax.lang.model.util.Types.erasure(javax.lang.model.type.TypeMirror):javax.lang.model.type.TypeMirror" + ) + ; ( o1 + , "javax.lang.model.util.Types.boxedClass(javax.lang.model.type.PrimitiveType):javax.lang.model.element.TypeElement" + ) + ; ( o1 + , "javax.lang.model.util.Types.unboxedType(javax.lang.model.type.TypeMirror):javax.lang.model.type.PrimitiveType" + ) + ; ( o1 + , "javax.lang.model.util.Types.capture(javax.lang.model.type.TypeMirror):javax.lang.model.type.TypeMirror" + ) + ; ( o1 + , "javax.lang.model.util.Types.getArrayType(javax.lang.model.type.TypeMirror):javax.lang.model.type.ArrayType" + ) + ; ( o2 + , "javax.lang.model.util.Types.getWildcardType(javax.lang.model.type.TypeMirror, \ + javax.lang.model.type.TypeMirror):javax.lang.model.type.WildcardType" ) + ; ( o2 + , "javax.lang.model.util.Types.getDeclaredType(javax.lang.model.element.TypeElement, \ + javax.lang.model.type.TypeMirror[]):javax.lang.model.type.DeclaredType" ) + ; ( o3 + , "javax.lang.model.util.Types.getDeclaredType(javax.lang.model.type.DeclaredType, \ + javax.lang.model.element.TypeElement, \ + javax.lang.model.type.TypeMirror[]):javax.lang.model.type.DeclaredType" ) + ; ( o2 + , "javax.lang.model.util.Types.asMemberOf(javax.lang.model.type.DeclaredType, \ + javax.lang.model.element.Element):javax.lang.model.type.TypeMirror" ) + ; ( n3 + , "javax.tools.JavaCompiler.getStandardFileManager(javax.tools.DiagnosticListener,java.util.Locale,java.nio.charset.Charset):javax.tools.StandardJavaFileManager" + ) + ; (ng, "javax.tools.JavaFileObject.getAccessLevel():javax.lang.model.element.Modifier") + ; (ng, "javax.tools.JavaFileObject.getNestingKind():javax.lang.model.element.NestingKind") + ; ( o2 + , "com.sun.source.util.SourcePositions.getStartPosition(com.sun.source.tree.CompilationUnitTree, \ + com.sun.source.tree.Tree):long" ) + ; ( o2 + , "com.sun.source.util.SourcePositions.getEndPosition(com.sun.source.tree.CompilationUnitTree, \ + com.sun.source.tree.Tree):long" ) + ; ( (n, [o; o]) + , "com.sun.source.util.TreePath.getPath(com.sun.source.tree.CompilationUnitTree, \ + com.sun.source.tree.Tree):com.sun.source.util.TreePath" ) + ; ( (n, [o; o]) + , "com.sun.source.util.TreePath.getPath(com.sun.source.util.TreePath, \ + com.sun.source.tree.Tree):com.sun.source.util.TreePath" ) + ; ( (n, [o]) + , "com.sun.source.util.Trees.getTree(javax.lang.model.element.Element):com.sun.source.tree.Tree" + ) + ; ( (n, [o]) + , "com.sun.source.util.Trees.getTree(javax.lang.model.element.TypeElement):com.sun.source.tree.ClassTree" + ) + ; ( (n, [o]) + , "com.sun.source.util.Trees.getTree(javax.lang.model.element.ExecutableElement):com.sun.source.tree.MethodTree" + ) + ; ( (n, [o; o]) + , "com.sun.source.util.Trees.getTree(javax.lang.model.element.Element, \ + javax.lang.model.element.AnnotationMirror):com.sun.source.tree.Tree" ) + ; ( (n, [o; o; o]) + , "com.sun.source.util.Trees.getTree(javax.lang.model.element.Element, \ + javax.lang.model.element.AnnotationMirror, \ + javax.lang.model.element.AnnotationValue):com.sun.source.tree.Tree" ) + ; ( o2 + , "com.sun.source.util.Trees.getPath(com.sun.source.tree.CompilationUnitTree, \ + com.sun.source.tree.Tree):com.sun.source.util.TreePath" ) + ; ( (n, [o]) + , "com.sun.source.util.Trees.getPath(javax.lang.model.element.Element):com.sun.source.util.TreePath" + ) + ; ( (n, [o; o]) + , "com.sun.source.util.Trees.getPath(javax.lang.model.element.Element, \ + javax.lang.model.element.AnnotationMirror):com.sun.source.util.TreePath" ) + ; ( (n, [o; o; o]) + , "com.sun.source.util.Trees.getPath(javax.lang.model.element.Element, \ + javax.lang.model.element.AnnotationMirror, \ + javax.lang.model.element.AnnotationValue):com.sun.source.util.TreePath" ) + ; ( (n, [o]) + , "com.sun.source.util.Trees.getElement(com.sun.source.util.TreePath):javax.lang.model.element.Element" + ) + ; ( (n, [o]) + , "com.sun.source.util.Trees.getTypeMirror(com.sun.source.util.TreePath):javax.lang.model.type.TypeMirror" + ) + ; ( (n, [o]) + , "com.sun.source.util.Trees.getScope(com.sun.source.util.TreePath):com.sun.source.tree.Scope" + ) + ; ( (n, [o]) + , "com.sun.source.util.Trees.getDocComment(com.sun.source.util.TreePath):java.lang.String" ) + ; ( o2 + , "com.sun.source.util.Trees.isAccessible(com.sun.source.tree.Scope, \ + javax.lang.model.element.TypeElement):boolean" ) + ; ( o3 + , "com.sun.source.util.Trees.isAccessible(com.sun.source.tree.Scope, \ + javax.lang.model.element.Element, javax.lang.model.type.DeclaredType):boolean" ) + ; ( o1 + , "com.sun.source.util.Trees.getOriginalType(javax.lang.model.type.ErrorType):javax.lang.model.type.TypeMirror" + ) + ; ( (o, [o; o; o; o]) + , "com.sun.source.util.Trees.printMessage(javax.tools.Diagnostic.Kind, java.lang.CharSequence, \ + com.sun.source.tree.Tree, com.sun.source.tree.CompilationUnitTree):void" ) + ; ( o1 + , "com.sun.source.util.Trees.getLub(com.sun.source.tree.CatchTree):javax.lang.model.type.TypeMirror" + ) + ; ( (n, [o; n; n]) + , "org.w3c.dom.Document.setUserData(java.lang.String,java.lang.Object,org.w3c.dom.UserDataHandler):java.lang.Object" + ) + ; ( (n, [o; n; n]) + , "org.w3c.dom.Node.setUserData(java.lang.String,java.lang.Object,org.w3c.dom.UserDataHandler):java.lang.Object" + ) + ; (* References *) + (ng, "java.lang.ref.Reference.get():java.lang.Object") + ; (ng, "java.lang.ref.PhantomReference.get():java.lang.Object") + ; (ng, "java.lang.ref.SoftReference.get():java.lang.Object") + ; (ng, "java.lang.ref.WeakReference.get():java.lang.Object") + ; (ng, "java.util.concurrent.atomic.AtomicReference.get():java.lang.Object") ] + + (** Models for nullability *) let annotated_list_nullability = - check_not_null_list @ check_state_list @ check_argument_list @ true_on_null_list - @ [ ( o1 - , "android.text.SpannableString.valueOf(java.lang.CharSequence):android.text.SpannableString" - ) - ; (o1, "android.app.AlarmManager.cancel(android.app.PendingIntent):void") - ; (o1, "android.net.Uri.parse(java.lang.String):android.net.Uri") - ; (n1, "android.os.Parcel.writeList(java.util.List):void") - ; (n2, "android.os.Parcel.writeParcelable(android.os.Parcelable,int):void") - ; (n1, "android.os.Parcel.writeString(java.lang.String):void") - ; ( (o, [o; o; n; n; n]) - , "com.android.sdklib.build.ApkBuilder.(java.io.File,java.io.File,java.io.File,java.lang.String,java.io.PrintStream)" - ) - ; ( (o, [n]) - , "com.android.manifmerger.ManifestMerger.xmlFileAndLine(org.w3c.dom.Node):com.android.manifmerger.IMergerLog$FileAndLine" - ) - ; ( on - , "com.android.util.CommandLineParser$Mode.process(com.android.util.CommandLineParser$Arg,java.lang.String):java.lang.Object" - ) - ; ( on - , "com.google.common.base.Objects$ToStringHelper.add(java.lang.String,java.lang.Object):com.google.common.base.Objects$ToStringHelper" - ) - ; (n2, "com.google.common.base.Objects.equal(java.lang.Object,java.lang.Object):boolean") - ; ( n1 - , "com.google.common.base.Optional.fromNullable(java.lang.Object):com.google.common.base.Optional" - ) - ; ((n, []), "com.google.common.base.Optional.orNull():java.lang.Object") - ; (n1, "com.google.common.base.Strings.nullToEmpty(java.lang.String):java.lang.String") - ; (cg, "com.google.common.collect.ImmutableMap.get(java.lang.Object):java.lang.Object") - ; (* container get *) - ( o1 - , "com.google.common.collect.ImmutableList$Builder.add(java.lang.Object):com.google.common.collect.ImmutableList$Builder" - ) - ; ( o1 - , "com.google.common.collect.ImmutableList$Builder.addAll(java.lang.Iterable):com.google.common.collect.ImmutableList$Builder" - ) - ; ( o1 - , "com.google.common.collect.ImmutableList.of(java.lang.Object):com.google.common.collect.ImmutableList" - ) - ; ( o2 - , "com.google.common.collect.ImmutableList.of(java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableList" - ) - ; ( o3 - , "com.google.common.collect.ImmutableList.of(java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableList" - ) - ; ( o4 - , "com.google.common.collect.ImmutableList.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableList" - ) - ; ( o5 - , "com.google.common.collect.ImmutableList.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableList" - ) - ; ( o6 - , "com.google.common.collect.ImmutableList.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableList" - ) - ; ( o7 - , "com.google.common.collect.ImmutableList.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableList" - ) - ; ( o8 - , "com.google.common.collect.ImmutableList.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableList" - ) - ; ( o9 - , "com.google.common.collect.ImmutableList.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableList" - ) - ; ( o10 - , "com.google.common.collect.ImmutableList.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableList" - ) - ; ( o11 - , "com.google.common.collect.ImmutableList.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableList" - ) - ; ( o12 - , "com.google.common.collect.ImmutableList.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableList" - ) - ; ( o1 - , "com.google.common.collect.ImmutableList.copyOf(java.lang.Iterable):com.google.common.collect.ImmutableList" - ) - ; ( o1 - , "com.google.common.collect.ImmutableList.copyOf(java.util.Collection):com.google.common.collect.ImmutableList" - ) - ; ( o1 - , "com.google.common.collect.ImmutableList.copyOf(java.util.Iterator):com.google.common.collect.ImmutableList" - ) - ; ( o1 - , "com.google.common.collect.ImmutableList.copyOf(java.lang.Object):com.google.common.collect.ImmutableList" - ) - ; ( o2 - , "com.google.common.collect.ImmutableList.sortedCopyOf(java.util.Comparator,java.lang.Iterable):com.google.common.collect.ImmutableList" - ) - ; ( o1 - , "com.google.common.collect.ImmutableSet.of(java.lang.Object):com.google.common.collect.ImmutableSet" - ) - ; ( o2 - , "com.google.common.collect.ImmutableSet.of(java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableSet" - ) - ; ( o3 - , "com.google.common.collect.ImmutableSet.of(java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableSet" - ) - ; ( o4 - , "com.google.common.collect.ImmutableSet.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableSet" - ) - ; ( o5 - , "com.google.common.collect.ImmutableSet.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableSet" - ) - ; ( o1 - , "com.google.common.collect.ImmutableSet.copyOf(java.lang.Iterable):com.google.common.collect.ImmutableSet" - ) - ; ( o1 - , "com.google.common.collect.ImmutableSet.copyOf(java.util.Collection):com.google.common.collect.ImmutableSet" - ) - ; ( o1 - , "com.google.common.collect.ImmutableSet.copyOf(java.util.Iterator):com.google.common.collect.ImmutableSet" - ) - ; ( o1 - , "com.google.common.collect.ImmutableSet.copyOf(java.lang.Object):com.google.common.collect.ImmutableSet" - ) - ; ( o1 - , "com.google.common.collect.ImmutableSortedSet$Builder.add(java.lang.Object):com.google.common.collect.ImmutableSortedSet$Builder" - ) - ; ( o2 - , "com.google.common.collect.ImmutableMap.of(java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableMap" - ) - ; ( o4 - , "com.google.common.collect.ImmutableMap.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableMap" - ) - ; ( o6 - , "com.google.common.collect.ImmutableMap.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableMap" - ) - ; ( o8 - , "com.google.common.collect.ImmutableMap.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableMap" - ) - ; ( o10 - , "com.google.common.collect.ImmutableMap.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableMap" - ) - ; ( o1 - , "com.google.common.collect.ImmutableMap.copyOf(java.util.Map):com.google.common.collect.ImmutableMap" - ) - ; ( o1 - , "com.google.common.collect.ImmutableMap.copyOf(java.lang.Iterable):com.google.common.collect.ImmutableMap" - ) - ; ( on - , "com.google.common.collect.Iterables.getFirst(java.lang.Iterable,java.lang.Object):java.lang.Object" - ) - ; ( o1 - , "com.google.common.util.concurrent.SettableFuture.setException(java.lang.Throwable):boolean" - ) - ; (o1, "java.io.File.(java.lang.String)") - ; (n1, "java.io.PrintStream.print(java.lang.String):void") - ; ((n, [o]), "java.lang.Class.getResource(java.lang.String):java.net.URL") - ; (o1, "java.lang.Class.isAssignableFrom(java.lang.Class):boolean") - ; (n1, "java.lang.Integer.equals(java.lang.Object):boolean") - ; (o1, "java.lang.Integer.parseInt(java.lang.String):int") - ; (o1, "java.lang.Long.parseLong(java.lang.String):long") - ; (n1, "java.lang.Object.equals(java.lang.Object):boolean") - ; (n2, "java.lang.RuntimeException.(java.lang.String,java.lang.Throwable)") - ; (n1, "java.lang.String.equals(java.lang.Object):boolean") - ; (n1, "java.lang.StringBuilder.append(java.lang.String):java.lang.StringBuilder") - ; ((n, [o]), "java.lang.System.getProperty(java.lang.String):java.lang.String") - ; ((n, [o]), "java.lang.System.getenv(java.lang.String):java.lang.String") - ; ( on - , "java.net.URLClassLoader.newInstance(java.net.URL[],java.lang.ClassLoader):java.net.URLClassLoader" - ) - ; (ng, "java.nio.file.Path.getParent():java.nio.file.Path") - ; (n1, "java.util.AbstractList.equals(java.lang.Object):boolean") - ; (ca, "java.util.ArrayList.add(java.lang.Object):boolean") - ; (ca, "java.util.List.add(java.lang.Object):boolean") - ; (cg, "java.util.Map.get(java.lang.Object):java.lang.Object") - ; (cr, "java.util.Map.remove(java.lang.Object):java.lang.Object") - ; (cp, "java.util.Map.put(java.lang.Object,java.lang.Object):java.lang.Object") - ; (cg, "java.util.HashMap.get(java.lang.Object):java.lang.Object") - ; (cr, "java.util.HashMap.remove(java.lang.Object):java.lang.Object") - ; (cp, "java.util.HashMap.put(java.lang.Object,java.lang.Object):java.lang.Object") - ; (cg, "java.util.concurrent.ConcurrentHashMap.get(java.lang.Object):java.lang.Object") - ; (cr, "java.util.concurrent.ConcurrentHashMap.remove(java.lang.Object):java.lang.Object") - ; (cg, "java.util.AbstractMap.get(java.lang.Object):java.lang.Object") - ; (cr, "java.util.AbstractMap.remove(java.lang.Object):java.lang.Object") - ; (cg, "java.util.concurrent.ConcurrentSkipListMap.get(java.lang.Object):java.lang.Object") - ; (cr, "java.util.concurrent.ConcurrentSkipListMap.remove(java.lang.Object):java.lang.Object") - ; (cg, "java.util.EnumMap.get(java.lang.Object):java.lang.Object") - ; (cr, "java.util.EnumMap.remove(java.lang.Object):java.lang.Object") - ; (cg, "java.util.Hashtable.get(java.lang.Object):java.lang.Object") - ; (cr, "java.util.Hashtable.remove(java.lang.Object):java.lang.Object") - ; (cg, "java.util.IdentityHashMap.get(java.lang.Object):java.lang.Object") - ; (cr, "java.util.IdentityHashMap.remove(java.lang.Object):java.lang.Object") - ; (cg, "java.util.LinkedHashMap.get(java.lang.Object):java.lang.Object") - ; (cr, "java.util.LinkedHashMap.remove(java.lang.Object):java.lang.Object") - ; (cg, "java.util.TreeMap.get(java.lang.Object):java.lang.Object") - ; (cr, "java.util.TreeMap.remove(java.lang.Object):java.lang.Object") - ; (cg, "java.util.WeakHashMap.get(java.lang.Object):java.lang.Object") - ; (cr, "java.util.WeakHashMap.remove(java.lang.Object):java.lang.Object") - ; ( (n, [o]) - , "javax.lang.model.element.Element.getAnnotation(java.lang.Class):java.lang.annotation.Annotation" - ) - ; (ng, "javax.lang.model.element.Element.getEnclosingElement():javax.lang.model.element.Element") - ; ( ng - , "javax.lang.model.element.ExecutableElement.getDefaultValue():javax.lang.model.element.AnnotationValue" - ) - ; ( ng - , "javax.lang.model.element.PackageElement.getEnclosingElement():javax.lang.model.element.Element" - ) - ; (ng, "javax.lang.model.element.VariableElement.getConstantValue():java.lang.Object") - ; (ng, "javax.lang.model.type.WildcardType.getSuperBound():javax.lang.model.type.TypeMirror") - ; ( (n, [o]) - , "javax.lang.model.util.Elements.getPackageElement(java.lang.CharSequence):javax.lang.model.element.PackageElement" - ) - ; ( (n, [o]) - , "javax.lang.model.util.Elements.getTypeElement(java.lang.CharSequence):javax.lang.model.element.TypeElement" - ) - ; ( (n, [o]) - , "javax.lang.model.util.Elements.getDocComment(javax.lang.model.element.Element):java.lang.String" - ) - ; ( o1 - , "javax.lang.model.util.Elements.getElementValuesWithDefaults(javax.lang.model.element.AnnotationMirror):java.util.Map" - ) - ; (o1, "javax.lang.model.util.Elements.isDeprecated(javax.lang.model.element.Element):boolean") - ; ( o1 - , "javax.lang.model.util.Elements.getBinaryName(javax.lang.model.element.TypeElement):javax.lang.model.element.Name" - ) - ; ( o1 - , "javax.lang.model.util.Elements.getPackageOf(javax.lang.model.element.Element):javax.lang.model.element.PackageElement" - ) - ; ( o1 - , "javax.lang.model.util.Elements.getAllMembers(javax.lang.model.element.TypeElement):java.util.List" - ) - ; ( o1 - , "javax.lang.model.util.Elements.getAllAnnotationMirrors(javax.lang.model.element.Element):java.util.List" - ) - ; ( o2 - , "javax.lang.model.util.Elements.hides(javax.lang.model.element.Element, \ - javax.lang.model.element.Element):boolean" ) - ; ( o3 - , "javax.lang.model.util.Elements.overrides(javax.lang.model.element.ExecutableElement, \ - javax.lang.model.element.ExecutableElement, javax.lang.model.element.TypeElement):boolean" - ) - ; ( o1 - , "javax.lang.model.util.Types.asElement(javax.lang.model.type.TypeMirror):javax.lang.model.element.Element" - ) - ; ( o2 - , "javax.lang.model.util.Types.isSameType(javax.lang.model.type.TypeMirror, \ - javax.lang.model.type.TypeMirror):boolean" ) - ; ( o2 - , "javax.lang.model.util.Types.isSubtype(javax.lang.model.type.TypeMirror, \ - javax.lang.model.type.TypeMirror):boolean" ) - ; ( o2 - , "javax.lang.model.util.Types.isAssignable(javax.lang.model.type.TypeMirror, \ - javax.lang.model.type.TypeMirror):boolean" ) - ; ( o2 - , "javax.lang.model.util.Types.contains(javax.lang.model.type.TypeMirror, \ - javax.lang.model.type.TypeMirror):boolean" ) - ; ( o2 - , "javax.lang.model.util.Types.isSubsignature(javax.lang.model.type.ExecutableType, \ - javax.lang.model.type.ExecutableType):boolean" ) - ; ( o1 - , "javax.lang.model.util.Types.directSupertypes(javax.lang.model.type.TypeMirror):java.util.List" - ) - ; ( o1 - , "javax.lang.model.util.Types.erasure(javax.lang.model.type.TypeMirror):javax.lang.model.type.TypeMirror" - ) - ; ( o1 - , "javax.lang.model.util.Types.boxedClass(javax.lang.model.type.PrimitiveType):javax.lang.model.element.TypeElement" - ) - ; ( o1 - , "javax.lang.model.util.Types.unboxedType(javax.lang.model.type.TypeMirror):javax.lang.model.type.PrimitiveType" - ) - ; ( o1 - , "javax.lang.model.util.Types.capture(javax.lang.model.type.TypeMirror):javax.lang.model.type.TypeMirror" - ) - ; ( o1 - , "javax.lang.model.util.Types.getArrayType(javax.lang.model.type.TypeMirror):javax.lang.model.type.ArrayType" - ) - ; ( o2 - , "javax.lang.model.util.Types.getWildcardType(javax.lang.model.type.TypeMirror, \ - javax.lang.model.type.TypeMirror):javax.lang.model.type.WildcardType" ) - ; ( o2 - , "javax.lang.model.util.Types.getDeclaredType(javax.lang.model.element.TypeElement, \ - javax.lang.model.type.TypeMirror[]):javax.lang.model.type.DeclaredType" ) - ; ( o3 - , "javax.lang.model.util.Types.getDeclaredType(javax.lang.model.type.DeclaredType, \ - javax.lang.model.element.TypeElement, \ - javax.lang.model.type.TypeMirror[]):javax.lang.model.type.DeclaredType" ) - ; ( o2 - , "javax.lang.model.util.Types.asMemberOf(javax.lang.model.type.DeclaredType, \ - javax.lang.model.element.Element):javax.lang.model.type.TypeMirror" ) - ; ( n3 - , "javax.tools.JavaCompiler.getStandardFileManager(javax.tools.DiagnosticListener,java.util.Locale,java.nio.charset.Charset):javax.tools.StandardJavaFileManager" - ) - ; (ng, "javax.tools.JavaFileObject.getAccessLevel():javax.lang.model.element.Modifier") - ; (ng, "javax.tools.JavaFileObject.getNestingKind():javax.lang.model.element.NestingKind") - ; ( o2 - , "com.sun.source.util.SourcePositions.getStartPosition(com.sun.source.tree.CompilationUnitTree, \ - com.sun.source.tree.Tree):long" ) - ; ( o2 - , "com.sun.source.util.SourcePositions.getEndPosition(com.sun.source.tree.CompilationUnitTree, \ - com.sun.source.tree.Tree):long" ) - ; ( (n, [o; o]) - , "com.sun.source.util.TreePath.getPath(com.sun.source.tree.CompilationUnitTree, \ - com.sun.source.tree.Tree):com.sun.source.util.TreePath" ) - ; ( (n, [o; o]) - , "com.sun.source.util.TreePath.getPath(com.sun.source.util.TreePath, \ - com.sun.source.tree.Tree):com.sun.source.util.TreePath" ) - ; ( (n, [o]) - , "com.sun.source.util.Trees.getTree(javax.lang.model.element.Element):com.sun.source.tree.Tree" - ) - ; ( (n, [o]) - , "com.sun.source.util.Trees.getTree(javax.lang.model.element.TypeElement):com.sun.source.tree.ClassTree" - ) - ; ( (n, [o]) - , "com.sun.source.util.Trees.getTree(javax.lang.model.element.ExecutableElement):com.sun.source.tree.MethodTree" - ) - ; ( (n, [o; o]) - , "com.sun.source.util.Trees.getTree(javax.lang.model.element.Element, \ - javax.lang.model.element.AnnotationMirror):com.sun.source.tree.Tree" ) - ; ( (n, [o; o; o]) - , "com.sun.source.util.Trees.getTree(javax.lang.model.element.Element, \ - javax.lang.model.element.AnnotationMirror, \ - javax.lang.model.element.AnnotationValue):com.sun.source.tree.Tree" ) - ; ( o2 - , "com.sun.source.util.Trees.getPath(com.sun.source.tree.CompilationUnitTree, \ - com.sun.source.tree.Tree):com.sun.source.util.TreePath" ) - ; ( (n, [o]) - , "com.sun.source.util.Trees.getPath(javax.lang.model.element.Element):com.sun.source.util.TreePath" - ) - ; ( (n, [o; o]) - , "com.sun.source.util.Trees.getPath(javax.lang.model.element.Element, \ - javax.lang.model.element.AnnotationMirror):com.sun.source.util.TreePath" ) - ; ( (n, [o; o; o]) - , "com.sun.source.util.Trees.getPath(javax.lang.model.element.Element, \ - javax.lang.model.element.AnnotationMirror, \ - javax.lang.model.element.AnnotationValue):com.sun.source.util.TreePath" ) - ; ( (n, [o]) - , "com.sun.source.util.Trees.getElement(com.sun.source.util.TreePath):javax.lang.model.element.Element" - ) - ; ( (n, [o]) - , "com.sun.source.util.Trees.getTypeMirror(com.sun.source.util.TreePath):javax.lang.model.type.TypeMirror" - ) - ; ( (n, [o]) - , "com.sun.source.util.Trees.getScope(com.sun.source.util.TreePath):com.sun.source.tree.Scope" - ) - ; ( (n, [o]) - , "com.sun.source.util.Trees.getDocComment(com.sun.source.util.TreePath):java.lang.String" ) - ; ( o2 - , "com.sun.source.util.Trees.isAccessible(com.sun.source.tree.Scope, \ - javax.lang.model.element.TypeElement):boolean" ) - ; ( o3 - , "com.sun.source.util.Trees.isAccessible(com.sun.source.tree.Scope, \ - javax.lang.model.element.Element, javax.lang.model.type.DeclaredType):boolean" ) - ; ( o1 - , "com.sun.source.util.Trees.getOriginalType(javax.lang.model.type.ErrorType):javax.lang.model.type.TypeMirror" - ) - ; ( (o, [o; o; o; o]) - , "com.sun.source.util.Trees.printMessage(javax.tools.Diagnostic.Kind, \ - java.lang.CharSequence, com.sun.source.tree.Tree, \ - com.sun.source.tree.CompilationUnitTree):void" ) - ; ( o1 - , "com.sun.source.util.Trees.getLub(com.sun.source.tree.CatchTree):javax.lang.model.type.TypeMirror" - ) - ; ( (n, [o; n; n]) - , "org.w3c.dom.Document.setUserData(java.lang.String,java.lang.Object,org.w3c.dom.UserDataHandler):java.lang.Object" - ) - ; ( (n, [o; n; n]) - , "org.w3c.dom.Node.setUserData(java.lang.String,java.lang.Object,org.w3c.dom.UserDataHandler):java.lang.Object" - ) - ; (* References *) - (ng, "java.lang.ref.Reference.get():java.lang.Object") - ; (ng, "java.lang.ref.PhantomReference.get():java.lang.Object") - ; (ng, "java.lang.ref.SoftReference.get():java.lang.Object") - ; (ng, "java.lang.ref.WeakReference.get():java.lang.Object") - ; (ng, "java.util.concurrent.atomic.AtomicReference.get():java.lang.Object") ] + let result = + check_not_null_list @ check_state_list @ check_argument_list @ true_on_null_list + @ nullable_method_with_nonnull_alternatives_nullability_list @ annotated_list_nullability_other + in + List.find_a_dup result ~compare:(fun (_, descr1) (_, descr2) -> String.compare descr1 descr2) + |> Option.iter ~f:(fun (_, duplicate_method_descr) -> + Logging.die Logging.InternalError "Nullability table contains a duplicate %s" + duplicate_method_descr ) ; + result (** Models for methods that do not return *) @@ -621,3 +653,10 @@ let mapPut_table = mk_table mapPut_list let noreturn_table = mk_table noreturn_list let true_on_null_table = mk_table true_on_null_list + +let nonnull_alternatives_table = + let method_descr_to_alternative = + List.map nullable_methods_with_nonnull_alternatives_list + ~f:(fun (_, method_descr, alternative) -> (alternative, method_descr)) + in + mk_table method_descr_to_alternative diff --git a/infer/src/nullsafe/modelTables.mli b/infer/src/nullsafe/modelTables.mli index baf4688d9..c7c187c98 100644 --- a/infer/src/nullsafe/modelTables.mli +++ b/infer/src/nullsafe/modelTables.mli @@ -34,3 +34,12 @@ val mapPut_table : model_table_t val noreturn_table : model_table_t val true_on_null_table : model_table_t + +(** Used to describe a method complementary to a given one. Contains information needed for + reporting (hence does not describe the whole signature). *) +type nonnull_alternative_method = {package_name: string; class_name: string; method_name: string} + +val nonnull_alternatives_table : (string, nonnull_alternative_method) Caml.Hashtbl.t +(** The key is a string representation of a [@Nullable] method. The value is the description of + non-nullable alternative: a method does the same, but never returns null (does a null check + inside). *) diff --git a/infer/src/nullsafe/models.ml b/infer/src/nullsafe/models.ml index c7c649588..45d3479eb 100644 --- a/infer/src/nullsafe/models.ml +++ b/infer/src/nullsafe/models.ml @@ -144,3 +144,11 @@ let is_containsKey proc_name = table_has_procedure containsKey_table proc_name (** Check if the procedure is Map.put(). *) let is_mapPut proc_name = table_has_procedure mapPut_table proc_name + +(** Check if a (nullable) method has a non-nullable alternative: A method that does the same as + [proc_name] but asserts the result is not null before returning to the caller. *) +let find_nonnullable_alternative proc_name = + (* NOTE: For now we fetch this info from internal models. + It is a good idea to support this feature in a user-facing third party repository. *) + let proc_id = Procname.to_unique_id proc_name in + Hashtbl.find_opt nonnull_alternatives_table proc_id diff --git a/infer/tests/codetoanalyze/java/nullsafe-default/AlternativeRecommendations.java b/infer/tests/codetoanalyze/java/nullsafe-default/AlternativeRecommendations.java new file mode 100644 index 000000000..5ebd767d0 --- /dev/null +++ b/infer/tests/codetoanalyze/java/nullsafe-default/AlternativeRecommendations.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package codetoanalyze.java.nullsafe_default; + +import android.annotation.SuppressLint; +import android.view.View; + +/** + * Test to ensure we have special messaging when misusing known nullable methods that have + * non-nullable alternatives. + */ +public class AlternativeRecommendations { + @SuppressLint("eradicate-field-not-initialized") + View field; + + static void dereference_ShouldSuggestAlternative(View view) { + view.findViewById(2).setId(3); + } + + static void passingParam_ShouldSuggestAlternative(View view) { + acceptsNonnullView(view.findViewById(2)); + } + + static View returnValue_ShouldSuggestAlternative(View view) { + return view.findViewById(2); + } + + void assigningField_ShouldSuggestAlternative(View view) { + field = view.findViewById(2); + } + + static void acceptsNonnullView(View view) {} +} diff --git a/infer/tests/codetoanalyze/java/nullsafe-default/issues.exp b/infer/tests/codetoanalyze/java/nullsafe-default/issues.exp index 73f1b3d43..5e1bef934 100644 --- a/infer/tests/codetoanalyze/java/nullsafe-default/issues.exp +++ b/infer/tests/codetoanalyze/java/nullsafe-default/issues.exp @@ -1,3 +1,7 @@ +codetoanalyze/java/nullsafe-default/AlternativeRecommendations.java, codetoanalyze.java.nullsafe_default.AlternativeRecommendations.assigningField_ShouldSuggestAlternative(android.view.View):void, 0, ERADICATE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [`field` is declared non-nullable but is assigned a nullable: call to View.findViewById(...) at line 34 (nullable according to nullsafe internal models). If you don't expect null, use `View.requireViewById()` instead.] +codetoanalyze/java/nullsafe-default/AlternativeRecommendations.java, codetoanalyze.java.nullsafe_default.AlternativeRecommendations.dereference_ShouldSuggestAlternative(android.view.View):void, 0, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`view.findViewById(...)` is nullable and is not locally checked for null when calling `setId(...)`: call to View.findViewById(...) at line 22 (nullable according to nullsafe internal models). If this is intentional, use `View.requireViewById()` instead.] +codetoanalyze/java/nullsafe-default/AlternativeRecommendations.java, codetoanalyze.java.nullsafe_default.AlternativeRecommendations.passingParam_ShouldSuggestAlternative(android.view.View):void, 0, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`AlternativeRecommendations.acceptsNonnullView(...)`: parameter #1(`view`) is declared non-nullable but the argument `view.findViewById(...)` is nullable: call to View.findViewById(...) at line 26 (nullable according to nullsafe internal models). If you don't expect null, use `View.requireViewById()` instead.] +codetoanalyze/java/nullsafe-default/AlternativeRecommendations.java, codetoanalyze.java.nullsafe_default.AlternativeRecommendations.returnValue_ShouldSuggestAlternative(android.view.View):android.view.View, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [`returnValue_ShouldSuggestAlternative(...)`: return type is declared non-nullable but the method returns a nullable value: call to View.findViewById(...) at line 30 (nullable according to nullsafe internal models). If you don't expect null, use `View.requireViewById()` instead.] codetoanalyze/java/nullsafe-default/ButterKnife.java, codetoanalyze.java.nullsafe_default.ButterKnife$TestNotInitialized.(codetoanalyze.java.nullsafe_default.ButterKnife), 0, ERADICATE_FIELD_NOT_INITIALIZED, no_bucket, WARNING, [Field `notInitializedNormalIsBAD` is declared non-nullable, so it should be initialized in the constructor or in an `@Initializer` method] codetoanalyze/java/nullsafe-default/ButterKnife.java, codetoanalyze.java.nullsafe_default.ButterKnife.(), 0, ERADICATE_FIELD_OVER_ANNOTATED, no_bucket, WARNING, [Field `ButterKnife.nullable` is always initialized in the constructor but is declared `@Nullable`] codetoanalyze/java/nullsafe-default/ButterKnife.java, codetoanalyze.java.nullsafe_default.ButterKnife.assignNullToNormalIsBAD():void, 0, ERADICATE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [`normal` is declared non-nullable but is assigned `null`: null constant at line 76.]