1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.errorprone.bugpatterns.android;
18 
19 import static com.google.errorprone.BugPattern.LinkType.NONE;
20 import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
21 import static com.google.errorprone.matchers.Matchers.allOf;
22 import static com.google.errorprone.matchers.Matchers.enclosingClass;
23 import static com.google.errorprone.matchers.Matchers.enclosingMethod;
24 import static com.google.errorprone.matchers.Matchers.instanceMethod;
25 import static com.google.errorprone.matchers.Matchers.isSubtypeOf;
26 import static com.google.errorprone.matchers.Matchers.methodInvocation;
27 import static com.google.errorprone.matchers.Matchers.methodIsNamed;
28 
29 import com.google.auto.service.AutoService;
30 import com.google.errorprone.BugPattern;
31 import com.google.errorprone.VisitorState;
32 import com.google.errorprone.bugpatterns.BugChecker;
33 import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
34 import com.google.errorprone.matchers.Description;
35 import com.google.errorprone.matchers.Matcher;
36 import com.sun.source.tree.ExpressionTree;
37 import com.sun.source.tree.MethodInvocationTree;
38 import com.sun.source.tree.Tree;
39 
40 /**
41  * Parcelable data can be transported in many ways (some of which can be very
42  * inefficient) so this checker guides developers towards using high-performance
43  * best-practices.
44  */
45 @AutoService(BugChecker.class)
46 @BugPattern(
47     name = "AndroidFrameworkEfficientParcelable",
48     summary = "Verifies Parcelable performance best-practices",
49     linkType = NONE,
50     severity = WARNING)
51 public final class EfficientParcelableChecker extends BugChecker
52         implements MethodInvocationTreeMatcher {
53     private static final Matcher<Tree> INSIDE_WRITE_TO_PARCEL = allOf(
54             enclosingClass(isSubtypeOf("android.os.Parcelable")),
55             enclosingMethod(methodIsNamed("writeToParcel")));
56 
57     private static final Matcher<ExpressionTree> WRITE_STRING = methodInvocation(
58             instanceMethod().onExactClass("android.os.Parcel").named("writeString"));
59     private static final Matcher<ExpressionTree> WRITE_STRING_ARRAY = methodInvocation(
60             instanceMethod().onExactClass("android.os.Parcel").named("writeStringArray"));
61 
62     private static final Matcher<ExpressionTree> WRITE_VALUE = methodInvocation(
63             instanceMethod().onExactClass("android.os.Parcel").named("writeValue"));
64     private static final Matcher<ExpressionTree> WRITE_PARCELABLE = methodInvocation(
65             instanceMethod().onExactClass("android.os.Parcel").named("writeParcelable"));
66 
67     private static final Matcher<ExpressionTree> WRITE_LIST = methodInvocation(
68             instanceMethod().onExactClass("android.os.Parcel").named("writeList"));
69     private static final Matcher<ExpressionTree> WRITE_PARCELABLE_LIST = methodInvocation(
70             instanceMethod().onExactClass("android.os.Parcel").named("writeParcelableList"));
71     private static final Matcher<ExpressionTree> WRITE_PARCELABLE_ARRAY = methodInvocation(
72             instanceMethod().onExactClass("android.os.Parcel").named("writeParcelableArray"));
73 
74     @Override
matchMethodInvocation(MethodInvocationTree tree, VisitorState state)75     public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
76         if (INSIDE_WRITE_TO_PARCEL.matches(tree, state)) {
77             if (WRITE_STRING.matches(tree, state)) {
78                 return buildDescription(tree)
79                         .setMessage("Recommended to use 'writeString8()' to improve "
80                                 + "efficiency; sending as UTF-8 can double throughput")
81                         .build();
82             }
83             if (WRITE_STRING_ARRAY.matches(tree, state)) {
84                 return buildDescription(tree)
85                         .setMessage("Recommended to use 'writeString8Array()' to improve "
86                                 + "efficiency; sending as UTF-8 can double throughput")
87                         .build();
88             }
89 
90             if (WRITE_VALUE.matches(tree, state)) {
91                 return buildDescription(tree)
92                         .setMessage("Recommended to use strongly-typed methods to improve "
93                                 + "efficiency; saves 4 bytes for type and overhead of "
94                                 + "Parcelable class name")
95                         .build();
96             }
97             if (WRITE_PARCELABLE.matches(tree, state)) {
98                 return buildDescription(tree)
99                         .setMessage("Recommended to use 'item.writeToParcel()' to improve "
100                                 + "efficiency; saves overhead of Parcelable class name")
101                         .build();
102             }
103 
104             if (WRITE_LIST.matches(tree, state)) {
105                 return buildDescription(tree)
106                         .setMessage("Recommended to use 'writeTypedList()' to improve "
107                                 + "efficiency; saves overhead of repeated Parcelable class name")
108                         .build();
109             }
110             if (WRITE_PARCELABLE_LIST.matches(tree, state)) {
111                 return buildDescription(tree)
112                         .setMessage("Recommended to use 'writeTypedList()' to improve "
113                                 + "efficiency; saves overhead of repeated Parcelable class name")
114                         .build();
115             }
116             if (WRITE_PARCELABLE_ARRAY.matches(tree, state)) {
117                 return buildDescription(tree)
118                         .setMessage("Recommended to use 'writeTypedArray()' to improve "
119                                 + "efficiency; saves overhead of repeated Parcelable class name")
120                         .build();
121             }
122         }
123         return Description.NO_MATCH;
124     }
125 }
126