Annotations in Java

💡 原文中文,约11700字,阅读约需28分钟。
📝

内容提要

本文介绍Java中注解的CodeQL查询方法。CodeQL用Annotatable、AnnotationType、AnnotationElement和Annotation类表示注解,可通过getValue获取值。示例包括查找@SuppressWarnings注解、缺失的@Override注解,以及调用废弃方法(排除被@SuppressWarnings("deprecation")抑制的情况),并展示如何定义CodeQL类封装注解概念以简化查询。

🔎

延伸解读

CodeQL 注解查询的核心类

CodeQL 将 Java 注解建模为四个核心类:Annotatable(可注解实体)、AnnotationType(注解类型)、AnnotationElement(注解元素)和 Annotation(具体注解)。理解这些类的层次关系是编写注解相关查询的基础。例如,通过 getAnAnnotation() 获取实体上的注解,通过 getType() 获取注解类型,通过 getValue() 获取注解元素的值。掌握这些 API 能帮助您快速定位特定注解及其参数。

封装注解概念简化查询

文章展示了如何将注解概念封装为 CodeQL 类,例如定义 OverrideAnnotation 类来代表 @Override 注解。这种封装使查询逻辑更清晰、更易复用。例如,在查找缺失 @Override 的方法时,只需使用 instanceof OverrideAnnotation 即可,无需重复编写类型判断。建议在复杂查询中采用类似方法,提高代码可读性和维护性。

处理注解值的匹配细节

在查找 @SuppressWarnings("deprecation") 时,文章使用正则表达式 ".*deprecation.*" 匹配注解值。但实际中需注意:注解值可能包含多个警告类别,且 "all" 值会抑制所有警告。文章建议使用 \bdeprecation\b 进行单词边界匹配,避免误匹配。此外,还需考虑注解值可能不是字符串字面量的情况,需进行类型转换和空值检查。

Q&A

CodeQL中如何表示Java注解?

CodeQL使用四个类来表示Java注解:Annotatable(表示可能带有注解的实体,如包、类型、字段、方法、局部变量)、AnnotationType(表示注解类型,如java.lang.Override)、AnnotationElement(表示注解类型的成员)和Annotation(表示具体的注解,如@SuppressWarnings)。注解值可以通过Annotation类的getValue谓词访问。

如何用CodeQL查找所有@SuppressWarnings注解及其值?

可以编写如下查询:import java; from Constructor c, Annotation ann, AnnotationType anntp where ann = c.getAnAnnotation() and anntp = ann.getType() and anntp.hasQualifiedName("java.lang", "SuppressWarnings") select ann, ann.getValue("value")。该查询会返回所有构造函数上的@SuppressWarnings注解及其value元素的值。

如何用CodeQL查找缺少@Override注解的方法?

首先定义OverrideAnnotation类(继承Annotation,构造器中检查类型为java.lang.Override),然后查询:from Method overriding, Method overridden where overriding.overrides(overridden) and not overriding.getAnAnnotation() instanceof OverrideAnnotation select overriding, "Method overrides another method, but does not have an @Override annotation."。为了排除库代码,可添加overriding.fromSource()条件。

如何用CodeQL查找对废弃方法的调用?

定义DeprecatedAnnotation和DeprecatedMethod类,然后查询:from Call call where call.getCallee() instanceof DeprecatedMethod and not call.getCaller() instanceof DeprecatedMethod select call, "This call invokes a deprecated method."。该查询会排除废弃方法内部的调用。

如何改进废弃方法调用查询以忽略@SuppressWarnings("deprecation")?

定义SuppressDeprecationWarningAnnotation类,检查注解类型为java.lang.SuppressWarnings且值包含"deprecation"(使用getAValue和正则表达式)。然后在查询中添加条件:not call.getCaller().getAnAnnotation() instanceof SuppressDeprecationWarningAnnotation。

CodeQL中如何获取注解的值?

可以使用Annotation类的getValue谓词获取指定元素的值,例如ann.getValue("value")。也可以使用getAValue()获取所有值,然后进一步处理,如检查是否为字面量并获取其字符串值。

🏷️

标签

➡️

继续阅读