Simplify the code to collect the list of @SuppressWarnings annotations

Summary:public
Simplifies the code to collect the `SuppressWarnings` annotations and makes the code more robust in the sense that not finding the output of the annotation processor will result in an error directly at the top-level instead of later on when trying to load the output file in the Java frontend.

Reviewed By: sblackshear

Differential Revision: D3034690

fb-gh-sync-id: 60caa0c
shipit-source-id: 60caa0c
master
jrm 9 years ago committed by Facebook Github Bot 9
parent 8d62fd12ca
commit 81a59515e4

@ -16,9 +16,7 @@ import com.sun.source.util.TreePath;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -29,7 +27,6 @@ import javax.annotation.processing.SupportedOptions;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
@SupportedOptions({ "classSourceMapOutputFilename" })
// this says: "process all classes, even ones without any annotations"

@ -9,11 +9,9 @@
package com.facebook.infer.annotprocess;
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;
@ -22,39 +20,37 @@ import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedOptions;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
@SupportedOptions({ "SuppressWarningsOutputFilename" })
@SupportedAnnotationTypes({ "java.lang.SuppressWarnings" })
public class CollectSuppressWarnings extends AbstractProcessor {
private static final String ANNOTATION_ENV_VAR = "INFER_ANNOTATIONS_OUT";
private static final String OUTPUT_FILENAME_OPTION = "SuppressWarningsOutputFilename";
private String mOutputFilename;
// map of (classes -> methods in class). an empty set means suppress all
// warnings on class
// 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>>();
// total number of classes/methods with a SuppressWarnings annotation
private int mNumToSuppress = 0;
// write the methods/classes to suppress to .inferconfig-style JSON
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);
}
private void exportSuppressMap() throws IOException {
Map<String, String> options = processingEnv.getOptions();
String mOutputFilename =
Preconditions.checkNotNull(options.get(OUTPUT_FILENAME_OPTION),
"The filename should be passed from the Infer top-level script");
// output .inferconfig format file in JSON
try (PrintWriter out = new PrintWriter(mOutputFilename)) {
int elemCount = 0;
out.println("{ \"suppress_procedures\": [");
out.println("{ \"suppress_warnings\": [");
for (Map.Entry<String, Set<String>> entry : mSuppressMap.entrySet()) {
String clazz = entry.getKey();
Set<String> methods = entry.getValue();
@ -79,10 +75,11 @@ public class CollectSuppressWarnings extends AbstractProcessor {
}
// collect all of the SuppressWarnings annotations from the Java source files being compiled
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
Elements elements = processingEnv.getElementUtils();
for (TypeElement te : annotations) {
for (Element e : env.getElementsAnnotatedWith(te)) {
for (Element e : roundEnv.getElementsAnnotatedWith(te)) {
SuppressWarnings annot = e.getAnnotation(SuppressWarnings.class);
if (annot != null && shouldProcess(annot)) {
if (e instanceof TypeElement) { // class
@ -107,13 +104,14 @@ public class CollectSuppressWarnings extends AbstractProcessor {
}
}
if (env.processingOver() && mNumToSuppress > 0) {
if (roundEnv.processingOver() && mNumToSuppress > 0) {
try {
exportSuppressMap();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return false;
}

@ -0,0 +1,21 @@
/*
* Copyright (c) 2015 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.infer.annotprocess;
public class Preconditions {
public static <T> T checkNotNull(T reference, String errorMessage) {
if (reference == null) {
throw new NullPointerException(errorMessage);
}
return reference;
}
}

@ -53,6 +53,7 @@ LOG_FILE = 'toplevel.log'
BUCK_INFER_OUT = 'infer'
CLASS_SOURCE_MAP_OUTPUT_FILENAME_OPTION = 'classSourceMapOutputFilename'
SUPRESS_WARNINGS_OUTPUT_FILENAME_OPTION = 'SuppressWarningsOutputFilename'
# exit value when infer finds something to report

@ -80,7 +80,6 @@ class CompilerCall(object):
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:
@ -136,7 +135,10 @@ class CompilerCall(object):
suffix='.out',
prefix='annotations_',
delete=False) as annot_out:
self.annotations_out = annot_out.name
self.suppress_warnings_out = annot_out.name
javac_cmd += ['-A%s=%s' %
(config.SUPRESS_WARNINGS_OUTPUT_FILENAME_OPTION,
self.suppress_warnings_out)]
with tempfile.NamedTemporaryFile(
mode='w',
@ -144,7 +146,6 @@ class CompilerCall(object):
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:
@ -241,14 +242,12 @@ class AnalyzerWithFrontendWrapper(analyze.AnalyzerWrapper):
infer_cmd += [
'-results_dir', self.args.infer_out,
'-verbose_out', self.javac.verbose_out,
'-suppress_warnings_out', self.javac.suppress_warnings_out,
]
if os.path.isfile(config.MODELS_JAR):
infer_cmd += ['-models', config.MODELS_JAR]
if self.javac.annotations_out is not None:
infer_cmd += ['-local_config', self.javac.annotations_out]
infer_cmd.append('-no-static_final')
if self.args.debug:
@ -276,4 +275,4 @@ class AnalyzerWithFrontendWrapper(analyze.AnalyzerWrapper):
def _close(self):
os.remove(self.javac.verbose_out)
os.remove(self.javac.annotations_out)
os.remove(self.javac.suppress_warnings_out)

@ -12,7 +12,7 @@ let inferconfig_file = ".inferconfig"
let inferconfig_home = ref None
let local_config = ref None
let suppress_warnings_annotations = ref None
(** Look up a key in a json file containing a list of strings *)
let lookup_string_list key json =
@ -280,8 +280,8 @@ module NeverReturnNull = FileOrProcMatcher(struct
let json_key = "never_returning_null"
end)
module ProcMatcher = FileOrProcMatcher(struct
let json_key = "suppress_procedures"
module SuppressWarningsMatcher = FileOrProcMatcher(struct
let json_key = "suppress_warnings"
end)
module SkipTranslationMatcher = FileOrProcMatcher(struct

@ -9,7 +9,7 @@
val inferconfig_home : string option ref
val local_config : string option ref
val suppress_warnings_annotations : string option ref
(** get the path to the .inferconfig file *)
val inferconfig : unit -> string
@ -45,7 +45,7 @@ module NeverReturnNull : Matcher
module SkipTranslationMatcher : Matcher
module ProcMatcher : Matcher
module SuppressWarningsMatcher : Matcher
(** Load the config file and list the files to report on *)
val test: unit -> unit

@ -55,12 +55,11 @@ let log_issue
?(pre = None)
exn =
let should_suppress_warnings summary =
if !Config.curr_language = Config.C_CPP then false
else
let annotated_signature =
Annotations.get_annotated_signature summary.Specs.attributes in
let ret_annotation, _ = annotated_signature.Annotations.ret in
Annotations.ia_is_suppress_warnings ret_annotation in
!Config.curr_language = Config.Java &&
let annotated_signature =
Annotations.get_annotated_signature summary.Specs.attributes in
let ret_annotation, _ = annotated_signature.Annotations.ret in
Annotations.ia_is_suppress_warnings ret_annotation in
match Specs.get_summary proc_name with
| Some summary when should_suppress_warnings summary -> ()
| Some summary ->

@ -17,10 +17,10 @@ let suppress_warnings_lookup = ref None
let load_suppress_warnings_lookup () =
let default_matcher = fun _ -> false in
let matcher =
match !Inferconfig.local_config with
match !Inferconfig.suppress_warnings_annotations with
| Some f ->
(try
let m = Inferconfig.ProcMatcher.load_matcher f in
let m = Inferconfig.SuppressWarningsMatcher.load_matcher f in
(m DB.source_file_empty)
with Yojson.Json_error _ ->
default_matcher)

@ -62,10 +62,10 @@ let arg_desc =
None,
"Set the path to the javac verbose output"
;
"-local_config",
Arg.String (fun s -> Inferconfig.local_config := Some s),
"-suppress_warnings_out",
Arg.String (fun s -> Inferconfig.suppress_warnings_annotations := Some s),
Some "Path",
"Path to local config file"
"Path to list of collected @SuppressWarnings annotations"
;
] in
Arg.create_options_desc false "Parsing Options" desc

Loading…
Cancel
Save