[infer][java] add support for @SuppressWarnings, step 1

Summary:
Passing the list of SuppressWarnings annotations detected during the compilation to InferPrint. The next step will be to add support for error filtering in .inferconfig and use the same mechanism. The annotation processor will generate an .inferconfig like config file and use it to suppress the reports.
master
jrm 10 years ago
parent cb52bff366
commit 1a615a467b

@ -9,41 +9,81 @@
package com.facebook.infer.annotprocess;
import javax.annotation.processing.*;
import javax.lang.model.element.*;
import javax.tools.*;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.SourceVersion;
import java.util.*;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
@SupportedAnnotationTypes({ "java.lang.SuppressWarnings" })
public class AnnotationProcessor extends AbstractProcessor {
// map of (classes -> methods in class). an empty set means suppress all warnings on class
public Map<String,Set<String>> suppressMap = new LinkedHashMap<String,Set<String>>();
private static final String ANNOTATION_ENV_VAR = "INFER_ANNOTATIONS_OUT";
private String mOutputFilename = "suppress_warnings_map.txt";
// map of (classes -> methods in class). an empty set means suppress all
// warnings on class
public Map<String, Set<String>> mSuppressMap = new LinkedHashMap<String, Set<String>>();
private void exportSuppressMap() throws FileNotFoundException, IOException {
Map<String, String> env = System.getenv();
if (env.get(ANNOTATION_ENV_VAR) == null) {
throw new RuntimeException("Env variable INFER_ANNOTATIONS_OUT not set");
} else {
mOutputFilename = env.get(ANNOTATION_ENV_VAR);
}
try (PrintWriter out = new PrintWriter(mOutputFilename)) {
for (Map.Entry<String, Set<String>> entry : mSuppressMap.entrySet()) {
out.write(entry.getKey() + " " + entry.getValue());
}
}
}
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
for (TypeElement te : annotations) {
for (Element e : env.getElementsAnnotatedWith(te)) {
if (e instanceof TypeElement) { // class
String className = ((TypeElement) e).getQualifiedName().toString();
suppressMap.put(className, Collections.EMPTY_SET);
mSuppressMap.put(className, Collections.EMPTY_SET);
} else if (e instanceof ExecutableElement) { // method
String clazz = e.getEnclosingElement().toString();
Set<String> suppressMethods = suppressMap.get(clazz);
if (suppressMethods == null) {
suppressMethods = new LinkedHashSet();
suppressMap.put(clazz, suppressMethods);
} else if (suppressMethods.isEmpty()) {
// empty set means suppress warnings on all methods in class; do nothing
String classname = e.getEnclosingElement().toString();
java.util.Set<String> suppressMethods = mSuppressMap.get(classname);
if (suppressMethods != null && suppressMethods.isEmpty()) {
// empty set means suppress warnings on all methods in class; do
// nothing
continue;
}
suppressMethods.add(clazz);
if (suppressMethods == null) {
suppressMethods = new LinkedHashSet<String>();
}
suppressMethods.add(e.toString());
mSuppressMap.put(classname, suppressMethods);
}
}
}
if (env.processingOver()) {
// TODO: write suppressMap to a .inferconfig file on disk
try {
exportSuppressMap();
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return false;
}

@ -590,6 +590,9 @@ class Infer:
'-procs', procs_report,
'-analyzer', self.args.analyzer
]
if self.javac.annotations_out is not None:
infer_print_options += [
'-local_config', self.javac.annotations_out]
exit_status = subprocess.check_call(
infer_print_cmd + infer_print_options
@ -631,6 +634,7 @@ class Infer:
def close(self):
if self.args.analyzer != COMPILE:
os.remove(self.javac.verbose_out)
os.remove(self.javac.annotations_out)
def analyze_and_report(self):
if self.args.analyzer not in [COMPILE, CAPTURE]:

@ -32,27 +32,44 @@ class CompilerCall:
self.original_arguments = arguments
self.args, self.remaining_args = parser.parse_known_args(arguments)
self.verbose_out = None
self.annotations_out = None
def run(self):
if self.args.version:
return subprocess.call(['javac'] + self.original_arguments)
else:
javac_cmd = ['javac', '-verbose', '-g']
if self.args.bootclasspath is not None:
javac_cmd += ['-bootclasspath', self.args.bootclasspath]
if self.args.classpath is not None:
javac_cmd += ['-cp', self.args.classpath]
if self.args.classpath is None:
classpath = utils.ANNOT_PROCESSOR_JAR
else:
classpath = os.pathsep.join([
utils.ANNOT_PROCESSOR_JAR,
self.args.classpath])
javac_cmd += ['-cp', classpath]
if self.args.classes_out is not None:
javac_cmd += ['-d', self.args.classes_out]
javac_cmd += self.remaining_args
javac_cmd.append('-J-Duser.language=en')
with tempfile.NamedTemporaryFile(
mode='w',
suffix='.out',
prefix='annotations_',
delete=False) as annot_out:
self.annotations_out = annot_out.name
with tempfile.NamedTemporaryFile(
mode='w',
suffix='.out',
prefix='javac_',
delete=False) as file_out:
self.verbose_out = file_out.name
os.environ['INFER_ANNOTATIONS_OUT'] = self.annotations_out
try:
subprocess.check_call(javac_cmd, stderr=file_out)
except subprocess.CalledProcessError:

@ -29,6 +29,9 @@ LIB_DIRECTORY = os.path.join(BIN_DIRECTORY, '..', 'lib', 'java')
TMP_DIRECTORY = tempfile.gettempdir()
MODELS_JAR = os.path.join(LIB_DIRECTORY, 'models.jar')
ANNOT_PROCESSOR_JAR = os.path.join(
BIN_DIRECTORY, '..', 'annotations', 'processor.jar')
DEFAULT_INFER_OUT = os.path.join(os.getcwd(), 'infer-out')
CSV_PERF_FILENAME = 'performances.csv'

@ -14,6 +14,8 @@ let inferconfig_file = ".inferconfig"
let inferconfig_home = ref None
let local_config = ref None
(** Look up a key in a json file containing a list of strings *)
let lookup_string_list key json =
Yojson.Basic.Util.filter_member key [json]

@ -12,6 +12,8 @@ type path_filter = DB.source_file -> bool
val inferconfig_home : string option ref
val local_config : string option ref
(** Filter type for an error name. *)
type error_filter = Localise.t -> bool

@ -112,6 +112,8 @@ let arg_desc =
"setup the analyzer for the path filtering";
"-inferconfig_home", Arg.String (fun s -> Inferconfig.inferconfig_home := Some s), Some "dir",
"Path to the .inferconfig file";
"-local_config", Arg.String (fun s -> Inferconfig.local_config := Some s), Some "Path",
"Path to local config file";
] in
Arg2.create_options_desc false "Options" desc in
let reserved_arg =

Loading…
Cancel
Save