1 package com.uber.nullaway.handlers; 2 3 /* 4 * Copyright (c) 2017 Uber Technologies, Inc. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 * THE SOFTWARE. 23 */ 24 import com.google.common.collect.ImmutableList; 25 import com.google.common.collect.ImmutableSet; 26 import com.google.errorprone.predicates.type.DescendantOf; 27 import com.google.errorprone.suppliers.Suppliers; 28 import com.uber.nullaway.handlers.stream.StreamModelBuilder; 29 import com.uber.nullaway.handlers.stream.StreamTypeRecord; 30 31 public class StreamNullabilityPropagatorFactory { 32 33 /** Returns a handler for the standard Java 8 stream APIs. */ getJavaStreamNullabilityPropagator()34 public static StreamNullabilityPropagator getJavaStreamNullabilityPropagator() { 35 ImmutableList<StreamTypeRecord> streamModels = 36 StreamModelBuilder.start() 37 .addStreamType(new DescendantOf(Suppliers.typeFromString("java.util.stream.Stream"))) 38 // Names of all the methods of java.util.stream.Stream that behave like .filter(...) 39 // (must take exactly 1 argument) 40 .withFilterMethodFromSignature("filter(java.util.function.Predicate<? super T>)") 41 // Names and relevant arguments of all the methods of java.util.stream.Stream that 42 // behave 43 // like .map(...) for the purposes of this checker (the listed arguments are those that 44 // take the potentially filtered objects from the stream) 45 .withMapMethodFromSignature( 46 "<R>map(java.util.function.Function<? super T,? extends R>)", 47 "apply", 48 ImmutableSet.of(0)) 49 .withMapMethodFromSignature( 50 "mapToInt(java.util.function.ToIntFunction<? super T>)", 51 "applyAsInt", 52 ImmutableSet.of(0)) 53 .withMapMethodFromSignature( 54 "mapToLong(java.util.function.ToLongFunction<? super T>)", 55 "applyAsLong", 56 ImmutableSet.of(0)) 57 .withMapMethodFromSignature( 58 "mapToDouble(java.util.function.ToDoubleFunction<? super T>)", 59 "applyAsDouble", 60 ImmutableSet.of(0)) 61 .withMapMethodFromSignature( 62 "forEach(java.util.function.Consumer<? super T>)", "accept", ImmutableSet.of(0)) 63 .withMapMethodFromSignature( 64 "forEachOrdered(java.util.function.Consumer<? super T>)", 65 "accept", 66 ImmutableSet.of(0)) 67 .withMapMethodAllFromName("flatMap", "apply", ImmutableSet.of(0)) 68 // List of methods of java.util.stream.Stream through which we just propagate the 69 // nullability information of the last call, e.g. m() in 70 // Observable.filter(...).m().map(...) means the 71 // nullability information from filter(...) should still be propagated to map(...), 72 // ignoring the interleaving call to m(). 73 .withPassthroughMethodFromSignature("distinct()") 74 // List of methods of java.util.stream.Stream that both use the nullability information 75 // internally (like map does), but also don't change the values flowing through the 76 // stream 77 // and thus propagate 78 // the nullability information of the last call. 79 .end(); 80 return new StreamNullabilityPropagator(streamModels); 81 } 82 83 /** Returns a handler for io.reactivex.* stream APIs */ getRxStreamNullabilityPropagator()84 public static StreamNullabilityPropagator getRxStreamNullabilityPropagator() { 85 ImmutableList<StreamTypeRecord> rxModels = 86 StreamModelBuilder.start() 87 .addStreamType(new DescendantOf(Suppliers.typeFromString("io.reactivex.Observable"))) 88 // Names of all the methods of io.reactivex.Observable that behave like .filter(...) 89 // (must take exactly 1 argument) 90 .withFilterMethodFromSignature("filter(io.reactivex.functions.Predicate<? super T>)") 91 // Names and relevant arguments of all the methods of io.reactivex.Observable that 92 // behave 93 // like .map(...) for the purposes of this checker (the listed arguments are those that 94 // take the potentially filtered objects from the stream) 95 .withMapMethodFromSignature( 96 "<R>map(io.reactivex.functions.Function<? super T,? extends R>)", 97 "apply", 98 ImmutableSet.of(0)) 99 .withMapMethodAllFromName("flatMap", "apply", ImmutableSet.of(0)) 100 .withMapMethodAllFromName("flatMapSingle", "apply", ImmutableSet.of(0)) 101 .withMapMethodFromSignature( 102 "distinctUntilChanged(io.reactivex.functions.BiPredicate<? super T,? super T>)", 103 "test", 104 ImmutableSet.of(0, 1)) 105 // List of methods of io.reactivex.Observable through which we just propagate the 106 // nullability information of the last call, e.g. m() in 107 // Observable.filter(...).m().map(...) means the 108 // nullability information from filter(...) should still be propagated to map(...), 109 // ignoring the interleaving call to m(). 110 .withPassthroughMethodFromSignature("distinct()") 111 .withPassthroughMethodFromSignature("distinctUntilChanged()") 112 .withPassthroughMethodAllFromName("observeOn") 113 // List of methods of io.reactivex.Observable that both use the nullability information 114 // internally (like map does), but also don't change the values flowing through the 115 // stream 116 // and thus propagate 117 // the nullability information of the last call. 118 .withUseAndPassthroughMethodAllFromName("doOnNext", "accept", ImmutableSet.of(0)) 119 .addStreamType(new DescendantOf(Suppliers.typeFromString("io.reactivex.Maybe"))) 120 .withFilterMethodFromSignature("filter(io.reactivex.functions.Predicate<? super T>)") 121 .withMapMethodFromSignature( 122 "<R>map(io.reactivex.functions.Function<? super T,? extends R>)", 123 "apply", 124 ImmutableSet.of(0)) 125 .withMapMethodAllFromName("flatMap", "apply", ImmutableSet.of(0)) 126 .withMapMethodAllFromName("flatMapSingle", "apply", ImmutableSet.of(0)) 127 .withPassthroughMethodAllFromName("observeOn") 128 .withUseAndPassthroughMethodAllFromName("doOnNext", "accept", ImmutableSet.of(0)) 129 .addStreamType(new DescendantOf(Suppliers.typeFromString("io.reactivex.Single"))) 130 .withFilterMethodFromSignature("filter(io.reactivex.functions.Predicate<? super T>)") 131 .withMapMethodFromSignature( 132 "<R>map(io.reactivex.functions.Function<? super T,? extends R>)", 133 "apply", 134 ImmutableSet.of(0)) 135 .withMapMethodAllFromName("flatMap", "apply", ImmutableSet.of(0)) 136 .withMapMethodAllFromName("flatMapSingle", "apply", ImmutableSet.of(0)) 137 .withPassthroughMethodAllFromName("observeOn") 138 .withUseAndPassthroughMethodAllFromName("doOnNext", "accept", ImmutableSet.of(0)) 139 .end(); 140 141 return new StreamNullabilityPropagator(rxModels); 142 } 143 144 /** 145 * Create a new StreamNullabilityPropagator from a list of StreamTypeRecord specs. 146 * 147 * <p>This is used to create a new StreamNullabilityPropagator based on stream API specs provided 148 * by library models. 149 * 150 * @param streamNullabilitySpecs the list of StreamTypeRecord objects defining one or more stream 151 * APIs (from {@link StreamModelBuilder}). 152 * @return A handler corresponding to the stream APIs defined by the given specs. 153 */ fromSpecs( ImmutableList<StreamTypeRecord> streamNullabilitySpecs)154 public static StreamNullabilityPropagator fromSpecs( 155 ImmutableList<StreamTypeRecord> streamNullabilitySpecs) { 156 return new StreamNullabilityPropagator(streamNullabilitySpecs); 157 } 158 } 159