Скачать презентацию Checker Framework Аннотации для статического анализа кода Владимир Скачать презентацию Checker Framework Аннотации для статического анализа кода Владимир

Meetup#7_Checker Framework.pptx

  • Количество слайдов: 30

Checker Framework Аннотации для статического анализа кода Владимир Смирнов Станислав Матковский 1 Checker Framework Аннотации для статического анализа кода Владимир Смирнов Станислав Матковский 1

Это тулза для статик-анализа • JDK уже разруливает кейсы типа int count = Это тулза для статик-анализа • JDK уже разруливает кейсы типа int count = "hello"; • IDE подсказывает больше, например DEAD CODE; • Find. Bugs, Check. Style и прочие утилиты для предсказания ошибок в существующем коде; • Для проверок на NPE, взаимодействия между слоями приложения или более глубоких concurrency errors требуется дополнительная метаинформация в коде. 2

Чем же хорош Checker Framework? • Недостатки типизации Java компенсируются аннотациями; • Он модульный, Чем же хорош Checker Framework? • Недостатки типизации Java компенсируются аннотациями; • Он модульный, как конструктор, и расширяем сообществом; • Есть встроенный компилятор; • Кросс-тулзовый. 3

Checker совместим с Вашим любимым сборщиком: ►Maven ►Gradle ►Ant Вашей незаменимой IDE: Intelli. J Checker совместим с Вашим любимым сборщиком: ►Maven ►Gradle ►Ant Вашей незаменимой IDE: Intelli. J IDEA◄ Eclipse◄ Net. Beans◄ 4

Встроенные анализаторы • Nullness Checker for null pointer errors • Initialization Checker to ensure Встроенные анализаторы • Nullness Checker for null pointer errors • Initialization Checker to ensure all fields are set in the constructor • Map Key Checker to track which values are keys in a map • Interning Checker for errors in equality testing and interning • Property File Checker to ensure that valid keys are used for property files and resource bundles • Internationalization Checker to ensure that code is properly internationalized • Lock Checker for concurrency and lock errors • Signature String Checker to ensure that the string representation of a type is properly used, for example in Class. for. Name • Fake Enum Checker to allow type-safe fake enum patterns and type aliases or typedefs • GUI Effect Checker to ensure that non-GUI threads do not access the UI, which would crash the application • Tainting Checker for trust and security errors • Units Checker to ensure operations are performed on correct units of measurement • Regex Checker to prevent use of syntactically invalid regular expressions • Format String Checker to ensure that format strings have the right number and type of % directives • Internationalization Format String Checker to ensure that i 18 n format strings have the right number and type of {} directives • Signedness Checker to ensure unsigned and signed values are not mixed • Constant Value Checker to determine whether an expression’s value can be known at compile time • Aliasing Checker to identify whether expressions have aliases • Linear Checker to control aliasing and prevent re-use • Subtyping Checker for customized checking without writing any code 5

Простейший пример Nullness Checker void show. Object. Safe(@Nullable Object o) { System. out. println(o. Простейший пример Nullness Checker void show. Object. Safe(@Nullable Object o) { System. out. println(o. to. String()); } void show. Object(@Nullable Object o) { show. Object. Unsafe(o); } void show. Object. Unsafe(@Non. Null Object o) { if (o != null) { System. out. println(o. to. String()); } } [ERROR] /D: /Coding/acs-server-stub/src/main/java/com/vtb/acs/Acs. Controller. java: [189, 28] [dereference. of. nullable] dereference of possibly-null reference o [ERROR] /D: /Coding/acs-server-stub/src/main/java/com/vtb/acs/Acs. Controller. java: [193, 26] [argument. type. incompatible] incompatible types in argument. [ERROR] found : @Initialized @Nullable Object [ERROR] required: @Initialized @Non. Null Object 6

Еще пример для Nullness Checker public class Base. Class { public @Non. Null Object Еще пример для Nullness Checker public class Base. Class { public @Non. Null Object nnobj; public Base. Class() { } public void main() { List<@Nullable String> l 1 = new Array. List<>(); l 1. add("test"); l 1. add(null); for (String s : l 1) show. String. Unsafe(s); List<@Non. Null String> l 2 = new Array. List<>(); l 2. add("test"); l 2. add(null); for (String s : l 2) show. String. Unsafe(s); } private void show. String. Unsafe(@Non. Null String s) { System. out. println(s. to. String()); } } 7

Я использую внешние библиотеки, там нет этих ваших аннотаций • Фреймворк поддерживает механизм внешнего Я использую внешние библиотеки, там нет этих ваших аннотаций • Фреймворк поддерживает механизм внешнего аннотирования библиотек; • Уже есть набор мета-аннотаций наиболее популярных библиотек, он расширяется и в этом даже можно поучаствовать; • Некоторые проекты распространяют уже аннотированные версии библиотек. 8

Subtyping Checker void main() { Person p = new Person( Subtyping Checker void main() { Person p = new Person("a", "b"); get. Statistics(p. get. Id(), p. get. Job. Id(), "c"); } public class Person { private String id; private String job. Id; public Person(String id, String job. Id) { this. id = id; this. job. Id = job. Id; } void get. Statistics(String person. Id, String job. Id) { // TODO } public String get. Id() { return id; } void get. Statistics(String job. Id, String person. Id, Object o) { // TODO } public String get. Job. Id() { return job. Id; } } 9

Subtyping Checker public class Person { private @Person. Guid String id; private @Job. Guid Subtyping Checker public class Person { private @Person. Guid String id; private @Job. Guid String job. Id; public Person(String id, String job. Id) { super(); // : : warning: (cast. unsafe) this. id = (@Person. Guid String) id; // : : warning: (cast. unsafe) this. job. Id = (@Job. Guid String) job. Id; } public @Person. Guid String get. Id() { return id; } public @Job. Guid String get. Job. Id() { return job. Id; } } 10

Subtyping Checker void main() { Person p = new Person( Subtyping Checker void main() { Person p = new Person("a", "b"); get. Statistics(p. get. Id(), p. get. Job. Id(), "c"); // ловим ошибку } void get. Statistics(@Person. Guid String person. Id, @Job. Guid String job. Id) { // TODO } void get. Statistics(@Job. Guid String job. Id, @Person. Guid String person. Id, String o) { // TODO } 11

Альтернативное решение без чекера public class Person { private Person. Guid id; private Job. Альтернативное решение без чекера public class Person { private Person. Guid id; private Job. Guid job. Id; public Person(String id, String job. Id) { this. id = new Person. Guid(id); this. job. Id = new Job. Guid(job. Id); } public Person. Guid get. Id() { return id; } public class Person. Guid extends Value. Holder { public Person. Guid(String value) { super(value); } } public class Job. Guid extends Value. Holder { public Job. Guid(String value) { super(value); } } public Job. Guid get. Job. Id() { return job. Id; } } 12

Альтернативное решение без чекера void main() { Person p = new Person( Альтернативное решение без чекера void main() { Person p = new Person("a", "b"); get. Statistics(p. get. Id(), p. get. Job. Id(), "c"); // Ловим ошибку } void get. Statistics(Person. Guid person. Id, Job. Guid job. Id) { // TODO } void get. Statistics(Job. Guid job. Id, Person. Guid person. Id, String o) { // TODO } 13

Fake Enum Checker @Suppress. Warnings( Fake Enum Checker @Suppress. Warnings("assignment. type. incompatible" ) public class Auth. Choice { @Fenum("Auth. Choice 1") public static final String AUTH_CHOICE_CORRECT = "CORRECT"; @Fenum("Auth. Choice 1") public static final String AUTH_CHOICE_INCORRECT = "INCORRECT"; @Fenum("Auth. Choice 2") public static final String AUTH_CHOICE_CORRECT_2 = "CORRECT 2"; @Fenum("Auth. Choice 2") public static final String AUTH_CHOICE_INCORRECT_2 = "INCORRECT 2"; } private Result generate. Result(Request auth. Request, @Fenum("Auth. Choice 1") String auth. Choice) { // Ошибка компиляции! // LOGGER. trace("Генерируется результат {}", auth. Choice); switch (auth. Choice) { case AUTH_CHOICE_CORRECT: return new Correct. Result(); case AUTH_CHOICE_INCORRECT: return new Incorrect. Result(); } return null; } 14

Interning Checker – простейший пример @Interned String foo =

Interning Checker public class Action. Type { private static final Map<String, Action. Type> actions. Interning Checker public class Action. Type { private static final Map actions. Map = new Concurrent. Hash. Map<>(); private String action; public Action. Type(String action) { this. action = action; actions. Map. put(this. action, this); System. out. println(); } public String get. Action() { return action; } @Suppress. Warnings("interning") public static @Interned Action. Type get. Value. Safe(String action. Type. Name) { public static Action. Type get. Value. Safe(String action. Type. Name) { action. Type. Name = action. Type. Name. to. Upper. Case(); Action. Type action. Type = actions. Map. get(action. Type. Name); return (action. Type == null) ? UNKNOWN : action. Type; } } } if (Action. Type. get. Value. Safe("DELETE") == // Важная логика } client. Action) { if (Action. Type. get. Value. Safe("DELETE") == new Action. Type("DELETE")) { // Важная логика } 16

Initialization Checker public abstract class Base. Controller { public class Child. Controller extends Base. Initialization Checker public abstract class Base. Controller { public class Child. Controller extends Base. Controller { private List default. Puppets; private List puppets; public Base. Controller() { default. Puppets = new Array. List<>(); List puppets = init. Puppets(); default. Puppets. add. All(puppets); } public Child. Controller() { puppets = new Array. List<>(); } @Override protected List init. Puppets() { // Инициализируем список. . . return puppets; } protected abstract List init. Puppets(); } } Child. Controller child. Controller = new Child. Controller(); [ERROR] /D: /Coding/acs-server-stub/src/main/java/org/test/Base. Controller. java: [13, 43] [method. invocation. invalid] call to init. Puppets() not allowed on the given receiver. [ERROR] found : @Under. Initialization(org. test. Base. Controller. class) @Non. Null Base. Controller [ERROR] required: @Initialized @Non. Null Base. Controller [ERROR] -> [Help 1] 17

Lock Checker private final Reentrant. Lock requests. Map. Lock = new Reentrant. Lock(); @Guarded. Lock Checker private final Reentrant. Lock requests. Map. Lock = new Reentrant. Lock(); @Guarded. By("requests. Map. Lock") protected Map requests = new Hash. Map<>(); @May. Release. Locks public Response. Entity> start. Request(String term. Url, String md, String pa. Req) { Auth. Request auth. Request = new Auth. Request(validate. Term. Url(term. Url), md, pa. Req); Map succeeded. Requests; requests. Map. Lock. lock(); try { succeeded. Requests = get. Succeeded. Requests(); } finally { requests. Map. Lock. unlock(); } return Response. Entity. ok(succeeded. Requests); } @Holding("requests. Map. Lock") protected Map get. Succeeded. Requests() { return requests. entry. Set(). stream(). filter(e -> e. get. Value() != null). collect(Collectors. to. Map(e -> e. get. Key(), e -> e. get. Value())); } 18

Tainting Checker public class Business. Object { private String sensitive. Data; public Request(String sensitive. Tainting Checker public class Business. Object { private String sensitive. Data; public Request(String sensitive. Data) { this. sensitive. Data = sensitive. Data; } public String get. Data() { return sensitive. Data; } } public void execute. Business. Logic(String data) { data = validate. Data(data); Business. Object obj = new Business. Object(data); } private String validate. Data(String data) { // Логика валидации. . . return pure. Data; } 19

Tainting Checker public class Business. Object { private String sensitive. Data; public Request(@Untainted String Tainting Checker public class Business. Object { private String sensitive. Data; public Request(@Untainted String sensitive. Data) { this. sensitive. Data = sensitive. Data; } public String get. Data() { return sensitive. Data; } } public void execute. Business. Logic(@Tainted String data) { data = validate. Data(data); Business. Object obj = new Business. Object(data); } @Suppress. Warnings("tainting") private @Untainted String validate. Data(@Tainted String data) { // Логика валидации. . . return pure. Data; } 20

Regex Checker Pattern. compile( Regex Checker Pattern. compile(". *"); // Ловит IDE, если что не так Pattern. compile(or(parenthesize("a*"), parenthesize("b*"))); // IDE уже не справится public @Regex parenthesize(String regex) { String parenthesize(@Regex String regex) { return "(" + regex + ")"; } public @Regex or(String a, String b) {a, @Regex String b) { String or(@Regex String return a + "|" + b; } 21

Internationalization & Format String Checkers System. out. printf( Internationalization & Format String Checkers System. out. printf("Float %f, number %g", 3. 1415, 42); Поддерживает также работу с Resource. Bundle void print. Float. And. Int(@Format({FLOAT, INT}) String fs) { System. out. printf(fs, 3. 1415, 42); } print. Float. And. Int("Float %f, Number %d"); print. Float. And. Int("Float %f"); // OK // Ошибка // Нет второго аргумента Message. Format. format("{0} {1}", 3. 1415); // Аргумент нельзя отформатировать как «время» Message. Format. format("{0, time}", "my string"); @I 18 n. Format({GENERAL, NUMBER}) String format; format = "{0} {1} {2}"; 22

Map Key Checker private void process. Key(String ext. Key) { Map<String, @Non. Null Object> Map Key Checker private void process. Key(String ext. Key) { Map map = new Hash. Map<>(); Collection<@Key. For("map") String> coll = new Array. List<>(); map. put(ext. Key, new Request()); // Некоторое время спустя coll. add(ext. Key); // Еще позже for (String s : coll) { show. Object. Unsafe(map. get(s)); } } private void show. Object. Unsafe(@Non. Null Object o) { System. out. println(o. to. String()); } 23

Units Checker @m int meters = 5 * Units. Tools. m; @s int secs Units Checker @m int meters = 5 * Units. Tools. m; @s int secs = 2 * Units. Tools. s; @m. PERs double speed = meters / secs; @km. PERh double kmph = speed * 3. 6; @km. PERh double kmph = Units. Tools. from. Meter. Per. Second. To. Kilo. Meter. Per. Hour(speed); @km. PERh public static double from. Meter. Per. Second. To. Kilo. Meter. Per. Hour( @m. PERs double mps) { return mps * 3. 6 D; } 24

GUI Effect Checker @Safe. Effect public void called. From. Background. Thread() { j. Label. GUI Effect Checker @Safe. Effect public void called. From. Background. Thread() { j. Label. set. Text("Foo"); // Ошибка } @UIEffect private void called. From. UIThread() { heavy. Load(); } @Safe. Effect private void heavy. Load() { // Some really heavy load. . . } 25

Aliasing Checker void test. Planet(Earth earth) { test. Planet(@Unique Earth new. Planet = earth; Aliasing Checker void test. Planet(Earth earth) { test. Planet(@Unique Earth new. Planet = earth; // Какая-то логика. . . new. Planet. annihilate(); } Метод test. Planet никак не должен менять earth! [ERROR] Reference annotated as @Unique is leaked. 26

Чекеры от сторонних разработчиков Они есть, да. 27 Чекеры от сторонних разработчиков Они есть, да. 27

Я не использую Java 8 • Компилятор фреймворка обработает и List</*@Non. Null*/ String> 28 Я не использую Java 8 • Компилятор фреймворка обработает и List 28

У меня куча легаси без этих ваших аннотаций! • Начинаем помаленьку, отдельные чекеры, отдельные У меня куча легаси без этих ваших аннотаций! • Начинаем помаленьку, отдельные чекеры, отдельные ветви проекта; • Думайте об аннотации как о части спецификации – обычно достаточно понимать и аннотировать сигнатуру метода, а не тело. В том же Javadoc хорошие ребята так же помечают, что например метод может вернуть null; • Старайтесь сделать свой код лучше, как например использование параметризированных типов вместо raw types. 29

Спасибо за внимание! 30 Спасибо за внимание! 30