• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 Uber Technologies, Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20  * THE SOFTWARE.
21  */
22 
23 package com.uber.nullaway.fixserialization;
24 
25 import com.google.common.base.Preconditions;
26 import com.google.common.collect.ImmutableMap;
27 import com.google.errorprone.VisitorState;
28 import com.sun.source.tree.Tree;
29 import com.sun.source.util.TreePath;
30 import com.sun.source.util.Trees;
31 import com.sun.tools.javac.code.Symbol;
32 import com.sun.tools.javac.processing.JavacProcessingEnvironment;
33 import com.uber.nullaway.Config;
34 import com.uber.nullaway.ErrorMessage;
35 import com.uber.nullaway.Nullness;
36 import com.uber.nullaway.fixserialization.location.SymbolLocation;
37 import com.uber.nullaway.fixserialization.out.ErrorInfo;
38 import com.uber.nullaway.fixserialization.out.SuggestedNullableFixInfo;
39 import java.util.Map;
40 import java.util.regex.Matcher;
41 import java.util.regex.Pattern;
42 import javax.annotation.Nullable;
43 
44 /** A facade class to interact with fix serialization package. */
45 public class SerializationService {
46 
47   /** Special characters that need to be escaped in TSV files. */
48   private static final ImmutableMap<Character, Character> escapes =
49       ImmutableMap.of(
50           '\n', 'n',
51           '\t', 't',
52           '\f', 'f',
53           '\b', 'b',
54           '\r', 'r');
55 
56   /**
57    * Escapes special characters in string to conform with TSV file formats. The most common
58    * convention for lossless conversion is to escape special characters with a backslash according
59    * to <a
60    * href="https://en.wikipedia.org/wiki/Tab-separated_values#Conventions_for_lossless_conversion_to_TSV">
61    * Conventions for lossless conversion to TSV</a>
62    *
63    * @param str String to process.
64    * @return returns modified str which its special characters are escaped.
65    */
escapeSpecialCharacters(String str)66   public static String escapeSpecialCharacters(String str) {
67     // regex needs "\\" to match character '\', each must also be escaped in string to create "\\",
68     // therefore we need four "\".
69     // escape existing backslashes
70     str = str.replaceAll(Pattern.quote("\\"), Matcher.quoteReplacement("\\\\"));
71     // escape special characters
72     for (Map.Entry<Character, Character> entry : escapes.entrySet()) {
73       str =
74           str.replaceAll(
75               String.valueOf(entry.getKey()), Matcher.quoteReplacement("\\" + entry.getValue()));
76     }
77     return str;
78   }
79 
80   /**
81    * Serializes the suggested type change of an element in the source code that can resolve the
82    * error. We do not want suggested fix changes to override explicit annotations in the code,
83    * therefore, if the target element has an explicit {@code @Nonnull} annotation, no type change is
84    * suggested.
85    *
86    * @param config NullAway config.
87    * @param state Visitor state.
88    * @param target Target element to alternate it's type.
89    * @param errorMessage Error caused by the target.
90    */
serializeFixSuggestion( Config config, VisitorState state, Symbol target, ErrorMessage errorMessage)91   public static void serializeFixSuggestion(
92       Config config, VisitorState state, Symbol target, ErrorMessage errorMessage) {
93     FixSerializationConfig serializationConfig = config.getSerializationConfig();
94     if (!serializationConfig.suggestEnabled) {
95       return;
96     }
97     // Skip if the element has an explicit @Nonnull annotation.
98     if (Nullness.hasNonNullAnnotation(target, config)) {
99       return;
100     }
101     Trees trees = Trees.instance(JavacProcessingEnvironment.instance(state.context));
102     // Skip if the element is received as bytecode.
103     if (trees.getPath(target) == null) {
104       return;
105     }
106     SymbolLocation location = SymbolLocation.createLocationFromSymbol(target);
107     SuggestedNullableFixInfo suggestedNullableFixInfo =
108         buildFixMetadata(state.getPath(), errorMessage, location);
109     Serializer serializer = serializationConfig.getSerializer();
110     Preconditions.checkNotNull(
111         serializer, "Serializer shouldn't be null at this point, error in configuration setting!");
112     serializer.serializeSuggestedFixInfo(
113         suggestedNullableFixInfo, serializationConfig.suggestEnclosing);
114   }
115 
116   /**
117    * Serializes the reporting error.
118    *
119    * @param config NullAway config.
120    * @param state Visitor state.
121    * @param errorTree Tree of the element involved in the reporting error.
122    * @param errorMessage Error caused by the target.
123    */
serializeReportingError( Config config, VisitorState state, Tree errorTree, @Nullable Symbol target, ErrorMessage errorMessage)124   public static void serializeReportingError(
125       Config config,
126       VisitorState state,
127       Tree errorTree,
128       @Nullable Symbol target,
129       ErrorMessage errorMessage) {
130     Serializer serializer = config.getSerializationConfig().getSerializer();
131     Preconditions.checkNotNull(
132         serializer, "Serializer shouldn't be null at this point, error in configuration setting!");
133     serializer.serializeErrorInfo(new ErrorInfo(state.getPath(), errorTree, errorMessage, target));
134   }
135 
136   /**
137    * Builds the {@link SuggestedNullableFixInfo} instance based on the {@link ErrorMessage} type.
138    */
buildFixMetadata( TreePath path, ErrorMessage errorMessage, SymbolLocation location)139   private static SuggestedNullableFixInfo buildFixMetadata(
140       TreePath path, ErrorMessage errorMessage, SymbolLocation location) {
141     SuggestedNullableFixInfo suggestedNullableFixInfo;
142     switch (errorMessage.getMessageType()) {
143       case RETURN_NULLABLE:
144       case WRONG_OVERRIDE_RETURN:
145       case WRONG_OVERRIDE_PARAM:
146       case PASS_NULLABLE:
147       case FIELD_NO_INIT:
148       case ASSIGN_FIELD_NULLABLE:
149       case METHOD_NO_INIT:
150         suggestedNullableFixInfo = new SuggestedNullableFixInfo(path, location, errorMessage);
151         break;
152       default:
153         throw new IllegalStateException(
154             "Cannot suggest a type to resolve error of type: " + errorMessage.getMessageType());
155     }
156     return suggestedNullableFixInfo;
157   }
158 }
159