Skip to content

Commit

Permalink
Initial support for @SuppressForbidden annotation (the default one)
Browse files Browse the repository at this point in the history
  • Loading branch information
uschindler committed Mar 28, 2015
1 parent c44be3a commit afce079
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 8 deletions.
8 changes: 3 additions & 5 deletions build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,8 @@
<compile module="main" classpathref="path.build"/>
</target>

<target name="compile-tools" depends="-init" description="Compile tools">
<compile module="tools" classpathref="path.build"/>
<target name="compile-tools" depends="compile" description="Compile tools">
<compile module="tools" classpathref="path.run"/>
</target>

<target name="clean" description="Clean">
Expand Down Expand Up @@ -411,9 +411,7 @@
<fileset dir="build/tools"/>
<bundledsignatures name="jdk-unsafe-${jdk.version}"/>
<bundledsignatures name="jdk-deprecated-${jdk.version}"/>
</forbiddenapis>
<forbiddenapis internalRuntimeForbidden="true" failOnUnsupportedJava="false" bundledSignatures="jdk-system-out" classpathref="path.run">
<fileset dir="build/main" excludes="de/thetaphi/forbiddenapis/CliMain.class"/>
<bundledsignatures name="jdk-system-out"/>
</forbiddenapis>
</target>

Expand Down
25 changes: 22 additions & 3 deletions src/main/java/de/thetaphi/forbiddenapis/ClassScanner.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ final class ClassScanner extends ClassVisitor {
final Map<String,String> forbiddenMethods;
// key is the internal name (slashed):
final Map<String,String> forbiddenClasses;
// internal names of all annotation that suppress:
// descriptors (not internal names) of all annotation that suppress:
final Set<String> suppressAnnotations;

private String source = null;
Expand All @@ -70,6 +70,7 @@ final class ClassScanner extends ClassVisitor {

// all groups that were disabled due to supressing annotation
final BitSet suppressedGroups = new BitSet();
boolean classSuppressed = false;

public ClassScanner(RelatedClassLookup lookup,
final Map<String,String> forbiddenClasses, Map<String,String> forbiddenMethods, Map<String,String> forbiddenFields,
Expand All @@ -79,7 +80,7 @@ public ClassScanner(RelatedClassLookup lookup,
this.forbiddenClasses = forbiddenClasses;
this.forbiddenMethods = forbiddenMethods;
this.forbiddenFields = forbiddenFields;
this.suppressAnnotations = Collections.emptySet();
this.suppressAnnotations = Collections.singleton(Type.getDescriptor(SuppressForbidden.class));
this.internalRuntimeForbidden = internalRuntimeForbidden;
}

Expand All @@ -90,7 +91,7 @@ private void checkDone() {

public List<ForbiddenViolation> getSortedViolations() {
checkDone();
return Collections.unmodifiableList(violations);
return classSuppressed ? Collections.<ForbiddenViolation>emptyList() : Collections.unmodifiableList(violations);
}

public String getSourceFile() {
Expand Down Expand Up @@ -222,6 +223,12 @@ String checkAnnotationDescriptor(String desc, boolean visible) {
return null;
}

void maybeSuppressCurrentGroup(String annotationDesc) {
if (suppressAnnotations.contains(annotationDesc)) {
suppressedGroups.set(currentGroupId);
}
}

private void reportClassViolation(String violation, String where) {
if (violation != null) {
violations.add(new ForbiddenViolation(currentGroupId, violation, where, -1));
Expand All @@ -234,6 +241,7 @@ public void visit(int version, int access, String name, String signature, String
this.isDeprecated = (access & Opcodes.ACC_DEPRECATED) != 0;
reportClassViolation(checkClassDefinition(superName, interfaces), "class declaration");
if (this.isDeprecated) {
classSuppressed |= suppressAnnotations.contains(DEPRECATED_DESCRIPTOR);
reportClassViolation(checkType(DEPRECATED_TYPE), "deprecation on class declaration");
}
}
Expand All @@ -249,6 +257,7 @@ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
// don't report 2 times!
return null;
}
classSuppressed |= suppressAnnotations.contains(desc);
reportClassViolation(checkAnnotationDescriptor(desc, visible), "annotation on class declaration");
return null;
}
Expand All @@ -262,6 +271,9 @@ public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, Str
@Override
public FieldVisitor visitField(final int access, final String name, final String desc, String signature, Object value) {
currentGroupId++;
if (classSuppressed) {
return null;
}
return new FieldVisitor(Opcodes.ASM5) {
final boolean isDeprecated = (access & Opcodes.ACC_DEPRECATED) != 0;
{
Expand All @@ -270,6 +282,7 @@ public FieldVisitor visitField(final int access, final String name, final String
reportFieldViolation(checkDescriptor(desc), "field declaration");
}
if (this.isDeprecated) {
maybeSuppressCurrentGroup(DEPRECATED_DESCRIPTOR);
reportFieldViolation(checkType(DEPRECATED_TYPE), "deprecation on field declaration");
}
}
Expand All @@ -280,6 +293,7 @@ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
// don't report 2 times!
return null;
}
maybeSuppressCurrentGroup(desc);
reportFieldViolation(checkAnnotationDescriptor(desc, visible), "annotation on field declaration");
return null;
}
Expand All @@ -301,6 +315,9 @@ private void reportFieldViolation(String violation, String where) {
@Override
public MethodVisitor visitMethod(final int access, final String name, final String desc, String signature, String[] exceptions) {
currentGroupId++;
if (classSuppressed) {
return null;
}
return new MethodVisitor(Opcodes.ASM5) {
private final Method myself = new Method(name, desc);
private final boolean isDeprecated = (access & Opcodes.ACC_DEPRECATED) != 0;
Expand All @@ -312,6 +329,7 @@ public MethodVisitor visitMethod(final int access, final String name, final Stri
reportMethodViolation(checkDescriptor(desc), "method declaration");
}
if (this.isDeprecated) {
maybeSuppressCurrentGroup(DEPRECATED_DESCRIPTOR);
reportMethodViolation(checkType(DEPRECATED_TYPE), "deprecation on method declaration");
}
}
Expand Down Expand Up @@ -407,6 +425,7 @@ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
// don't report 2 times!
return null;
}
maybeSuppressCurrentGroup(desc);
reportMethodViolation(checkAnnotationDescriptor(desc, visible), "annotation on method declaration");
return null;
}
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/de/thetaphi/forbiddenapis/CliMain.java
Original file line number Diff line number Diff line change
Expand Up @@ -138,14 +138,17 @@ public CliMain(String... args) throws ExitException {
}
}

@SuppressForbidden
void logError(String msg) {
System.err.println("ERROR: " + msg);
}

@SuppressForbidden
void logWarn(String msg) {
System.err.println("WARNING: " + msg);
}

@SuppressForbidden
void logInfo(String msg) {
System.out.println(msg);
}
Expand Down Expand Up @@ -321,6 +324,7 @@ public ExitException(int exitCode, String message) {
}
}

@SuppressForbidden
public static void main(String... args) {
try {
new CliMain(args).run();
Expand Down
31 changes: 31 additions & 0 deletions src/main/java/de/thetaphi/forbiddenapis/SuppressForbidden.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package de.thetaphi.forbiddenapis;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;

/*
* (C) Copyright 2015 Uwe Schindler (Generics Policeman) and others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* Default annotation to suppress forbidden-apis errors inside a whole class, a method, or a field.
* You can define your own annotation and pass a list of suppressing annotation types to the checker,
* this allows to use the feature without compile-time dependencies to forbidden-apis.
*/
@Retention(RetentionPolicy.CLASS)
@Target({ ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.TYPE })
public @interface SuppressForbidden {}
2 changes: 2 additions & 0 deletions src/tools/java/de/thetaphi/forbiddenapis/DeprecatedGen.java
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ void parseRT(InputStream in) throws IOException {
}
}

@SuppressForbidden
void writeOutput(OutputStream out) throws IOException {
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, "UTF-8"));
writer.write(header);
Expand All @@ -147,6 +148,7 @@ void writeOutput(OutputStream out) throws IOException {
System.err.println("Deprecated API signatures for Java version " + javaVersion + " written successfully.");
}

@SuppressForbidden
public static void main(String... args) throws Exception {
if (args.length != 3) {
System.err.println("Invalid parameters; must be: java_version /path/to/rt.jar /path/to/outputfile.txt");
Expand Down

0 comments on commit afce079

Please sign in to comment.