diff --git a/access-modifier-annotation/src/main/java/org/kohsuke/accmod/AccessRestriction.java b/access-modifier-annotation/src/main/java/org/kohsuke/accmod/AccessRestriction.java
index 1f9fa6a..1509196 100644
--- a/access-modifier-annotation/src/main/java/org/kohsuke/accmod/AccessRestriction.java
+++ b/access-modifier-annotation/src/main/java/org/kohsuke/accmod/AccessRestriction.java
@@ -38,7 +38,7 @@
*
* Single execution of the enforcement check would create at most one instance
* of a given {@link AccessRestriction} type, so instance fields can be used to store
- * heavy-weight objects or other indicies that you might need for implementing
+ * heavy-weight objects or other indices that you might need for implementing
* access control checks.
*
* @author Kohsuke Kawaguchi
diff --git a/access-modifier-checker/pom.xml b/access-modifier-checker/pom.xml
index fe0278b..3b2e3f1 100644
--- a/access-modifier-checker/pom.xml
+++ b/access-modifier-checker/pom.xml
@@ -22,6 +22,11 @@
access-modifier-annotation
${project.version}
+
+ ${project.groupId}
+ access-modifier-suppressions
+ ${project.version}
+
org.ow2.asm
asm-debug-all
diff --git a/access-modifier-checker/src/it/disable-restrictions-wrong-annotation/api/pom.xml b/access-modifier-checker/src/it/disable-restrictions-wrong-annotation/api/pom.xml
new file mode 100644
index 0000000..be7ee62
--- /dev/null
+++ b/access-modifier-checker/src/it/disable-restrictions-wrong-annotation/api/pom.xml
@@ -0,0 +1,17 @@
+
+
+ 4.0.0
+
+ test
+ disable-restrictions-wrong-annotation
+ 1.0-SNAPSHOT
+
+ api
+
+
+ org.kohsuke
+ access-modifier-annotation
+ @project.version@
+
+
+
\ No newline at end of file
diff --git a/access-modifier-checker/src/it/disable-restrictions-wrong-annotation/api/src/main/java/api/ApiWithRestrictedMethodAndField.java b/access-modifier-checker/src/it/disable-restrictions-wrong-annotation/api/src/main/java/api/ApiWithRestrictedMethodAndField.java
new file mode 100644
index 0000000..82faef5
--- /dev/null
+++ b/access-modifier-checker/src/it/disable-restrictions-wrong-annotation/api/src/main/java/api/ApiWithRestrictedMethodAndField.java
@@ -0,0 +1,13 @@
+package api;
+
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.NoExternalUse;
+
+public class ApiWithRestrictedMethodAndField {
+
+ @Restricted(NoExternalUse.class)
+ public String field;
+
+ @Restricted(NoExternalUse.class)
+ public static void notReallyPublic() {}
+}
diff --git a/access-modifier-checker/src/it/disable-restrictions-wrong-annotation/api/src/main/java/api/RestrictedApi.java b/access-modifier-checker/src/it/disable-restrictions-wrong-annotation/api/src/main/java/api/RestrictedApi.java
new file mode 100644
index 0000000..ef23039
--- /dev/null
+++ b/access-modifier-checker/src/it/disable-restrictions-wrong-annotation/api/src/main/java/api/RestrictedApi.java
@@ -0,0 +1,12 @@
+package api;
+
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.NoExternalUse;
+
+@Restricted(NoExternalUse.class)
+public class RestrictedApi {
+
+ public String field;
+
+ public void doNotUse() {}
+}
diff --git a/access-modifier-checker/src/it/disable-restrictions-wrong-annotation/api/src/main/java/api/RestrictedInterface.java b/access-modifier-checker/src/it/disable-restrictions-wrong-annotation/api/src/main/java/api/RestrictedInterface.java
new file mode 100644
index 0000000..c51fd14
--- /dev/null
+++ b/access-modifier-checker/src/it/disable-restrictions-wrong-annotation/api/src/main/java/api/RestrictedInterface.java
@@ -0,0 +1,8 @@
+package api;
+
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.NoExternalUse;
+
+@Restricted(NoExternalUse.class)
+public interface RestrictedInterface {
+}
diff --git a/access-modifier-checker/src/it/disable-restrictions-wrong-annotation/caller/pom.xml b/access-modifier-checker/src/it/disable-restrictions-wrong-annotation/caller/pom.xml
new file mode 100644
index 0000000..55c0989
--- /dev/null
+++ b/access-modifier-checker/src/it/disable-restrictions-wrong-annotation/caller/pom.xml
@@ -0,0 +1,33 @@
+
+
+ 4.0.0
+
+ test
+ disable-restrictions-wrong-annotation
+ 1.0-SNAPSHOT
+
+ caller
+
+
+ ${project.groupId}
+ api
+ ${project.version}
+
+
+
+
+
+ org.kohsuke
+ access-modifier-checker
+ @project.version@
+
+
+
+ enforce
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/access-modifier-checker/src/it/disable-restrictions-wrong-annotation/caller/src/main/java/caller/Caller.java b/access-modifier-checker/src/it/disable-restrictions-wrong-annotation/caller/src/main/java/caller/Caller.java
new file mode 100644
index 0000000..6591965
--- /dev/null
+++ b/access-modifier-checker/src/it/disable-restrictions-wrong-annotation/caller/src/main/java/caller/Caller.java
@@ -0,0 +1,11 @@
+package caller;
+
+import api.ApiWithRestrictedMethodAndField;
+
+public class Caller {
+
+ @WrongAnnotation(ApiWithRestrictedMethodAndField.class)
+ public Caller() {
+ ApiWithRestrictedMethodAndField.notReallyPublic(); // illegal
+ }
+}
diff --git a/access-modifier-checker/src/it/disable-restrictions-wrong-annotation/caller/src/main/java/caller/CallerDisabledAtClassLevel.java b/access-modifier-checker/src/it/disable-restrictions-wrong-annotation/caller/src/main/java/caller/CallerDisabledAtClassLevel.java
new file mode 100644
index 0000000..b912657
--- /dev/null
+++ b/access-modifier-checker/src/it/disable-restrictions-wrong-annotation/caller/src/main/java/caller/CallerDisabledAtClassLevel.java
@@ -0,0 +1,10 @@
+package caller;
+
+import api.ApiWithRestrictedMethodAndField;
+
+@WrongAnnotation(ApiWithRestrictedMethodAndField.class)
+public class CallerDisabledAtClassLevel {
+ public CallerDisabledAtClassLevel() {
+ ApiWithRestrictedMethodAndField.notReallyPublic(); // illegal
+ }
+}
\ No newline at end of file
diff --git a/access-modifier-checker/src/it/disable-restrictions-wrong-annotation/caller/src/main/java/caller/WrongAnnotation.java b/access-modifier-checker/src/it/disable-restrictions-wrong-annotation/caller/src/main/java/caller/WrongAnnotation.java
new file mode 100644
index 0000000..3ff929f
--- /dev/null
+++ b/access-modifier-checker/src/it/disable-restrictions-wrong-annotation/caller/src/main/java/caller/WrongAnnotation.java
@@ -0,0 +1,38 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2018, Steve Arch
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package caller;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Retention(RUNTIME)
+@Documented
+@Target({ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.TYPE})
+public @interface WrongAnnotation {
+ Class>[] value();
+}
diff --git a/access-modifier-checker/src/it/disable-restrictions-wrong-annotation/invoker.properties b/access-modifier-checker/src/it/disable-restrictions-wrong-annotation/invoker.properties
new file mode 100644
index 0000000..8b87cc7
--- /dev/null
+++ b/access-modifier-checker/src/it/disable-restrictions-wrong-annotation/invoker.properties
@@ -0,0 +1,2 @@
+invoker.goals=clean package
+invoker.buildResult = failure
diff --git a/access-modifier-checker/src/it/disable-restrictions-wrong-annotation/pom.xml b/access-modifier-checker/src/it/disable-restrictions-wrong-annotation/pom.xml
new file mode 100644
index 0000000..4886872
--- /dev/null
+++ b/access-modifier-checker/src/it/disable-restrictions-wrong-annotation/pom.xml
@@ -0,0 +1,17 @@
+
+
+ 4.0.0
+ test
+ disable-restrictions-wrong-annotation
+ 1.0-SNAPSHOT
+ pom
+
+ UTF-8
+ 1.7
+ 1.7
+
+
+ api
+ caller
+
+
\ No newline at end of file
diff --git a/access-modifier-checker/src/it/disable-restrictions-wrong-annotation/postbuild.groovy b/access-modifier-checker/src/it/disable-restrictions-wrong-annotation/postbuild.groovy
new file mode 100644
index 0000000..ae75e56
--- /dev/null
+++ b/access-modifier-checker/src/it/disable-restrictions-wrong-annotation/postbuild.groovy
@@ -0,0 +1,2 @@
+assert new File(basedir, 'build.log').text.contains('[ERROR] caller/Caller:9 api/ApiWithRestrictedMethodAndField.notReallyPublic()V must not be used')
+assert new File(basedir, 'build.log').text.contains('[ERROR] caller/CallerDisabledAtClassLevel:8 api/ApiWithRestrictedMethodAndField.notReallyPublic()V must not be used')
diff --git a/access-modifier-checker/src/it/disable-restrictions/api/pom.xml b/access-modifier-checker/src/it/disable-restrictions/api/pom.xml
new file mode 100644
index 0000000..bd06be5
--- /dev/null
+++ b/access-modifier-checker/src/it/disable-restrictions/api/pom.xml
@@ -0,0 +1,17 @@
+
+
+ 4.0.0
+
+ test
+ disable-restrictions
+ 1.0-SNAPSHOT
+
+ api
+
+
+ org.kohsuke
+ access-modifier-annotation
+ @project.version@
+
+
+
\ No newline at end of file
diff --git a/access-modifier-checker/src/it/disable-restrictions/api/src/main/java/api/ApiWithRestrictedMethodAndField.java b/access-modifier-checker/src/it/disable-restrictions/api/src/main/java/api/ApiWithRestrictedMethodAndField.java
new file mode 100644
index 0000000..82faef5
--- /dev/null
+++ b/access-modifier-checker/src/it/disable-restrictions/api/src/main/java/api/ApiWithRestrictedMethodAndField.java
@@ -0,0 +1,13 @@
+package api;
+
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.NoExternalUse;
+
+public class ApiWithRestrictedMethodAndField {
+
+ @Restricted(NoExternalUse.class)
+ public String field;
+
+ @Restricted(NoExternalUse.class)
+ public static void notReallyPublic() {}
+}
diff --git a/access-modifier-checker/src/it/disable-restrictions/api/src/main/java/api/RestrictedApi.java b/access-modifier-checker/src/it/disable-restrictions/api/src/main/java/api/RestrictedApi.java
new file mode 100644
index 0000000..ef23039
--- /dev/null
+++ b/access-modifier-checker/src/it/disable-restrictions/api/src/main/java/api/RestrictedApi.java
@@ -0,0 +1,12 @@
+package api;
+
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.NoExternalUse;
+
+@Restricted(NoExternalUse.class)
+public class RestrictedApi {
+
+ public String field;
+
+ public void doNotUse() {}
+}
diff --git a/access-modifier-checker/src/it/disable-restrictions/api/src/main/java/api/RestrictedInterface.java b/access-modifier-checker/src/it/disable-restrictions/api/src/main/java/api/RestrictedInterface.java
new file mode 100644
index 0000000..c51fd14
--- /dev/null
+++ b/access-modifier-checker/src/it/disable-restrictions/api/src/main/java/api/RestrictedInterface.java
@@ -0,0 +1,8 @@
+package api;
+
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.NoExternalUse;
+
+@Restricted(NoExternalUse.class)
+public interface RestrictedInterface {
+}
diff --git a/access-modifier-checker/src/it/disable-restrictions/caller/pom.xml b/access-modifier-checker/src/it/disable-restrictions/caller/pom.xml
new file mode 100644
index 0000000..95d97b3
--- /dev/null
+++ b/access-modifier-checker/src/it/disable-restrictions/caller/pom.xml
@@ -0,0 +1,38 @@
+
+
+ 4.0.0
+
+ test
+ disable-restrictions
+ 1.0-SNAPSHOT
+
+ caller
+
+
+ ${project.groupId}
+ api
+ ${project.version}
+
+
+ org.kohsuke
+ access-modifier-suppressions
+ @project.version@
+
+
+
+
+
+ org.kohsuke
+ access-modifier-checker
+ @project.version@
+
+
+
+ enforce
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/access-modifier-checker/src/it/disable-restrictions/caller/src/main/java/caller/Caller.java b/access-modifier-checker/src/it/disable-restrictions/caller/src/main/java/caller/Caller.java
new file mode 100644
index 0000000..040d5f9
--- /dev/null
+++ b/access-modifier-checker/src/it/disable-restrictions/caller/src/main/java/caller/Caller.java
@@ -0,0 +1,36 @@
+package caller;
+
+import api.ApiWithRestrictedMethodAndField;
+import api.RestrictedApi;
+import org.kohsuke.accmod.restrictions.suppressions.SuppressRestrictedWarnings;
+
+public class Caller extends ApiWithRestrictedMethodAndField { // This is fine, ApiWithRestrictedMethodAndField itself is not restricted
+
+ private RestrictedApi restrictedApi;
+
+ @SuppressRestrictedWarnings(ApiWithRestrictedMethodAndField.class)
+ public Caller() {
+ ApiWithRestrictedMethodAndField.notReallyPublic(); // illegal but check disabled at the method level
+ }
+
+ @SuppressRestrictedWarnings({RestrictedApi.class, ApiWithRestrictedMethodAndField.class})
+ private void invalidFieldUse() {
+ restrictedApi.field = null;
+ super.field = null;
+ }
+
+ @SuppressRestrictedWarnings(ApiWithRestrictedMethodAndField.class)
+ public void callerMethod() {
+ ApiWithRestrictedMethodAndField.notReallyPublic(); // illegal but check disabled at the method level
+ }
+
+ @SuppressRestrictedWarnings(RestrictedApi.class)
+ public void methodWithRestrictedParameter(RestrictedApi api) {
+ api.doNotUse(); // illegal but check disabled at the method level
+ }
+
+ @SuppressRestrictedWarnings(RestrictedApi.class)
+ public RestrictedApi getRestrictedApi() {
+ return new RestrictedApi(); // illegal but check disabled at the method level
+ }
+}
diff --git a/access-modifier-checker/src/it/disable-restrictions/caller/src/main/java/caller/CallerDisabledAtClassLevel.java b/access-modifier-checker/src/it/disable-restrictions/caller/src/main/java/caller/CallerDisabledAtClassLevel.java
new file mode 100644
index 0000000..1314fbc
--- /dev/null
+++ b/access-modifier-checker/src/it/disable-restrictions/caller/src/main/java/caller/CallerDisabledAtClassLevel.java
@@ -0,0 +1,32 @@
+package caller;
+
+import api.ApiWithRestrictedMethodAndField;
+import api.RestrictedApi;
+import org.kohsuke.accmod.restrictions.suppressions.SuppressRestrictedWarnings;
+
+@SuppressRestrictedWarnings( {ApiWithRestrictedMethodAndField.class, RestrictedApi.class})
+public class CallerDisabledAtClassLevel extends RestrictedApi {
+
+ private RestrictedApi restrictedApi;
+
+ public CallerDisabledAtClassLevel() {
+ ApiWithRestrictedMethodAndField.notReallyPublic(); // illegal but check disabled at the class level
+ }
+
+ private void invalidFieldUse() {
+ restrictedApi.field = null;
+ super.field = null;
+ }
+
+ public void callerMethod() {
+ ApiWithRestrictedMethodAndField.notReallyPublic(); // illegal but check disabled at the class level
+ }
+
+ public void methodWithRestrictedParameter(RestrictedApi api) {
+ api.doNotUse(); // illegal but check disabled at the class level
+ }
+
+ public RestrictedApi getRestrictedApi() {
+ return new RestrictedApi(); // illegal but check disabled at the class level
+ }
+}
\ No newline at end of file
diff --git a/access-modifier-checker/src/it/disable-restrictions/caller/src/main/java/caller/RestrictedApiSubclass.java b/access-modifier-checker/src/it/disable-restrictions/caller/src/main/java/caller/RestrictedApiSubclass.java
new file mode 100644
index 0000000..f03ff47
--- /dev/null
+++ b/access-modifier-checker/src/it/disable-restrictions/caller/src/main/java/caller/RestrictedApiSubclass.java
@@ -0,0 +1,8 @@
+package caller;
+
+import api.RestrictedApi;
+import org.kohsuke.accmod.restrictions.suppressions.SuppressRestrictedWarnings;
+
+@SuppressRestrictedWarnings(RestrictedApi.class)
+public class RestrictedApiSubclass extends RestrictedApi {
+}
diff --git a/access-modifier-checker/src/it/disable-restrictions/caller/src/main/java/caller/RestrictedInterfaceImplementation.java b/access-modifier-checker/src/it/disable-restrictions/caller/src/main/java/caller/RestrictedInterfaceImplementation.java
new file mode 100644
index 0000000..d09afcc
--- /dev/null
+++ b/access-modifier-checker/src/it/disable-restrictions/caller/src/main/java/caller/RestrictedInterfaceImplementation.java
@@ -0,0 +1,8 @@
+package caller;
+
+import api.RestrictedInterface;
+import org.kohsuke.accmod.restrictions.suppressions.SuppressRestrictedWarnings;
+
+@SuppressRestrictedWarnings(RestrictedInterface.class)
+public class RestrictedInterfaceImplementation implements RestrictedInterface {
+}
diff --git a/access-modifier-checker/src/it/disable-restrictions/invoker.properties b/access-modifier-checker/src/it/disable-restrictions/invoker.properties
new file mode 100644
index 0000000..5c363f7
--- /dev/null
+++ b/access-modifier-checker/src/it/disable-restrictions/invoker.properties
@@ -0,0 +1,2 @@
+invoker.goals=clean package
+invoker.buildResult = success
diff --git a/access-modifier-checker/src/it/disable-restrictions/pom.xml b/access-modifier-checker/src/it/disable-restrictions/pom.xml
new file mode 100644
index 0000000..c03dd1e
--- /dev/null
+++ b/access-modifier-checker/src/it/disable-restrictions/pom.xml
@@ -0,0 +1,17 @@
+
+
+ 4.0.0
+ test
+ disable-restrictions
+ 1.0-SNAPSHOT
+ pom
+
+ UTF-8
+ 1.7
+ 1.7
+
+
+ api
+ caller
+
+
\ No newline at end of file
diff --git a/access-modifier-checker/src/main/java/org/kohsuke/accmod/impl/Checker.java b/access-modifier-checker/src/main/java/org/kohsuke/accmod/impl/Checker.java
index bb504d1..42160c8 100644
--- a/access-modifier-checker/src/main/java/org/kohsuke/accmod/impl/Checker.java
+++ b/access-modifier-checker/src/main/java/org/kohsuke/accmod/impl/Checker.java
@@ -23,9 +23,13 @@
*/
package org.kohsuke.accmod.impl;
+import java.util.HashSet;
+import java.util.Set;
+import org.apache.maven.plugin.logging.Log;
import org.kohsuke.accmod.AccessRestriction;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.impl.Restrictions.Parser;
+import org.kohsuke.accmod.restrictions.suppressions.SuppressRestrictedWarnings;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
@@ -85,12 +89,17 @@ public class Checker {
private final AccessRestrictionFactory factory;
+ private final Log log;
- Checker(ClassLoader dependencies, ErrorListener errorListener, Properties properties) throws IOException {
+ private int line;
+
+ Checker(ClassLoader dependencies, ErrorListener errorListener, Properties properties,
+ Log log) throws IOException {
this.dependencies = dependencies;
this.errorListener = errorListener;
this.properties = properties;
this.factory = new AccessRestrictionFactory(dependencies);
+ this.log = log;
// load access restrictions
loadAccessRestrictions();
@@ -228,132 +237,17 @@ public void checkClass(File clazz) throws IOException {
FileInputStream in = new FileInputStream(clazz);
try {
ClassReader cr = new ClassReader(in);
- cr.accept(new ClassVisitor(Opcodes.ASM5) {
- private String className;
- private String methodName,methodDesc;
- private int line;
-
- @Override
- public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
- this.className = name;
-
- if (isSynthetic(access)) {
- return;
- }
-
- if (superName != null) {
- for (Restrictions r : getRestrictions(superName)) {
- r.usedAsSuperType(currentLocation, errorListener);
- }
- }
-
- if (interfaces != null) {
- for (String intf : interfaces) {
- for (Restrictions r : getRestrictions(intf)) {
- r.usedAsInterface(currentLocation, errorListener);
- }
- }
- }
- }
-
- @Override
- public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
- this.methodName = name;
- this.methodDesc = desc;
-
- if (isSynthetic(access)) {
- return null;
- }
-
- return new MethodVisitor(Opcodes.ASM5) {
- @Override
- public void visitLineNumber(int _line, Label start) {
- line = _line;
- }
-
- public void visitTypeInsn(int opcode, String type) {
- switch (opcode) {
- case Opcodes.NEW:
- for (Restrictions r : getRestrictions(type)) {
- r.instantiated(currentLocation, errorListener);
- }
- }
- }
-
- @Override
- public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
- for (Restrictions r : getRestrictions(owner + '.' + name + desc)) {
- r.invoked(currentLocation, errorListener);
- }
- }
-
- @Override
- public void visitFieldInsn(int opcode, String owner, String name, String desc) {
- Iterable rs = getRestrictions(owner + '.' + name);
- switch (opcode) {
- case Opcodes.GETSTATIC:
- case Opcodes.GETFIELD:
- for (Restrictions r : rs) {
- r.read(currentLocation, errorListener);
- }
- break;
- case Opcodes.PUTSTATIC:
- case Opcodes.PUTFIELD:
- for (Restrictions r : rs) {
- r.written(currentLocation, errorListener);
- }
- break;
- }
- super.visitFieldInsn(opcode, owner, name, desc);
- }
- };
- }
-
-
- /**
- * Constant that represents the current location.
- */
- private final Location currentLocation = new Location() {
- public String getClassName() {
- return className.replace('/','.');
- }
-
- public String getMethodName() {
- return methodName;
- }
-
- public String getMethodDescriptor() {
- return methodDesc;
- }
-
- public int getLineNumber() {
- return line;
- }
-
- public String toString() {
- return className+':'+line;
- }
-
- public ClassLoader getDependencyClassLoader() {
- return dependencies;
- }
-
- @Override
- public String getProperty(String key) {
- return properties.getProperty(key);
- }
- };
- }, SKIP_FRAMES);
+ cr.accept(new RestrictedClassVisitor(), SKIP_FRAMES);
} finally {
in.close();
}
}
- private Iterable getRestrictions(String keyName) {
+ private Iterable getRestrictions(String keyName, Set skippedTypes) {
List rs = new ArrayList<>();
Restrictions r = restrictions.get(keyName);
- if (r != null) {
- rs.add(r);
+ if (r != null && skippedTypes.isEmpty()) {
+ rs.add(r); // Don't get it from the cache if we have types that we want to skip
}
int idx = Integer.MAX_VALUE;
while (true) {
@@ -366,6 +260,10 @@ private Iterable getRestrictions(String keyName) {
}
idx = newIdx;
keyName = keyName.substring(0, idx);
+ if(skippedTypes.contains(Type.getObjectType(keyName))) {
+ // We have hit a type that should be skipped - do not add it to the restrictions
+ break;
+ }
r = restrictions.get(keyName);
if (r != null) {
Collection applicable = new ArrayList<>();
@@ -387,4 +285,201 @@ private Iterable getRestrictions(String keyName) {
private static boolean isSynthetic(int access) {
return (access & Opcodes.ACC_SYNTHETIC) != 0;
}
+
+ private class RestrictedClassVisitor extends ClassVisitor {
+ private String className;
+ private String methodName, methodDesc;
+ private String superName;
+ private String[] interfaces;
+ private RestrictedAnnotationVisitor annotationVisitor = new RestrictedAnnotationVisitor();
+
+ private Set getSkippedTypes() {
+ return annotationVisitor.getSkippedTypes();
+ }
+
+ public RestrictedClassVisitor() {
+ super(Opcodes.ASM5);
+ }
+
+ @Override
+ public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
+ this.className = name;
+
+ if (isSynthetic(access)) {
+ return;
+ }
+
+ this.superName = superName;
+ this.interfaces = interfaces;
+ }
+
+ @Override
+ public void visitEnd() {
+ // We need to do this in visitEnd so that we have parsed the annotations _before_ doing these checks
+ if (superName != null) {
+ for (Restrictions r : getRestrictions(superName, getSkippedTypes())) {
+ r.usedAsSuperType(currentLocation, errorListener);
+ }
+ }
+ if (interfaces != null) {
+ for (String intf : interfaces) {
+ for (Restrictions r : getRestrictions(intf, getSkippedTypes())) {
+ r.usedAsInterface(currentLocation, errorListener);
+ }
+ }
+ }
+ }
+
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
+ this.methodName = name;
+ this.methodDesc = desc;
+
+ if (isSynthetic(access)) {
+ return null;
+ }
+
+ return new RestrictedMethodVisitor(currentLocation, annotationVisitor.getSkippedTypes());
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ return Type.getType(SuppressRestrictedWarnings.class).equals(Type.getType(desc))
+ ? annotationVisitor
+ : super.visitAnnotation(desc, visible); }
+
+ /**
+ * Constant that represents the current location.
+ */
+ private final Location currentLocation = new Location() {
+ public String getClassName() {
+ return className.replace('/','.');
+ }
+
+ public String getMethodName() {
+ return methodName;
+ }
+
+ public String getMethodDescriptor() {
+ return methodDesc;
+ }
+
+ public int getLineNumber() {
+ return line;
+ }
+
+ public String toString() {
+ return className+':'+line;
+ }
+
+ public ClassLoader getDependencyClassLoader() {
+ return dependencies;
+ }
+
+ @Override
+ public String getProperty(String key) {
+ return properties.getProperty(key);
+ }
+ };
+ }
+
+ private class RestrictedMethodVisitor extends MethodVisitor {
+
+ private final Set skippedTypesFromParent;
+ private Location currentLocation;
+ private RestrictedAnnotationVisitor annotationVisitor = new RestrictedAnnotationVisitor();
+
+ private Set getSkippedTypes() {
+ Set allSkippedTypes = new HashSet<>(skippedTypesFromParent);
+ allSkippedTypes.addAll(annotationVisitor.getSkippedTypes());
+ return allSkippedTypes;
+ }
+
+ public RestrictedMethodVisitor(Location currentLocation, Set skippedTypes) {
+ super(Opcodes.ASM5);
+ log.debug(String.format("New method visitor at %s#%s",
+ currentLocation.getClassName(), currentLocation.getMethodName()));
+ this.currentLocation = currentLocation;
+ this.skippedTypesFromParent = skippedTypes;
+ }
+
+ @Override
+ public void visitLineNumber(int _line, Label start) {
+ line = _line;
+ }
+
+ public void visitTypeInsn(int opcode, String type) {
+ switch (opcode) {
+ case Opcodes.NEW:
+ for (Restrictions r : getRestrictions(type, getSkippedTypes())) {
+ r.instantiated(currentLocation, errorListener);
+ }
+ }
+ }
+
+ @Override
+ public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
+ log.debug(String.format("Visiting method %s#%s", owner, name));
+ for (Restrictions r : getRestrictions(owner + '.' + name + desc, getSkippedTypes())) {
+ r.invoked(currentLocation, errorListener);
+ }
+ }
+
+ @Override
+ public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+ log.debug(String.format("Visiting field '%s %s' in type %s", desc, name, owner));
+
+ Iterable rs = getRestrictions(owner + '.' + name, getSkippedTypes());
+ switch (opcode) {
+ case Opcodes.GETSTATIC:
+ case Opcodes.GETFIELD:
+ for (Restrictions r : rs) {
+ r.read(currentLocation, errorListener);
+ }
+ break;
+ case Opcodes.PUTSTATIC:
+ case Opcodes.PUTFIELD:
+ for (Restrictions r : rs) {
+ r.written(currentLocation, errorListener);
+ }
+ break;
+ }
+ super.visitFieldInsn(opcode, owner, name, desc);
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ return Type.getType(SuppressRestrictedWarnings.class).equals(Type.getType(desc))
+ ? annotationVisitor
+ : super.visitAnnotation(desc, visible);
+ }
+ }
+
+ private class RestrictedAnnotationVisitor extends AnnotationVisitor {
+
+ private Set skippedRestrictedClasses = new HashSet<>();
+
+ public RestrictedAnnotationVisitor() {
+ super(Opcodes.ASM5);
+ }
+
+ public Set getSkippedTypes() {
+ return skippedRestrictedClasses;
+ }
+
+ @Override
+ public AnnotationVisitor visitArray(String name) {
+ return new AnnotationVisitor(Opcodes.ASM5) {
+
+ @Override
+ public void visit(String name, Object value) {
+ Type type = value instanceof Type ? (Type) value : Type.getType(value.getClass());
+ log.debug(String.format("Skipping @%s class: %s",
+ Restricted.class.getSimpleName(), type.getClassName()));
+ skippedRestrictedClasses.add(type);
+ super.visit(name, value);
+ }
+ };
+ }
+ }
}
diff --git a/access-modifier-checker/src/main/java/org/kohsuke/accmod/impl/EnforcerMojo.java b/access-modifier-checker/src/main/java/org/kohsuke/accmod/impl/EnforcerMojo.java
index 55e5c83..ca769b4 100644
--- a/access-modifier-checker/src/main/java/org/kohsuke/accmod/impl/EnforcerMojo.java
+++ b/access-modifier-checker/src/main/java/org/kohsuke/accmod/impl/EnforcerMojo.java
@@ -84,7 +84,7 @@ public void onError(Throwable t, Location loc, String msg) {
public void onWarning(Throwable t, Location loc, String msg) {
getLog().warn(loc+" "+msg,t);
}
- }, properties != null ? properties : new Properties());
+ }, properties != null ? properties : new Properties(), getLog());
{// if there's restriction list in the inspected module itself, load it as well
InputStream self = null;
diff --git a/access-modifier-suppressions/pom.xml b/access-modifier-suppressions/pom.xml
new file mode 100644
index 0000000..aded61f
--- /dev/null
+++ b/access-modifier-suppressions/pom.xml
@@ -0,0 +1,29 @@
+
+
+ 4.0.0
+
+ access-modifier
+ org.kohsuke
+ 1.16-SNAPSHOT
+
+ access-modifier-suppressions
+
+ Suppression for Access Modifier annotations
+ This module allows you to enable suppressions for turning off warnings about Restricted APIs.
+ !!!WARNING!!!
+ Classes are marked as @Restricted for a reason and this module should not be used lightly! It implies that the
+ author does not intend for them to be used outside their defined scope and as such they may be
+ changed/modified/removed at any stage without warning. A simple upgrade of the dependency may break your module. Use
+ at your own risk.
+ You should try to not use @Restricted classes in the first place, but if you _must_ use them, this is a less-brutal
+ approach than just disabling the access-modifier-checker entirely
+
+
+
+
+ org.kohsuke
+ access-modifier-annotation
+ ${project.version}
+
+
+
diff --git a/access-modifier-suppressions/src/main/java/org/kohsuke/accmod/restrictions/suppressions/SuppressRestrictedWarnings.java b/access-modifier-suppressions/src/main/java/org/kohsuke/accmod/restrictions/suppressions/SuppressRestrictedWarnings.java
new file mode 100644
index 0000000..b2f2224
--- /dev/null
+++ b/access-modifier-suppressions/src/main/java/org/kohsuke/accmod/restrictions/suppressions/SuppressRestrictedWarnings.java
@@ -0,0 +1,51 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2018, Steve Arch
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package org.kohsuke.accmod.restrictions.suppressions;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import org.kohsuke.accmod.Restricted;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Indicates that certain classes annotated with {@link Restricted} annotations should be skipped during the
+ * access-modifier-check.
+ *
+ * Warning! Classes are markes as {@link Restricted} for a reason! Do not use these suppressions lightly.
+ * Use at your own risk
+ *
+ * @author Steve Arch
+ */
+@Retention(RUNTIME)
+@Documented
+@Target({ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.TYPE})
+public @interface SuppressRestrictedWarnings {
+ /**
+ * The classes that are marked as {@link Restricted} that should be skipped from the scan.
+ */
+ Class>[] value();
+}
diff --git a/pom.xml b/pom.xml
index a851974..2ca0fdc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -18,6 +18,7 @@
access-modifier-annotation
access-modifier-checker
+ access-modifier-suppressions