ideas for org.jetbrains.annotations.* candidates?

@Nullable and @NotNull
are informative and thanks to the associated inspections and intentions,
really useful. They help us make our code clearer and more solid. This
makes me wish for more of the same vein, hence this thread. Suggestions
are welcome.

(The motivation for placing those new annotations in
org.jetbrains.annotations is to ease distribution. Additionally, it
would also provide some new material to Dave - the Inspection Machine - G.)


Rules:
rule 1: should not depend on external libraries (except for junit),
rule 2: are only to be used by editor (IDEA, Eclipse, ..) inspections,
intentions
and info popups (=> no bytecode modif, aspect weaving, etc..)
=> bad candidates depend on external libs and/or code modification :
ex: @cached, @persisted

Note: An open question is whether it's better to use java5.0-only
annotations, or store the info in plain javadoc.

Alain

13 comments
Comment actions Permalink

Candidates:
-


name scope
-


@TestOnly
@TestOnlyPrivate
@TestOnlyProtected
@TestOnlyPackage

@Output
@InOutput
@Immutable
@Undeterministic :inheritable

@Untested :inheritable
@Buggy :inheritable
@Slow/@PossiblySlow :inheritable



-


@TestOnly
-


Example:

@TestOnly
public List getCachedData () {
...
return ...
}


Purpose:
Mark production code (members, fields, elements) that is only to
be used by test code. This code cannot be used at all all by the class
production code (use @TestOnlyXXX alternatives instead).


Inspection:
- enforce: "this field/method/class may only be used by code in the test
source tree"
level=error
- suggest: "this field/method/class can be annotated with @TestOnly"
level=warning
quickfix= add the annotation

Intention:
- move this method/class to the test code tree.



-


@TestOnlyPrivate
-


as @TestOnly, but can be used by the owning class (+inner classes), as a
standard private member.

Typically, a public method could be made private, if it were not used by
test code.

-


@TestOnlyProtected/Package
-


As @TestOnlyPrivate, mutatis mutandis.





-


@Output
-



Purpose:
Mark a method parameter - object, array, collection - that can be
modified by the method.
The original value of the parameter is not used (as would be for a
@InOutput parameter).

Visual feedback:
A small down arrow may be used to indicate the direction of the dataflow.

Inspection:
- enforce: "this field/method/class may only be used by code in the test
source tree"
level=error
- suggest: "this field/method/class can be annotated with @TestOnly"
level=warning
quickfix= add the annotation

Intention:
- move this method/class to the test code tree.

-


@InOutput
-


example:
public void sortInPlace (@InOuptput Comparable []
dataToSortInplace) {
..
}


Same as @Output, but the param value is used in the method.

Visual feedback:
A small upAndDown arrow may be used to indicate the direction of the
dataflow.

0
Comment actions Permalink

First a comment note, then my own suggestions (including some I've actually been working on).

>rule 1: should not depend on external libraries (except for junit)

I would actually avoid JUnit, even. Whatever tools analyze your annotations need to have some way of specifying the distinction between "test" and "source", but I would suggest that depending on JUnit for that is sub-optimal. Better to use something like IDEAs test-root/source-root distinction.

>rule 2: are only to be used by editor (IDEA, Eclipse, ..) inspections, intentions

A better way of putting this would be to say that the annotations shouldn't implement or modify the program's runtime semantics, but should instead only be used to explicate design intent. Some of your suggested annotations might be usable by both static and dynamic analysis programs. For instance, @slow might very well be used by a profiler, as well as a static analyzer.

>Note: An open question is whether it's better to use java5.0-only annotations, or store the info in plain javadoc.

No, it's really not. Javadoc can't annotate library jars, and the full power of some of these only comes into play when libraries get annotated. The only real point in favor of javadoc is that it works with 1.4 source, a benefit with a rather short shelf-life.

>@TestOnly

I love this. I've often been of the opinion that a huge number of classes would benefit from specific testing ports, but the ways of doing this in Java are all somewhat sketchy. In addition to analysis, one could easily imagine compiler settings or byte-code-mungers which stripped out testing code from production, for cases (J2ME) where every byte counts.

>@TestOnlyPrivate

This I don't care for. A better way of getting this effect would be to create a public @TestOnly method which delegates to your private method. Cleaner, amenable to test-code-stripping, and doesn't try to fake out the security model.

>@Output
>@InOutput
>@Immutable

Very much like these. More thoughts in the next post.

>@Undeterministic.

Yes!!! (Except that the correct English is "Nondeterministic").

>@Untested

Something like this needs to exist, but the semantics need to be very tightly specified. Could easily be used by dynamic testing tools (code coverage, mutation testers).

>@Buggy

Yes, theoretically, but who would use it on their own code?

>@Slow/@PossiblySlow

I kind of like this precisely because it fits the vague performance semantics that most developers usually think of, but there really needs to be annotations for performance that include things like expected runtime distributions: @slow(mean = 200, variance = 50, distribution = "gaussian").
--Dave Griffith

0
Comment actions Permalink


The annotations I've been playing with have broadly been in the context of alias analysis. Aliasing is a difficult problem to even think about, but one that is incredibly important from a design perspective, particularly in multi-threaded or secure environments. There has been a fair amount of academic work on annotations for aliasing properties, but mostly it's been oriented toward proving some very hard theorems(e.g. lack of data-races), and less toward simply explicating design intent. I've got a feeling there's value in walking before running.

@Owned --Indicates that references to the object bound to a field should never escape from the containing class (and any inner classes). The field should never be returned by a method, assigned from, or passed to a method which may capture it. Additionally, the field should only be assigned from objects local to the class. Only makes sense for "private" fields.

@Shared -- Opposite of @Owned.

@DeepOwned -- only applicable to array or Collection fields. Indicates that the contents of an array or collection are to be considered @Owned as well.

@SelfContained --indicates that references to an instance of a class should never escape from the class (and any inner classes of it). "this" should never be returned by a method of the class, assigned from, or passed to a method which may capture it.
If you call a method on an instance of a @SelfContained class, you end up with no more references to it after the call than before.

@Captured . Indicates that a parameter to a method may become bound to a new reference in the course of a method call.

@Free . Opposite of @Captured.

@Immutable . Important because @Immutable objects may be safely ignored by most aliasing analyses. In practice, @Immutable classes would have only private fields, which are only assigned in initializers, constructors, readObject(), or clone().

@Utility . Indicates that a class is a utility class. Utility classes should have only static members, shouldn't inherit from anything but object, shouldn't be inherited from, and should never be instantiated.

--Dave Griffith

0
Comment actions Permalink


Another candidate:
@Pure, as in functional languages, I know:)
This annotation could be applied to class methods separately, or to whole
class.
Says this method does not modify current heap aliasing, (though may create
additional objects or modify native environment)
Examples of usage include our constant conditions & assignments analysis: it
could safely ignore calls to immutable
methods retaining all information gathered to the moment.
Probably this has been already discussed though...

Eugene.


0
Comment actions Permalink

Dave

> >rule 1: should not depend on external libraries (except for junit)
>I would actually avoid JUnit, even.

Right. Poor wording to work around the problem that comes below:

> Whatever tools analyze your annotations need to have some way of
specifying the distinction
> between "test" and "source", but I would suggest that depending on
JUnit for that is sub-optimal.
> Better to use something like IDEAs test-root/source-root distinction.


1/ Not always that simple, I'm afraid: See the use case in :
http://www.jetbrains.net/jira/browse/IDEA-1833
We may need a @Tester tag for these edge cases ?!

2/ And we also want that feature to be cross platform.



> > Note: An open question is whether it's better to use java5.0-only
> > annotations, or store the info in plain javadoc.
> No, it's really not. ..
> The only real point in favor of javadoc is that it works with 1.4
source, a benefit with a rather short shelf-life.

I'd like to see percentage of "old" JDK used in legacy/apps under
maintenance. Moving a project to Java5.0 requires extra testing, extra
time, and extra money, and that can be a problem. To name one brand new
commercial app, the first version of the ViewletBuilder for MacOS X that
was published 2 weeks ago doesn't work with Java5.0.



> Javadoc can't annotate library jars, and the full power of
> some of these only comes into play when libraries get annotated.

There is already a lot of value in the inspections related to the simple
annotations, like @Buggy, @Slow, @Immutable, @TestOnly. A lot of legacy
projects would greatly benefit from them.


Maxim was harsher, and killed this request for performance reason:
"allow/recognize @Nullable in javadoc"
http://www.jetbrains.net/jira/browse/IDEA-2372

Though I keep thinking it would be better to give the user an "inspect
javadoc annotations" option.
Note:
- You shouldn't limit IG Telemetry to EAP versions of IDEA.
- It would be great to open it to other inspections plugins too. Add
sortable columns, and we
could see in a snap how much slower inspecting the javadoc
annotations is.




> > @TestOnly


> > @TestOnlyPrivate
> This I don't care for.
A very frequent question in the junit forums is "how do I test private
methods". This is the answer: make it public - for tests-, and let your
IDE enforce its privateness in production code. IDEA could/should even
use this info, when filtering members on their visibility.

> > @TestOnlyProtected
> > @TestOnlyPackage
Like above: "How do I test protected/package methods". This is the answer.


> A better way of getting this effect would be to create a public
@TestOnly method
> which delegates to your private method.

Problem 1: coding style.
I like my classes to be as small as possible; no meaningless code. I
even avoid getters and setters when I can.

> Cleaner ..
Problem 2: how do you name a delegate call to this method?
private void theVeryBestNameForThisAction() {..}

Example - your suggestion

private void theVeryBestNameForThisAction() {..}

@TestOnly
public void theVeryBestNameForThisAction_testDelegate() {
theVeryBestNameForThisAction ();
}


I much prefer

@TestOnlyPrivate
public void theVeryBestNameForThisAction() {..}

It's a matter of taste, but the tool shouldn't force the user to change
its habits when there is no real benefits and/or it can be avoided at a
very low cost.


> > @Untested
> Could easily be used by dynamic testing tools (code coverage,
mutation testers).
Ideally, a code coverage inspection would place those tags.
A special colour for calls to @Untested and @Buggy methods would be great.


> > @Buggy
> Yes, theoretically, but who would use it on their own code?


I place TODO BUG markers in my code. Formalizing this info in an
annotation would open it to inspections.
. If a test passes, that exercises a @Buggy method, it's suspect: you
should test more.
. If foo() is @Buggy, and bar() calls foo(), then bar() is @Buggy.
Actually, bar() is @BuggyPossibly - *1 -, as soon as you remove the
@Buggy in the calling tree, all the inherited @BuggyPossibly are to be
removed automatically.
Add a dedicated colour to @BuggyPossibly and @Buggy code, and code
reviewing would be a easier.

*1: there must a better name to explain that a method is buggy because
it's inheriting the bugginess by calling a genuine buggy method, but I
can't find it. Maybe @BuggySource + @Buggy, where @BuggySource is set
manually, and @Buggy is set automatically and dynamically.


> > @Slow/@PossiblySlow


There's the obvious inspection: "Don't call @Slow code in a loop.",
or

@MustBeFast
public void foo(){
bar(); <---- Error: don't call a @Slow method in @MustBeFast code.
}
@Slow
public void bar(){
}

> .. it fits the vague performance semantics that most developers
usually think of,
..
> but there really needs to be annotations for performance that
include things like
> expected runtime distributions: @slow(mean = 200, variance = 50,
distribution = "gaussian").

Useful and easy to explain, but hard to implement, especially when slow
methods are optimized with limited-size caches.


As you wrote, there is value in walking before running.
Let's walk.

Alain

0
Comment actions Permalink


Yup, that's exactly the sort of thing I was thinking of. I'd forgotten to mention how much more powerful dataflow analyses can be made with this sort of thing. Common-subexpression analysis might actually become valuable at the source-code level, for instance. It also helps more lightweight local analyses. @Pure methods should be flagged if their return values are ignored, as should constructors of @SelfContained classes.

--Dave Griffith

0
Comment actions Permalink


>We may need a @Tester tag for these edge cases ?!

Almost certainly, although a better name could be found.

>A very frequent question in the junit forums is "how do I test private
>methods". This is the answer: make it public - for tests-, and let your
>IDE enforce its privateness in production code.

This would be just fine if "private" was just a design notation enforced by the compiler. Unfortunately, it's also a run-time flag enforced by the JVM's security system. There are a lot of environments which would have to red-light the code you describe for security reasons, but which could green-light the code I describe, once it was run through a @TestOnly stripping process.

>There's the obvious inspection: "Don't call @Slow code in a loop."

"Only call Thread.sleep() or Thread.wait() in @Slow methods."

"Only access JDBC/JNDI/EJB resources in @Slow methods"

Cool ideas, but add up enough of them and contradictions will probably start arising, based on the question of just what is meant by "Slow".

>A special colour for calls to @Untested and @Buggy methods would be great.

And @Slow, as well. Drool.

--Dave Griffith

0
Comment actions Permalink

I think it's cool that people are interested in this kind of thing all
of a sudden, and I think it's cool to talk about it here.

However, I haven't heard any talk of it, so I don't know what the status
is, but I believe JetBrains is involved in starting a JSR for "QA
Annotations" including the nullness annotations and probably more. I
think we should all become involved in that JSR when it is made public,
with ideas like these.

Alain Ravet wrote:

@Nullable and @NotNull
are informative and thanks to the associated inspections and intentions,
really useful. They help us make our code clearer and more solid. This
makes me wish for more of the same vein, hence this thread. Suggestions
are welcome.

(The motivation for placing those new annotations in
org.jetbrains.annotations is to ease distribution. Additionally, it
would also provide some new material to Dave - the Inspection Machine -
G.)


Rules:
rule 1: should not depend on external libraries (except for junit),
rule 2: are only to be used by editor (IDEA, Eclipse, ..) inspections,
intentions
and info popups (=> no bytecode modif, aspect weaving, etc..)
=> bad candidates depend on external libs and/or code modification :
ex: @cached, @persisted

Note: An open question is whether it's better to use java5.0-only
annotations, or store the info in plain javadoc.

Alain

0
Comment actions Permalink

And another...

Parallel to @Untested, there should be

@TestedBy .

@TestedBy takes as arguments a list of class names which test the class or method annotated. Not much use in terms of static analysis, but can be used to allow a poor-man's incremental retest, and useful for code-coverage tools.

Use cases:

1) "I've just changed this class. Run all of the test cases annotated for it".

2) "I want to know if my class is actually covered by all of it's unit test cases. Simply being covered by any unit test in the project isn't good enough."

3) "Goto test case" navigation in an IDE.

Ideally, an IDE or code coverage tool will make it simple to maintain this annotation. Of all the annotations described, this is the one I want most after @Nullable/@NotNull.

--Dave Griffith

0
Comment actions Permalink

Dave Griffith wrote:

>1) "I've just changed this class. Run all of the test cases annotated for it".
>

>

I'd rather have this done transparently:
http://www.jetbrains.net/jira/browse/IDEABKL-557

Note: continous testing would make this obsolete, I think.

Though, the probability of seeing the 2 requests above implemented being
close to zero, I'd be happy if an annotation+inspections set could
provide a similar service.


Alain

0
Comment actions Permalink


Yes, this is very much a "poor man's" continuous testing tool. However, there is some value in being able to say that class Foo is intended to be tested by class FooTest, irrespective of whatever test cases actually hit it, which is what a continuous testing tool would accomplish. Expression of design intent, and all.

On further reflection, this might be better as an annotation on the test class, rather than the tested, though. Otherwise it feels like a rather wierd inversion of dependency direction.

--Dave Griffith

0
Comment actions Permalink

Dave

>On further reflection, this might be better as an annotation on the test class, rather than the tested, though. Otherwise it feels like a rather wierd inversion of dependency direction.
>

>

A 1-1 relationship between production and test classes will be more the
exception than the rule, especially if you group your tests by setup
rather than by target class, and refactor heavily your code.

Even 1 test method may easily exercise more than 1 class:

@test(com.project.foobar.Foo, com.project.foobar.Bar,
com.common.interaction.Utils)
public void testFooBarInteraction () {
...
}

That's not good enough, and already too complex.
If you want to be really accurate - and it would be a waste not to use
all the info found by the inspection - then a finer granularity is
required.
To store it in @nnotations would be overkill. An external file, or an
in-memory structure is required.


Option 1: accuracy.
level=methods (m-n)
The only practical solution is to store the test coverage inspection
result in an external file

Option 2: simplicity
level=classes (1-n)
Store the info in annotations.

In both cases, you need a customized junit runner.


Alain

0

Please sign in to leave a comment.