Jak celowo wywołać niestandardowy komunikat ostrzegawczy kompilatora Javy?

Mam zamiar popełnić brzydki tymczasowy hack, Aby obejść problem blokowania, podczas gdy czekamy na zewnętrzny zasób, który zostanie naprawiony. Poza oznaczeniem go wielkim strasznym komentarzem i kilkoma poprawkami, chciałbym, aby kompilator rzucił oczywisty komunikat ostrzegawczy jako przypomnienie, abyśmy nie zapomnieli tego wyjąć. Na przykład coś w stylu:

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

Czy istnieje sposób, w jaki mogę spowodować celowe Ostrzeżenie kompilatora z wybranym przeze mnie Komunikatem? W przeciwnym razie, co jest najprostszą rzeczą, aby dodać do kodu, aby rzucić istniejące ostrzeżenie, z Może wiadomość w ciągu znaków na linii obrażenia, więc zostanie wydrukowany w komunikacie ostrzegawczym?

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

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

Brak kompilatora lub błędów runtime w eclipse lub z sun javac 1.6 (uruchomiony ze skryptu ant), i na pewno wykonuje tę funkcję.

Author: pimlottc, 2009-11-18

11 answers

Jedną z technik, jaką widziałem, jest powiązanie tego z testami jednostkowymi (ty robisz test jednostkowy, prawda?). Zasadniczo tworzysz test jednostkowy, który nie powiedzie się Po osiągnięciu poprawki zasobów zewnętrznych. Następnie komentujesz ten test jednostkowy, aby powiedzieć innym, jak cofnąć gnarly hack, gdy problem zostanie rozwiązany.

Co jest naprawdę slick w tym podejściu jest to, że wyzwalacz do cofania hack jest naprawa samego problemu podstawowego.

 32
Author: Kevin Day,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-18 02:44:17

Myślę, że rozwiązaniem jest niestandardowa adnotacja, która będzie przetwarzana przez kompilator. Często piszę niestandardowe adnotacje, aby robić rzeczy w czasie wykonywania, ale nigdy nie próbowałem ich używać w czasie kompilacji. Więc mogę tylko dać wskazówki na temat narzędzi, które mogą być potrzebne:

  • napisz własny typ adnotacji. ta strona wyjaśnia, jak napisać adnotację.
  • napisz procesor adnotacji, który przetwarza Twoją niestandardową adnotację w celu wysłania Ostrzeżenia. Narzędzie, które działa takie procesory adnotacji nazywane są APT. Możesz znaleźć indrodukcję na tej stronie . Myślę, że to, czego potrzebujesz w Apt API to AnnotationProcessorEnvironment, który pozwoli Ci emitować Ostrzeżenia.
  • z Javy 6, APT jest zintegrowany z javac. Oznacza to, że możesz dodać procesor adnotacji w wierszu poleceń javac. Ta sekcja podręcznika javac powie Ci, jak wywołać własny procesor adnotacji.

Nie wiem czy to rozwiązanie jest naprawdę / align = "left" / Postaram się to wdrożyć sam, kiedy znajdę trochę czasu.

Edit

Udało mi się wdrożyć moje rozwiązanie. Jako bonus skorzystałem z usługi java Service provider facility, aby uprościć jej użytkowanie. Właściwie moim rozwiązaniem jest jar, który zawiera 2 klasy: niestandardową adnotację i procesor adnotacji. Aby go użyć, po prostu dodaj ten jar w ścieżce klasowej swojego projektu i Adnotuj, co chcesz ! To działa dobrze w moim IDE (NetBeans).

Kod o adnotacji:

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 {

}

Kod procesora:

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;
    }

}

Aby włączyć wynikowy jar jako dostawcę usług, Dodaj plik META-INF/services/javax.annotation.processing.Processor do jar. Ten plik jest plikiem acsii, który musi zawierać następujący tekst:

fr.barjak.hack_processor.Processor
 78
Author: barjak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-02 13:14:38

Jeden dobry hack zasługuje na drugiego... Zwykle generuję ostrzeżenia kompilatora dla opisanego celu wprowadzając do metody hacky nieużywaną zmienną, w ten sposób: {[0]}

Ta nieużywana zmienna wygeneruje Ostrzeżenie, które (w zależności od kompilatora) będzie wyglądało mniej więcej tak:

WARNING: The local variable FIXMEtemporaryHackToWorkAroundLibraryBugRemoveMeWhenLibraryIsFixed is never read.

To rozwiązanie nie jest tak miłe jak niestandardowa adnotacja, ale ma tę zaletę, że nie wymaga wcześniejszego przygotowania (zakładając, że kompilator jest już skonfigurowany do wydawania ostrzeżeń dla nieużywane zmienne). Sugerowałbym, że takie podejście jest odpowiednie tylko dla krótkotrwałych hacków. W przypadku długotrwałych hacków argumentowałbym, że wysiłek stworzenia niestandardowej adnotacji byłby uzasadniony.

 12
Author: WReach,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-21 17:43:33

Pewnym szybkim i nie tak brudnym podejściem może być użycie @SuppressWarnings adnotacji z celowo błędnym argumentem String:

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

Wygeneruje Ostrzeżenie, ponieważ nie jest rozpoznawane przez kompilator jako specjalne ostrzeżenie do powstrzymania:

Unsupported @SuppressWarnings ("FIXME: to jest hack i powinno być naprawione.")

 12
Author: Luchostein,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-20 21:16:34

Napisałem bibliotekę, która robi to z adnotacjami: Lightweight Javac @ Warning Annotation

Użycie jest bardzo proste:

// some code...

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

I kompilator wyrzuci komunikat ostrzegawczy z Twoim tekstem

 8
Author: Artem Zinnatullin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-10 19:18:43

Może oznaczyć metodę lub klasę jako @ Deprecated? dokumenty tutaj . Zauważ, że istnieje zarówno @ Deprecated, jak i @deprecated - wersja z dużymi literami D to adnotacja, a mała litera d to wersja javadoc. Wersja javadoc pozwala na określenie dowolnego ciągu wyjaśniającego, co się dzieje. Ale Kompilatory nie muszą emitować ostrzeżenia, gdy je zobaczą (choć wielu tak robi). Adnotacja powinna zawsze powodować Ostrzeżenie, choć nie sądzę, że można dodać wyjaśnienie do to.

UPDATE oto kod, z którym właśnie testowałem: Próbka.java zawiera:

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

SampleCaller.java zawiera:

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

Kiedy uruchamiam " javac Sample.java SampleCaller.java " otrzymuję następujące wyjście:

Note: SampleCaller.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
Używam javac 1.6 firmy sun. Jeśli chcesz Ostrzeżenia szczerego dla dobroci, a nie tylko notatki, użyj opcji-Xlint. Może to przeczyści się przez mrówkę.
 5
Author: Peter Recore,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-18 03:55:50

Możemy to zrobić z adnotacjami!

Aby zgłosić błąd, użyj Messager aby wysłać wiadomość z Diagnostic.Kind.ERROR. Krótki przykład:

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

Oto dość prosta adnotacja, którą napisałem, aby to przetestować.

Ta @Marker adnotacja wskazuje, że celem jest interfejs znacznika:

package marker;

import java.lang.annotation.*;

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

I procesor adnotacji powoduje błąd, jeśli nie jest:

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));
        }
    }
}

Na przykład, są to poprawne zastosowania @Marker:

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

Ale te zastosowania @Marker spowodują błąd kompilatora:

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

    błąd znacznika

Oto wpis na blogu, który uważam za bardzo pomocny w rozpoczęciu pracy w tym temacie:]}

Mała uwaga: Co komentator poniżej wskazuje, że ponieważ MarkerProcessor Marker.class, jest od niego zależna w czasie kompilacji. Napisałem powyższy przykład z założeniem, że obie klasy trafią do tego samego pliku JAR (powiedzmy marker.jar), ale nie zawsze jest to możliwe.

Na przykład, załóżmy, że istnieje JAR aplikacji z następującymi klasami:

com.acme.app.Main
com.acme.app.@Ann
com.acme.app.AnnotatedTypeA (uses @Ann)
com.acme.app.AnnotatedTypeB (uses @Ann)
Procesor dla @Ann istnieje w oddzielnym JAR, który jest używany podczas kompilacji jar aplikacji:
com.acme.proc.AnnProcessor (processes @Ann)

W takim przypadku, AnnProcessor nie będzie w stanie odwołać się do typu @Ann bezpośrednio, ponieważ tworzyłoby to zależność od Jara kołowego. Będzie mógł odwoływać się tylko do @Ann przez String nazwę lub TypeElement/TypeMirror.

 3
Author: Radiodef,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-06 00:06:20

Tutaj pokazuje samouczek na temat adnotacji, a na dole daje przykład definiowania własnych adnotacji. Niestety szybki przegląd tutoriala stwierdził, że są one dostępne tylko w javadoc...

Adnotacje używane przez kompilator istnieją trzy typy adnotacji które są predefiniowane przez samą specyfikację języka: @ Deprecated, @Override i @Supresswarnings.

Więc wygląda na to, że wszystko, co naprawdę można zrobić, to dorzucić @ Deprecated tag, który kompilator wydrukuje lub umieści niestandardowy tag w javadocs, który mówi o włamaniu.

 2
Author: Matt Phillips,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-21 11:17:08

Powinieneś użyć narzędzia do kompilacji, takiego jak ant ou maven. Z nim, należy zdefiniować niektóre zadania w czasie kompilacji, które mogą produkować niektóre dzienniki (jak wiadomości lub ostrzeżenia) na temat znaczników FIXME, na przykład.

A jeśli chcesz mieć jakieś błędy, to też jest to możliwe. Jak stop kompilacji, gdy zostawiłeś trochę TODO w kodzie (dlaczego nie ?)

 0
Author: Lioda,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-17 23:46:50

Aby w ogóle pojawiło się Ostrzeżenie, odkryłem, że nieużywane zmienne i niestandardowe @ SuppressWarnings nie działają dla mnie, ale niepotrzebna Obsada TAK:

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

Teraz kiedy kompiluję:

$ javac -Xlint:all Example.java
ExampleTest.java:12: warning: [cast] redundant cast to String
        String s = (String) "Hello!";
                   ^
1 warning
 0
Author: Andy Balaam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-23 14:45:59

Jeśli używasz IntelliJ. Możesz przejść do: Preferencje > edytor > TODO i dodać " \ bhack.b* " lub jakikolwiek inny wzór.

If you then make a comment like // HACK: temporary fix to work around server issues

Następnie w oknie TODO tool pojawi się ładnie, wraz ze wszystkimi innymi zdefiniowanymi wzorami podczas edycji.

 0
Author: BARJ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-27 09:08:14