test/tools/javac/warnings/suppress/VerifySuppressWarnings.java

Mon, 16 Sep 2013 14:13:44 +0200

author
jlahoda
date
Mon, 16 Sep 2013 14:13:44 +0200
changeset 2028
4ce8148ffc4f
parent 0
959103a6100f
permissions
-rw-r--r--

8021112: Spurious unchecked warning reported by javac
6480588: No way to suppress deprecation warnings when implementing deprecated interface
Summary: Fixing DeferredLintHandler configuration, so lint warnings are reported with correct @SuppressWarnings settings
Reviewed-by: jjg, vromero

     1 /*
     2  * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.
     8  *
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    12  * version 2 for more details (a copy is included in the LICENSE file that
    13  * accompanied this code).
    14  *
    15  * You should have received a copy of the GNU General Public License version
    16  * 2 along with this work; if not, write to the Free Software Foundation,
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    18  *
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    20  * or visit www.oracle.com if you need additional information or have any
    21  * questions.
    22  */
    24 import com.sun.source.tree.ClassTree;
    25 import com.sun.source.tree.CompilationUnitTree;
    26 import com.sun.source.tree.MethodTree;
    27 import com.sun.source.tree.NewClassTree;
    28 import com.sun.source.tree.Tree;
    29 import com.sun.source.tree.VariableTree;
    30 import com.sun.source.util.JavacTask;
    31 import com.sun.source.util.TreeScanner;
    32 import com.sun.source.util.Trees;
    33 import com.sun.tools.javac.api.JavacTool;
    34 import com.sun.tools.javac.code.Flags;
    35 import com.sun.tools.javac.file.JavacFileManager;
    36 import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
    37 import java.io.File;
    38 import java.io.IOException;
    39 import java.net.URI;
    40 import java.net.URISyntaxException;
    41 import java.util.ArrayList;
    42 import java.util.Arrays;
    43 import java.util.Iterator;
    44 import java.util.List;
    45 import javax.tools.Diagnostic;
    46 import javax.tools.DiagnosticListener;
    47 import javax.tools.FileObject;
    48 import javax.tools.ForwardingJavaFileManager;
    49 import javax.tools.JavaFileManager;
    50 import javax.tools.JavaFileObject;
    51 import javax.tools.SimpleJavaFileObject;
    53 /**Takes a source file, parses it once to get the warnings inside the file and
    54  * then for each and every declaration in the file, it tries to place
    55  * the @SuppressWarnings annotation on the declaration and verifies than no
    56  * warnings are produced inside the declaration, but all are produced outside it.
    57  *
    58  * Currently only works with <code>unchecked,deprecation,cast</code> warnings.
    59  */
    60 public class VerifySuppressWarnings {
    62     private static final List<String> STANDARD_PARAMS = Arrays.asList("-Xlint:unchecked,deprecation,cast", "-Xjcov");
    64     public static void main(String... args) throws IOException, URISyntaxException {
    65         if (args.length != 1) throw new IllegalStateException("Must provide class name!");
    66         String testContent = null;
    67         List<File> sourcePath = new ArrayList<>();
    68         for (String sourcePaths : System.getProperty("test.src.path").split(":")) {
    69             sourcePath.add(new File(sourcePaths));
    70         }
    71         JavacFileManager fm = JavacTool.create().getStandardFileManager(null, null, null);
    72         for (File sp : sourcePath) {
    73             File inp = new File(sp, args[0]);
    75             if (inp.canRead()) {
    76                 testContent = fm.getRegularFile(inp).getCharContent(true).toString();
    77             }
    78         }
    79         if (testContent == null) throw new IllegalStateException();
    80         final List<Diagnostic<?>> diagnostics = new ArrayList<>();
    81         DiagnosticListener<JavaFileObject> collectDiagnostics = new DiagnosticListener<JavaFileObject>() {
    82             @Override public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
    83                 diagnostics.add(diagnostic);
    84             }
    85         };
    86         JavaFileObject testFile = new TestFO(new URI("mem://" + args[0]), testContent);
    87         JavacTask task = JavacTool.create().getTask(null,
    88                                                     new TestFM(fm),
    89                                                     collectDiagnostics,
    90                                                     STANDARD_PARAMS,
    91                                                     null,
    92                                                     Arrays.asList(testFile));
    93         final Trees trees = Trees.instance(task);
    94         final CompilationUnitTree cut = task.parse().iterator().next();
    95         task.analyze();
    97         final List<int[]> declarationSpans = new ArrayList<>();
    99         new TreeScanner<Void, Void>() {
   100             @Override public Void visitClass(ClassTree node, Void p) {
   101                 handleDeclaration(node);
   102                 return super.visitClass(node, p);
   103             }
   104             @Override public Void visitMethod(MethodTree node, Void p) {
   105                 handleDeclaration(node);
   106                 return super.visitMethod(node, p);
   107             }
   108             @Override public Void visitVariable(VariableTree node, Void p) {
   109                 handleDeclaration(node);
   110                 return super.visitVariable(node, p);
   111             }
   113             @Override
   114             public Void visitNewClass(NewClassTree node, Void p) {
   115                 if (node.getClassBody() != null) {
   116                     scan(node.getClassBody().getMembers(), null);
   117                 }
   118                 return null;
   119             }
   121             private void handleDeclaration(Tree node) {
   122                 int endPos = (int) trees.getSourcePositions().getEndPosition(cut, node);
   124                 if (endPos == (-1)) {
   125                     if (node.getKind() == Tree.Kind.METHOD && (((JCMethodDecl) node).getModifiers().flags & Flags.GENERATEDCONSTR) != 0) {
   126                         return ;
   127                     }
   128                     throw new IllegalStateException();
   129                 }
   131                 declarationSpans.add(new int[] {(int) trees.getSourcePositions().getStartPosition(cut, node), endPos});
   132             }
   133         }.scan(cut, null);
   135         for (final int[] declarationSpan : declarationSpans) {
   136             final String suppressWarnings = "@SuppressWarnings({\"deprecation\", \"unchecked\", \"serial\"})";
   137             final String updatedContent = testContent.substring(0, declarationSpan[0]) + suppressWarnings + testContent.substring(declarationSpan[0]);
   138             final List<Diagnostic<?>> foundErrors = new ArrayList<>(diagnostics);
   139             DiagnosticListener<JavaFileObject> verifyDiagnostics = new DiagnosticListener<JavaFileObject>() {
   140                 @Override public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
   141                     long adjustedPos = diagnostic.getPosition();
   143                     if (adjustedPos >= declarationSpan[0]) adjustedPos -= suppressWarnings.length();
   145                     if (declarationSpan[0] <= adjustedPos && adjustedPos <= declarationSpan[1]) {
   146                         throw new IllegalStateException("unsuppressed: " + diagnostic.getMessage(null));
   147                     }
   149                     boolean found = false;
   151                     for (Iterator<Diagnostic<?>> it = foundErrors.iterator(); it.hasNext();) {
   152                         Diagnostic<?> d = it.next();
   153                         if (d.getPosition() == adjustedPos && d.getCode().equals(diagnostic.getCode())) {
   154                             it.remove();
   155                             found = true;
   156                             break;
   157                         }
   158                     }
   160                     if (!found) {
   161                         throw new IllegalStateException("diagnostic not originally reported: " + diagnostic.getMessage(null));
   162                     }
   163                 }
   164             };
   166             JavaFileObject updatedFile = new TestFO(new URI("mem://" + args[0]), updatedContent);
   167             JavacTask testTask = JavacTool.create().getTask(null,
   168                                                             new TestFM(fm),
   169                                                             verifyDiagnostics,
   170                                                             STANDARD_PARAMS,
   171                                                             null,
   172                                                             Arrays.asList(updatedFile));
   174             testTask.analyze();
   176             for (Diagnostic<?> d : foundErrors) {
   177                 if (d.getPosition() < declarationSpan[0] || declarationSpan[1] < d.getPosition()) {
   178                     throw new IllegalStateException("missing: " + d.getMessage(null));
   179                 }
   180             }
   181         }
   182     }
   184     private static final class TestFO extends SimpleJavaFileObject {
   185         private final String content;
   186         public TestFO(URI uri, String content) {
   187             super(uri, Kind.SOURCE);
   188             this.content = content;
   189         }
   191         @Override public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
   192             return content;
   193         }
   195         @Override public boolean isNameCompatible(String simpleName, Kind kind) {
   196             return true;
   197         }
   198     }
   200     private static final class TestFM extends ForwardingJavaFileManager<JavaFileManager> {
   202         public TestFM(JavaFileManager fileManager) {
   203             super(fileManager);
   204         }
   206         @Override
   207         public boolean isSameFile(FileObject a, FileObject b) {
   208             return a.equals(b);
   209         }
   211     }
   212 }

mercurial