如何故意导致自定义 java 编译器警告消息?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1752607/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 22:18:49  来源:igfitidea点击:

How to intentionally cause a custom java compiler warning message?

javacompiler-warningsjavac

提问by pimlottc

I'm about to commit an ugly temporary hack in order to work around a blocking issue while we wait for an external resource to be fixed. Aside from marking it with a big scary comment and a bunch of FIXMEs, I'd love to have the compiler throw an obvious warning message as a reminder so we don't forget to take this out. For example, something like:

我即将提交一个丑陋的临时黑客,以便在我们等待修复外部资源时解决阻塞问题。除了用可怕的大注释和一堆 FIXME 标记它之外,我希望编译器抛出一个明显的警告消息作为提醒,这样我们就不会忘记将其删除。例如,类似于:

[javac] com.foo.Hacky.java:192: warning: FIXME temporary hack to work around library bug, remove me when library is fixed!

Is there a way I can cause an intentional compiler warning with a message of my choosing? Failing that, what's the easiest thing to add to the code to throw an existing warning, with perhaps a message in a string on the offending line so it gets printed in the warning message?

有没有办法用我选择的消息引起有意的编译器警告?否则,添加到代码中以抛出现有警告的最简单的方法是什么,可能在违规行的字符串中包含一条消息,以便将其打印在警告消息中?

EDIT: Deprecated tags don't seem to be doing anything for me:

编辑:已弃用的标签似乎对我没有任何作用:

/**
 * @deprecated "Temporary hack to work around remote server quirks"
 */
@Deprecated
private void doSomeHackyStuff() { ... }

No compiler or runtime errors in eclipse or from sun javac 1.6 (running from ant script), and it's definitely executing the function.

eclipse 或 sun javac 1.6(从 ant 脚本运行)中没有编译器或运行时错误,并且它肯定在执行该函数。

采纳答案by Kevin Day

One technique that I've seen used is to tie this into unit testing (you dounit test, right?). Basically you create a unit test that failsonce the external resource fix is achieved. Then you comment that unit test to tell others how to undo your gnarly hack once the issue is resolved.

我见过的一种技术是将其与单元测试联系起来(您进行单元测试,对吗?)。基本上,您创建了一个单元测试,一旦实现外部资源修复,该单元测试就会失败。然后,您对该单元测试进行评论,以告诉其他人一旦问题得到解决,如何撤消您的粗暴攻击。

What's really slick about this approach is that the trigger for undoing your hack is a fix of the core issue itself.

这种方法的真正巧妙之处在于,撤销黑客行为的触发器是解决核心问题本身。

回答by Peter Recore

how about marking the method or class as @Deprecated? docs here. Note that there is both a @Deprecated and a @deprecated - the capital D version is the annotation and the lowercase d is the javadoc version. The javadoc version allows you to specify an arbitrary string explaining what is going on. But compilers are not required to emit a warning when seeing it (though many do). The annotation should always cause a warning, though i don't think you can add an explanation to it.

将方法或类标记为@Deprecated 怎么样?文档在这里。请注意,同时存在@Deprecated 和@deprecated - 大写的 D 版本是注释,小写的 d 是 javadoc 版本。javadoc 版本允许您指定一个任意字符串来解释正在发生的事情。但是编译器在看到它时不需要发出警告(尽管很多都这样做)。注释应始终引起警告,但我认为您无法为其添加解释。

UPDATE here is the code I just tested with: Sample.java contains:

更新这里是我刚刚测试的代码:Sample.java 包含:

public class Sample {
    @Deprecated
    public static void foo() {
         System.out.println("I am a hack");
    }
}

SampleCaller.java contains:

SampleCaller.java 包含:

public class SampleCaller{
     public static void main(String [] args) {
         Sample.foo();
     }
}

when i run "javac Sample.java SampleCaller.java" i get the following output:

当我运行“javac Sample.java SampleCaller.java”时,我得到以下输出:

Note: SampleCaller.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.

I am using sun's javac 1.6. If you want an honest to goodness warning rather than just a note, use the -Xlint option. Maybe that will percolate up through Ant properly.

我正在使用 sun 的 javac 1.6。如果您想要一个诚实的善良警告而不仅仅是一个注释,请使用 -Xlint 选项。也许这会正确地通过 Ant 渗透。

回答by Lioda

You should use a tool to compile, like ant ou maven. With it, you should define some tasks at compile time which could produce some logs (like messages or warnings) about your FIXME tags, for example.

你应该使用一个工具来编译,比如ant ou maven。有了它,您应该在编译时定义一些任务,这些任务可能会产生一些关于您的 FIXME 标签的日志(如消息或警告),例如。

And if you want some errors, it is possible too. Like stop compilation when you have left some TODO in your code (why not ?)

如果你想要一些错误,那也是可能的。就像在代码中留下一些 TODO 时停止编译一样(为什么不呢?)

回答by Matt Phillips

Hereshows a tutorial on annotations and at the bottom it gives an example of defining your own annotations. Unfortunately a quick skimming of the tutorial said that those are only available in the javadoc...

这里展示了一个关于注解的教程,在底部给出了一个定义你自己的注解的例子。不幸的是,快速浏览本教程说这些仅在 javadoc 中可用...

Annotations Used by the Compiler There are three annotation types that are predefined by the language specification itself: @Deprecated, @Override, and @SuppressWarnings.

编译器使用的注释 语言规范本身预定义了三种注释类型:@Deprecated、@Override 和 @SuppressWarnings。

So it appears that all you can really do is throw in an @Deprecated tag that the compiler will print out or put a custom tag in the javadocs that tells about the hack.

所以看起来你真正能做的就是抛出一个@Deprecated 标签,编译器会打印出来,或者在 javadocs 中放置一个自定义标签来说明黑客行为。

回答by barjak

I think that a custom annotation, which will be processed by the compiler, is the solution. I frequently write custom annotations to do things at runtime, but I never tried to use them at compilation time. So, I can only give you pointers on the tools you may need :

我认为将由编译器处理的自定义注释是解决方案。我经常编写自定义注释来在运行时做一些事情,但我从未尝试在编译时使用它们。所以,我只能给你一些你可能需要的工具的提示:

  • Write a custom annotation type. This pageexplains how to write an annotation.
  • Write an annotation processor, that processes your custom annotation to emit a warning. The tool that runs such annotation processors is called APT. You can find an indroduction on this page. I think what you need in the APT API is AnnotationProcessorEnvironment, which will let you emit warnings.
  • From Java 6, APT is integrated into javac. That is, you can add an annotation processor in the javac command line. This sectionof the javac manual will tell you how to call your custom annotation processor.
  • 编写自定义注释类型。本页介绍了如何编写注释。
  • 编写一个注释处理器,它处理您的自定义注释以发出警告。运行此类注释处理器的工具称为 APT。您可以在此页面上找到介绍。我认为您在 APT API 中需要的是 AnnotationProcessorEnvironment,它可以让您发出警告。
  • 从 Java 6 开始,APT 被集成到 javac 中。也就是说,您可以在 javac 命令行中添加注释处理器。javac 手册的这一部分将告诉您如何调用您的自定义注解处理器。

I don't know if this solution is really practicable. I'll try to implement it myself when I find some time.

我不知道这个解决方案是否真的可行。当我找到一些时间时,我会尝试自己实现它。

Edit

编辑

I successfully implemented my solution. And as a bonus, I used java's service provider facility to simplify its use. Actually, my solution is a jar that contains 2 classes : the custom annotation and the annotation processor. To use it, just add this jar in the classpath of your project, and annotate whatever you want ! This is working fine right inside my IDE (NetBeans).

我成功地实施了我的解决方案。作为奖励,我使用了 java 的服务提供者工具来简化它的使用。实际上,我的解决方案是一个包含 2 个类的 jar:自定义注释和注释处理器。要使用它,只需将此 jar 添加到您项目的类路径中,并注释您想要的任何内容!这在我的 IDE (NetBeans) 中运行良好。

Code of the annotation :

注释代码:

package fr.barjak.hack;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.METHOD, ElementType.PACKAGE, ElementType.PARAMETER, ElementType.TYPE})
public @interface Hack {

}

Code of the processor :

处理器代码:

package fr.barjak.hack_processor;

import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic.Kind;

@SupportedAnnotationTypes("fr.barjak.hack.Hack")
public class Processor extends AbstractProcessor {

    private ProcessingEnvironment env;

    @Override
    public synchronized void init(ProcessingEnvironment pe) {
        this.env = pe;
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        if (!roundEnv.processingOver()) {
            for (TypeElement te : annotations) {
                final Set< ? extends Element> elts = roundEnv.getElementsAnnotatedWith(te);
                for (Element elt : elts) {
                    env.getMessager().printMessage(Kind.WARNING,
                            String.format("%s : thou shalt not hack %s", roundEnv.getRootElements(), elt),
                            elt);
                }
            }
        }
        return true;
    }

}

To enable the resulting jar as a service provider, add the file META-INF/services/javax.annotation.processing.Processorin the jar. This file is an acsii file that must contain the following text :

要将生成的 jar 作为服务提供程序启用,请在 jar 中添加文件META-INF/services/javax.annotation.processing.Processor。此文件是 acsii 文件,必须包含以下文本:

fr.barjak.hack_processor.Processor

回答by WReach

One good hack deserves another... I usually generate compiler warnings for the described purpose by introducing an unused variable in the hacky method, thus:

一个好的 hack 值得另一个......我通常通过在 hacky 方法中引入一个未使用的变量来为所描述的目的生成编译器警告,因此:

/**
 * @deprecated "Temporary hack to work around remote server quirks"
 */
@Deprecated
private void doSomeHackyStuff() {
    int FIXMEtemporaryHackToWorkAroundLibraryBugRemoveMeWhenLibraryIsFixed;
    ...
}

This unused variable will generate a warning which (depending upon your compiler) will look something like this:

这个未使用的变量会产生一个警告(取决于你的编译器)看起来像这样:

WARNING: The local variable FIXMEtemporaryHackToWorkAroundLibraryBugRemoveMeWhenLibraryIsFixed is never read.

This solution is not as nice as a custom annotation, but it has the advantage that it requires no advance preparation (assuming the compiler is already configured to issue warnings for unused variables). I would suggest that this approach is only suitable for short-lived hacks. For long-lived hacks, I would argue that effort to create a custom annotation would be justified.

这个解决方案不如自定义注解好,但它的优点是不需要提前准备(假设编译器已经配置为对未使用的变量发出警告)。我建议这种方法只适用于短期黑客。对于长期存在的黑客,我认为创建自定义注释的努力是合理的。

回答by Artem Zinnatullin

I wrote a library that does this with annotations: Lightweight Javac @Warning Annotation

我写了一个带有注释的库:Lightweight Javac @Warning Annotation

Usage is very simple:

用法很简单:

// some code...

@Warning("This method should be refactored")
public void someCodeWhichYouNeedAtTheMomentButYouWantToRefactorItLater() {
    // bad stuff going on here...
}

And compiler will throw warning message with your text

并且编译器会在您的文本中抛出警告消息

回答by Luchostein

Some quick and not so dirty approach, may be to use a @SuppressWarningsannotation with a deliberately wrong Stringargument:

一些快速而不那么肮脏的方法可能是使用@SuppressWarnings带有故意错误String参数的注释:

@SuppressWarnings("FIXME: this is a hack and should be fixed.")

This will generate a warning because it is not recognized by the compiler as a specific warning to suppress:

这将生成警告,因为编译器不会将其识别为要抑制的特定警告:

Unsupported @SuppressWarnings("FIXME: this is a hack and should be fixed.")

不受支持的@SuppressWarnings("FIXME:这是一个黑客行为,应该被修复。")

回答by Radiodef

We can do this with annotations!

我们可以通过注释来做到这一点!

To raise an error, use Messagerto send a message with Diagnostic.Kind.ERROR. Short example:

要引发错误,请使用Messager发送带有 的消息Diagnostic.Kind.ERROR。简短示例:

processingEnv.getMessager().printMessage(
    Diagnostic.Kind.ERROR, "Something happened!", element);

Here's a fairly simple annotation I wrote just to test this out.

这是我编写的一个相当简单的注释,只是为了测试这一点。

This @Markerannotation indicates the target is a marker interface:

这个@Marker注解表明目标是一个标记接口:

package marker;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Marker {
}

And the annotation processor causes an error if it's not:

如果不是,则注释处理器会导致错误:

package marker;

import javax.annotation.processing.*;
import javax.lang.model.*;
import javax.lang.model.element.*;
import javax.lang.model.type.*;
import javax.lang.model.util.*;
import javax.tools.Diagnostic;
import java.util.Set;

@SupportedAnnotationTypes("marker.Marker")
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public final class MarkerProcessor extends AbstractProcessor {

    private void causeError(String message, Element e) {
        processingEnv.getMessager()
            .printMessage(Diagnostic.Kind.ERROR, message, e);
    }

    private void causeError(
            Element subtype, Element supertype, Element method) {
        String message;
        if (subtype == supertype) {
            message = String.format(
                "@Marker target %s declares a method %s",
                subtype, method);
        } else {
            message = String.format(
                "@Marker target %s has a superinterface " +
                "%s which declares a method %s",
                subtype, supertype, method);
        }

        causeError(message, subtype);
    }

    @Override
    public boolean process(
            Set<? extends TypeElement> annotations,
            RoundEnvironment roundEnv) {

        Elements elementUtils = processingEnv.getElementUtils();
        boolean processMarker = annotations.contains(
            elementUtils.getTypeElement(Marker.class.getName()));
        if (!processMarker)
            return false;

        for (Element e : roundEnv.getElementsAnnotatedWith(Marker.class)) {
            ElementKind kind = e.getKind();

            if (kind != ElementKind.INTERFACE) {
                causeError(String.format(
                    "target of @Marker %s is not an interface", e), e);
                continue;
            }

            if (kind == ElementKind.ANNOTATION_TYPE) {
                causeError(String.format(
                    "target of @Marker %s is an annotation", e), e);
                continue;
            }

            ensureNoMethodsDeclared(e, e);
        }

        return true;
    }

    private void ensureNoMethodsDeclared(
            Element subtype, Element supertype) {
        TypeElement type = (TypeElement) supertype;

        for (Element member : type.getEnclosedElements()) {
            if (member.getKind() != ElementKind.METHOD)
                continue;
            if (member.getModifiers().contains(Modifier.STATIC))
                continue;
            causeError(subtype, supertype, member);
        }

        Types typeUtils = processingEnv.getTypeUtils();
        for (TypeMirror face : type.getInterfaces()) {
            ensureNoMethodsDeclared(subtype, typeUtils.asElement(face));
        }
    }
}

For example, these are correct uses of @Marker:

例如,这些是的正确用法@Marker

  • @Marker
    interface Example {}
    
  • @Marker
    interface Example extends Serializable {}
    
  • @Marker
    interface Example {}
    
  • @Marker
    interface Example extends Serializable {}
    

But these uses of @Markerwill cause a compiler error:

但是这些使用@Marker会导致编译器错误:

  • @Marker
    class Example {}
    
  • @Marker
    interface Example {
        void method();
    }
    

    marker error

  • @Marker
    class Example {}
    
  • @Marker
    interface Example {
        void method();
    }
    

    标记错误

Here's a blog post I found very helpful getting started on the subject:

这是一篇博客文章,我发现它对开始讨论这个主题非常有帮助:



Small note: what the commentor below is pointing out is that because MarkerProcessorreferences Marker.class, it has a compile-time dependency on it. I wrote the above example with the assumption that both classes would go in the same JAR file (say, marker.jar), but that's not always possible.

小提示:下面的评论者指出的是,因为MarkerProcessorreferences Marker.class,它对它有编译时依赖。我写了上面的例子,假设两个类都放在同一个 JAR 文件中(比如,marker.jar),但这并不总是可能的。

For example, suppose there's an application JAR with the following classes:

例如,假设有一个具有以下类的应用程序 JAR:

com.acme.app.Main
com.acme.app.@Ann
com.acme.app.AnnotatedTypeA (uses @Ann)
com.acme.app.AnnotatedTypeB (uses @Ann)

Then the processor for @Annexists in a separate JAR, which is used while compiling the application JAR:

然后处理器@Ann存在于一个单独的 JAR 中,在编译应用程序 JAR 时使用:

com.acme.proc.AnnProcessor (processes @Ann)

In that case, AnnProcessorwould not be able to reference the type of @Anndirectly, because it would create a circular JAR dependency. It would only be able to reference @Annby Stringname or TypeElement/TypeMirror.

在这种情况下,AnnProcessor将无法@Ann直接引用类型,因为它会创建循环 JAR 依赖项。它只能@Ann通过String名称或TypeElement/进行引用TypeMirror

回答by Andy Balaam

To get any warning at all to appear, I found that unused variables and custom @SuppressWarnings didn't work for me, but an unnecessary cast did:

为了让任何警告出现,我发现未使用的变量和自定义 @SuppressWarnings 对我不起作用,但不必要的强制转换做了:

public class Example {
    public void warn() {
        String fixmePlease = (String)"Hello";
    }
}

Now when I compile:

现在当我编译时:

$ javac -Xlint:all Example.java
ExampleTest.java:12: warning: [cast] redundant cast to String
        String s = (String) "Hello!";
                   ^
1 warning