xref: /aosp_15_r20/external/doclava/src/com/google/doclava/Errors.java (revision feeed43c7c55e85932c547a3cefc559df175227c)
1 /*
2  * Copyright (C) 2010 Google Inc.
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.doclava;
18 
19 import java.util.ArrayList;
20 import java.util.List;
21 import java.util.Set;
22 import java.util.TreeSet;
23 
24 public class Errors {
25   public static boolean hadError = false;
26   private static boolean lintsAreErrors = false;
27   private static boolean warningsAreErrors = false;
28   private static List<LintBaselineEntry> baseline;
29   private static TreeSet<ErrorMessage> allErrors = new TreeSet<ErrorMessage>();
30 
31   public static class LintBaselineEntry {
32       final String file;
33       final String message;
34       final int code;
35 
LintBaselineEntry(String file, String message, int code)36       LintBaselineEntry(String file, String message, int code) {
37           this.file = file;
38           this.message = message;
39           this.code = code;
40       }
41   }
42 
43   public static class ErrorMessage implements Comparable<ErrorMessage> {
44     final int resolvedLevel;
45     final Error error;
46     final SourcePositionInfo pos;
47     final String msg;
48 
ErrorMessage(int r, Error e, SourcePositionInfo p, String m)49     ErrorMessage(int r, Error e, SourcePositionInfo p, String m) {
50       resolvedLevel = r;
51       error = e;
52       pos = p;
53       msg = m;
54     }
55 
56     @Override
compareTo(ErrorMessage other)57     public int compareTo(ErrorMessage other) {
58       int r = pos.compareTo(other.pos);
59       if (r != 0) return r;
60       return msg.compareTo(other.msg);
61     }
62 
parse(String line)63     public static LintBaselineEntry parse(String line) {
64       if (line.trim().length() == 0) {
65         return null;
66       }
67       // Format, per toString() below, is:
68       // file:row: lint: multi-word-message [code]
69       // We ignore the row number, so that edits don't invalidate baselines entries.
70       String[] words = line.split(" ");
71 
72       String[] fileAndRow = words[0].split(":");
73       if (fileAndRow.length != 2) {
74         System.err.println("ignored baseline entry (no file & row): " + line);
75         return null;
76       }
77       String file = fileAndRow[0];
78 
79       String lastWord = words[words.length - 1];
80       if (lastWord.length() <= 3 || lastWord.charAt(0) != '['
81           || lastWord.charAt(lastWord.length() - 1) != ']') {
82         System.err.println("ignored baseline entry (no error code): " + line);
83         return null;
84       }
85       int code = Integer.parseInt(lastWord.substring(1, lastWord.length() - 1));
86 
87       int messageStart = (words[0] + " lint: ").length();
88       int messageEnd = line.length() - lastWord.length() - 1;
89       String message = line.substring(messageStart, messageEnd);
90       return new LintBaselineEntry(file, message, code);
91     }
92 
93 
94     @Override
toString()95     public String toString() {
96       final String DEFAULT = "\033[0m";
97       final String BOLD = "\033[1m";
98       final String RED = "\033[31m";
99       final String YELLOW = "\033[33m";
100       final String CYAN = "\033[36m";
101 
102       StringBuilder res = new StringBuilder();
103       if (Doclava.android) {
104         res.append(BOLD).append(pos.toString()).append(": ");
105         switch (resolvedLevel) {
106           case LINT: res.append(CYAN); break;
107           case WARNING: res.append(YELLOW); break;
108           case ERROR: res.append(RED); break;
109         }
110         switch (error.getLevel()) {
111           case LINT: res.append("lint: "); break;
112           case WARNING: res.append("warning: "); break;
113           case ERROR: res.append("error: "); break;
114         }
115         res.append(DEFAULT);
116         res.append(msg);
117         res.append(" [").append(error.code).append("]");
118       } else {
119         // Sigh, some people are parsing the old format.
120         res.append(pos.toString()).append(": ");
121         switch (error.getLevel()) {
122           case LINT: res.append("lint "); break;
123           case WARNING: res.append("warning "); break;
124           case ERROR: res.append("error "); break;
125           default: break;
126         }
127         res.append(error.code).append(": ");
128         res.append(msg);
129       }
130       return res.toString();
131     }
132 
error()133     public Error error() {
134       return error;
135     }
136   }
137 
error(Error error, MemberInfo mi, String text)138   public static void error(Error error, MemberInfo mi, String text) {
139     if (error.getLevel() == Errors.LINT) {
140       final String ident = "Doclava" + error.code;
141       for (AnnotationInstanceInfo a : mi.annotations()) {
142         if (a.type().qualifiedNameMatches("android", "annotation.SuppressLint")) {
143           for (AnnotationValueInfo val : a.elementValues()) {
144             if ("value".equals(val.element().name())) {
145               for (AnnotationValueInfo inner : (ArrayList<AnnotationValueInfo>) val.value()) {
146                 if (ident.equals(String.valueOf(inner.value()))) {
147                   return;
148                 }
149               }
150             }
151           }
152         }
153       }
154     }
155     error(error, mi.position(), text);
156   }
157 
error(Error error, SourcePositionInfo where, String text)158   public static void error(Error error, SourcePositionInfo where, String text) {
159     if (error.getLevel() == HIDDEN) {
160       return;
161     }
162 
163     int resolvedLevel = error.getLevel();
164     if (resolvedLevel == LINT && lintsAreErrors) {
165       resolvedLevel = isBaselined(error, where, text) ? LINT : ERROR;
166     }
167     if (resolvedLevel == WARNING && warningsAreErrors) {
168       resolvedLevel = ERROR;
169     }
170 
171     if (where == null) {
172       where = new SourcePositionInfo("unknown", 0, 0);
173     }
174 
175     allErrors.add(new ErrorMessage(resolvedLevel, error, where, text));
176 
177     if (resolvedLevel == ERROR) {
178       hadError = true;
179     }
180   }
181 
clearErrors()182   public static void clearErrors() {
183     hadError = false;
184     allErrors.clear();
185   }
186 
printErrors()187   public static void printErrors() {
188     printErrors(allErrors);
189   }
190 
printErrors(Set<ErrorMessage> errors)191   public static void printErrors(Set<ErrorMessage> errors) {
192     for (ErrorMessage m : errors) {
193       System.err.println(m.toString());
194     }
195     System.err.flush();
196   }
197 
getErrors()198   public static Set<ErrorMessage> getErrors() {
199     return allErrors;
200   }
201 
202   public static final int INHERIT = -1;
203   public static final int HIDDEN = 0;
204 
205   /**
206    * Lint level means that we encountered inconsistent or broken documentation.
207    * These should be resolved, but don't impact API compatibility.
208    */
209   public static final int LINT = 1;
210 
211   /**
212    * Warning level means that we encountered some incompatible or inconsistent
213    * API change. These must be resolved to preserve API compatibility.
214    */
215   public static final int WARNING = 2;
216 
217   /**
218    * Error level means that we encountered severe trouble and were unable to
219    * output the requested documentation.
220    */
221   public static final int ERROR = 3;
222 
setLintsAreErrors(boolean val)223   public static void setLintsAreErrors(boolean val) {
224     lintsAreErrors = val;
225   }
226 
setWarningsAreErrors(boolean val)227   public static void setWarningsAreErrors(boolean val) {
228     warningsAreErrors = val;
229   }
230 
setLintBaseline(List<LintBaselineEntry> val)231   public static void setLintBaseline(List<LintBaselineEntry> val) {
232     baseline = val;
233   }
234 
isBaselined(Error error, SourcePositionInfo where, String text)235   private static boolean isBaselined(Error error, SourcePositionInfo where, String text) {
236     if (baseline == null) {
237       return false;
238     }
239     // Baselines entries have had newlines removed, so remove newlines in
240     // what we're trying to match with the baselines as well.
241     text = text.replace("\n", "");
242     for (LintBaselineEntry entry : baseline) {
243       if (where.file.endsWith(entry.file) && error.code == entry.code) {
244         if (text.equals(entry.message)) {
245           return true;
246         }
247         if (entry.message.startsWith(text.replaceAll(" in android$", ""))) {
248           // This is a hack: when we generate a compatchanges.html, we generate
249           // a bunch of comments without context, placed naively the "android" package.
250           // TODO: Remove this once all the lint violations in ChangeId constant javadoc
251           // have been cleaned up.
252           return true;
253         }
254       }
255     }
256     return false;
257   }
258 
259   public static class Error {
260     public int code;
261 
262     /**
263      * @deprecated This field should not be access directly. Instead, use
264      *             {@link #getLevel()}.
265      */
266     @Deprecated
267     public int level;
268 
269     /**
270      * When {@code level} is set to {@link #INHERIT}, this is the parent from
271      * which the error will inherit its level.
272      */
273     private final Error parent;
274 
Error(int code, int level)275     public Error(int code, int level) {
276       this.code = code;
277       this.level = level;
278       this.parent = null;
279       sErrors.add(this);
280     }
281 
Error(int code, Error parent)282     public Error(int code, Error parent) {
283       this.code = code;
284       this.level = -1;
285       this.parent = parent;
286       sErrors.add(this);
287     }
288 
289     /**
290      * Returns the implied level for this error.
291      * <p>
292      * If the level is {@link #INHERIT}, the level will be returned for the
293      * parent.
294      *
295      * @return
296      * @throws IllegalStateException if the level is {@link #INHERIT} and the
297      *         parent is {@code null}
298      */
getLevel()299     public int getLevel() {
300       if (level == INHERIT) {
301         if (parent == null) {
302           throw new IllegalStateException("Error with level INHERIT must have non-null parent");
303         }
304         return parent.getLevel();
305       }
306       return level;
307     }
308 
309     /**
310      * Sets the level.
311      * <p>
312      * Valid arguments are:
313      * <ul>
314      *     <li>{@link #HIDDEN}
315      *     <li>{@link #WARNING}
316      *     <li>{@link #ERROR}
317      * </ul>
318      *
319      * @param level the level to set
320      */
setLevel(int level)321     public void setLevel(int level) {
322       if (level == INHERIT) {
323         throw new IllegalArgumentException("Error level may not be set to INHERIT");
324       }
325       this.level = level;
326     }
327 
toString()328     public String toString() {
329       return "Error #" + this.code;
330     }
331   }
332 
333   public static final List<Error> sErrors = new ArrayList<>();
334 
335   // Errors for API verification
336   public static final Error PARSE_ERROR = new Error(1, ERROR);
337   public static final Error ADDED_PACKAGE = new Error(2, WARNING);
338   public static final Error ADDED_CLASS = new Error(3, WARNING);
339   public static final Error ADDED_METHOD = new Error(4, WARNING);
340   public static final Error ADDED_FIELD = new Error(5, WARNING);
341   public static final Error ADDED_INTERFACE = new Error(6, WARNING);
342   public static final Error REMOVED_PACKAGE = new Error(7, WARNING);
343   public static final Error REMOVED_CLASS = new Error(8, WARNING);
344   public static final Error REMOVED_METHOD = new Error(9, WARNING);
345   public static final Error REMOVED_FIELD = new Error(10, WARNING);
346   public static final Error REMOVED_INTERFACE = new Error(11, WARNING);
347   public static final Error CHANGED_STATIC = new Error(12, WARNING);
348   public static final Error ADDED_FINAL = new Error(13, WARNING);
349   public static final Error CHANGED_TRANSIENT = new Error(14, WARNING);
350   public static final Error CHANGED_VOLATILE = new Error(15, WARNING);
351   public static final Error CHANGED_TYPE = new Error(16, WARNING);
352   public static final Error CHANGED_VALUE = new Error(17, WARNING);
353   public static final Error CHANGED_SUPERCLASS = new Error(18, WARNING);
354   public static final Error CHANGED_SCOPE = new Error(19, WARNING);
355   public static final Error CHANGED_ABSTRACT = new Error(20, WARNING);
356   public static final Error CHANGED_THROWS = new Error(21, WARNING);
357   public static final Error CHANGED_NATIVE = new Error(22, HIDDEN);
358   public static final Error CHANGED_CLASS = new Error(23, WARNING);
359   public static final Error CHANGED_DEPRECATED = new Error(24, WARNING);
360   public static final Error CHANGED_SYNCHRONIZED = new Error(25, WARNING);
361   public static final Error ADDED_FINAL_UNINSTANTIABLE = new Error(26, WARNING);
362   public static final Error REMOVED_FINAL = new Error(27, WARNING);
363   public static final Error REMOVED_DEPRECATED_CLASS = new Error(28, REMOVED_CLASS);
364   public static final Error REMOVED_DEPRECATED_METHOD = new Error(29, REMOVED_METHOD);
365   public static final Error REMOVED_DEPRECATED_FIELD = new Error(30, REMOVED_FIELD);
366   public static final Error ADDED_ABSTRACT_METHOD = new Error(31, ADDED_METHOD);
367 
368   // Errors in javadoc generation
369   public static final Error UNRESOLVED_LINK = new Error(101, LINT);
370   public static final Error BAD_INCLUDE_TAG = new Error(102, LINT);
371   public static final Error UNKNOWN_TAG = new Error(103, LINT);
372   public static final Error UNKNOWN_PARAM_TAG_NAME = new Error(104, LINT);
373   public static final Error UNDOCUMENTED_PARAMETER = new Error(105, HIDDEN); // LINT
374   public static final Error BAD_ATTR_TAG = new Error(106, LINT);
375   public static final Error BAD_INHERITDOC = new Error(107, HIDDEN); // LINT
376   public static final Error HIDDEN_LINK = new Error(108, LINT);
377   public static final Error HIDDEN_CONSTRUCTOR = new Error(109, WARNING);
378   public static final Error UNAVAILABLE_SYMBOL = new Error(110, WARNING);
379   public static final Error HIDDEN_SUPERCLASS = new Error(111, WARNING);
380   public static final Error DEPRECATED = new Error(112, HIDDEN);
381   public static final Error DEPRECATION_MISMATCH = new Error(113, WARNING);
382   public static final Error MISSING_COMMENT = new Error(114, LINT);
383   public static final Error IO_ERROR = new Error(115, ERROR);
384   public static final Error NO_SINCE_DATA = new Error(116, HIDDEN);
385   public static final Error NO_FEDERATION_DATA = new Error(117, WARNING);
386   public static final Error BROKEN_SINCE_FILE = new Error(118, ERROR);
387   public static final Error INVALID_CONTENT_TYPE = new Error(119, ERROR);
388   public static final Error INVALID_SAMPLE_INDEX = new Error(120, ERROR);
389   public static final Error HIDDEN_TYPE_PARAMETER = new Error(121, WARNING);
390   public static final Error PRIVATE_SUPERCLASS = new Error(122, WARNING);
391   public static final Error NULLABLE = new Error(123, HIDDEN); // LINT
392   public static final Error INT_DEF = new Error(124, HIDDEN); // LINT
393   public static final Error REQUIRES_PERMISSION = new Error(125, LINT);
394   public static final Error BROADCAST_BEHAVIOR = new Error(126, LINT);
395   public static final Error SDK_CONSTANT = new Error(127, LINT);
396   public static final Error TODO = new Error(128, LINT);
397   public static final Error NO_ARTIFACT_DATA = new Error(129, HIDDEN);
398   public static final Error BROKEN_ARTIFACT_FILE = new Error(130, ERROR);
399   public static final Error JAVA_TAG_IN_COMMENT = new Error(131, LINT);
400   public static final Error MULTIPLE_SDK_EXT_INFO = new Error(132, ERROR);
401 
setErrorLevel(int code, int level)402   public static boolean setErrorLevel(int code, int level) {
403     for (Error e : sErrors) {
404       if (e.code == code) {
405         e.setLevel(level);
406         return true;
407       }
408     }
409     return false;
410   }
411 }
412