-
Notifications
You must be signed in to change notification settings - Fork 782
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One main comment from me: Didn't we agree on that we only want to state if a parameter is NonNull
and if if can be null
we omit the @Nullable
annotation?
And I found 2 other lines in the diff on sections that you touched where you could add an annotation.
* @return the thing types provided by the {@link ThingTypeProvider} | ||
*/ | ||
Collection<ThingType> getThingTypes(Locale locale); | ||
Collection<ThingType> getThingTypes(@Nullable Locale locale); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't we agree on that we only want to state if a parameter is NonNull
and if if can be null
we omit the @Nullable
annotation?
* @return thing type for the given UID or null if no type for the given | ||
* UID exists | ||
*/ | ||
@Nullable | ||
ThingType getThingType(ThingTypeUID thingTypeUID, Locale locale); | ||
ThingType getThingType(ThingTypeUID thingTypeUID, @Nullable Locale locale); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't we agree on that we only want to state if a parameter is NonNull
and if if can be null
we omit the @Nullable
annotation?
@@ -43,7 +44,7 @@ protected void unsetTranslationProvider(TranslationProvider i18nProvider) { | |||
this.thingTypeI18nUtil = null; | |||
} | |||
|
|||
public ThingType createLocalizedThingType(Bundle bundle, ThingType thingType, Locale locale) { | |||
public ThingType createLocalizedThingType(Bundle bundle, ThingType thingType, @Nullable Locale locale) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't we agree on that we only want to state if a parameter is NonNull
and if if can be null
we omit the @Nullable
annotation?
public BridgeType(ThingTypeUID uid, List<String> supportedBridgeTypeUIDs, String label, String description, | ||
List<ChannelDefinition> channelDefinitions, List<ChannelGroupDefinition> channelGroupDefinitions, | ||
Map<String, String> properties, URI configDescriptionURI) throws IllegalArgumentException { | ||
public BridgeType(@NonNull ThingTypeUID uid, @Nullable List<String> supportedBridgeTypeUIDs, @NonNull String label, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't we agree on that we only want to state if a parameter is NonNull
and if if can be null
we omit the @Nullable
annotation?
boolean listed, List<ChannelDefinition> channelDefinitions, | ||
List<ChannelGroupDefinition> channelGroupDefinitions, Map<String, String> properties, | ||
URI configDescriptionURI) throws IllegalArgumentException { | ||
public BridgeType(@NonNull ThingTypeUID uid, @Nullable List<String> supportedBridgeTypeUIDs, @NonNull String label, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't we agree on that we only want to state if a parameter is NonNull
and if if can be null
we omit the @Nullable
annotation?
String description) throws IllegalArgumentException { | ||
if ((id == null) || (id.isEmpty())) { | ||
public ChannelDefinition(@NonNull String id, @NonNull ChannelTypeUID channelTypeUID, | ||
@Nullable Map<String, String> properties, @Nullable String label, @Nullable String description) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't we agree on that we only want to state if a parameter is NonNull
and if if can be null
we omit the @Nullable
annotation?
* | ||
* @return subscribed event types (not null) | ||
* | ||
* @return subscribed event types (not null) | ||
*/ | ||
Set<String> getSubscribedEventTypes(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about a @NonNull
on the return value here?
EventFilter getEventFilter(); | ||
|
||
/** | ||
* Callback method for receiving {@link Event}s from the Eclipse SmartHome event bus. This method is called for | ||
* every event where the event subscriber is subscribed to and the event filter applies. | ||
* | ||
* | ||
* @param event the received event (not null) | ||
*/ | ||
void receive(Event event); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about a @NonNull
on the return value?
@@ -37,7 +38,9 @@ | |||
* | |||
* @return the translated text or the default text (could be null or empty) | |||
*/ | |||
String getText(Bundle bundle, String key, String defaultText, Locale locale); | |||
@Nullable | |||
String getText(@Nullable Bundle bundle, @Nullable String key, @Nullable String defaultText, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't we agree on that we only want to state if a parameter is NonNull
and if if can be null
we omit the @Nullable
annotation?
@@ -55,6 +58,8 @@ | |||
* | |||
* @return the translated text or the default text (could be null or empty) | |||
*/ | |||
String getText(Bundle bundle, String key, String defaultText, Locale locale, Object... arguments); | |||
@Nullable | |||
String getText(@Nullable Bundle bundle, @Nullable String key, @Nullable String defaultText, @Nullable Locale locale, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't we agree on that we only want to state if a parameter is NonNull
and if if can be null
we omit the @Nullable
annotation?
Also fixed a potential NPE. Signed-off-by: Markus Rathgeb <maggu2810@gmail.com>
No, we didn't. @kaikreuzer has been allowed to use |
I assume there are thousands of other places, too 😉 |
Signed-off-by: Markus Rathgeb <maggu2810@gmail.com>
And to be fair... The Checker Framework I am using to check my code assumes that a non |
Hmm you are right, @kaikreuzer used it on one occasion in his PR, but in #3624 (comment) he said:
So I am a bit confused now :) |
I agree with the above, but I don't see any comment that states we "must not" use @nullable on arguments. |
Doesn't that mean that you will actually HAVE TO put |
Do you want to tell me that our framework accepts null for every non annotated argument and can return null on every non annotated return value?
Adding annotations does break compatibility? Sorry this is new to me. I do not get the point against adding |
I would say: Yes. Because that is the default for Java and we do not want to touch it.
The point against that is that we do not want to "flood" our code with annotations. Regarding the checker framework: Can it be configured in a way that it accepts the fact that non annotated parameters can be null? |
A default is applied to every type, so arguments and return value. I also don't agree that nullness annotation does "flood" the code. I really don't understand your arguments against that annotations. |
That I don't know.
Not code size, for me its about readability, but if we are just arguing about the parameter of methods I could also live with |
Didn't we already discuss that here?
Sounds to me as a very similar discussion as putting "final" on every single parameter in every method. We all agree that it would be more correct, but nonetheless, it makes method signatures less readable as they are bloated. |
No, this is not the same as The annotations would be useful for third party code of e.g. my company. |
@triller-telekom already pointed to this example to show what this actually means in effect. I think there should be a strong reason FOR adding the annotations and not a reason against not adding them.
I thought I gave it here: "this feature is minimal invasive and really only brings new behavior at the places where we desire it" - so it is about not adding more than is actually needed. It is simply that parameters are nullable by default, if nothing else is specified.
If I understand it correctly, they are only useful, because your tooling (CheckerFramework) invariable assumes a "NonNullByDefault", while for Java (and thus for ESH as long as there is no other decision) it is "NullableByDefault". Or am I missing something?
Nobody wants to make anything hard for anyone. But if some external tools makes assumptions that contradict the assumptions that exist in the projects code base, I wonder how that should be resolved... |
Hm, so it is okay to have this information in the JavaDoc, so the one that reads the API does the correct thing, but it is not okay to add it to function declarations, so tools can use it for checks (e.g. https://github.com/eclipse/smarthome/compare/master...maggu2810:nullable?expand=1#diff-a1ada05baa22b325df4f26fba6959571R53)? "this feature is minimal invasive and really only brings new behavior at the places where we desire it"
I assume one reason that we have "Note the significant difference between @nullable and omitting a null annotation: This annotation explicitly states that null is OK and must be expected. By contrast, no annotation simply means, we don't know what's the intention. This is the old situation where sometimes both sides (caller and callee) redundantly check for null, and some times both sides wrongly assume that the other side will do the check. This is where NullPointerExceptions originate from. Without an annotation the compiler will not give specific advice, but with a @nullable annotation every unchecked dereference will be flagged." (http://help.eclipse.org/kepler/index.jsp?topic=%2Forg.eclipse.jdt.doc.user%2Ftasks%2Ftask-using_null_annotations.htm)
No, it is useful because it is independent of the default a tool is using. |
I stayed out of this discussion for long because I'm really torn... On one hand, I really see the value they can add to our (in the sense of "the Eclipse SmartHome project's") quality assurance. My personal fear however is (and I know the comparison is not fair, it won't happen just with null annotations alone) that we one day end up with something like this: The problem here is: It can be read pretty well by tools, can automatically generate consistent documentation, saves tons of code and of course is absolutely valuable - but it takes a while for humans (or maybe just for me) just to figure out what the signature is of this stupid method and if it takes one, two, three or a hundred parameters. I would like to avoid this. So don't get me wrong: I really support the approach of adding null annotations! But I would appreciate if we could keep them to a minimum, i.e. not stating the obvious. Adding an annotation to every method parameter somehow feels wrong: A method parameter either is allowed to be null or is not. And if we assume a default, then we can save 50%+ of such annotations (if we pick the right one...) in favor of human readability: void stateUpdated(@NonNull EventPublisher eventPublisher, @NonNull ItemChannelLink link,
@Nullable State state, @NonNull Item item); I would love to exchange many of those annotations by just a single @NonNullByDefault
void stateUpdated(EventPublisher eventPublisher, ItemChannelLink link,
@Nullable State state, Item item); And by the way - javadoc is a completely different story. Thanks to highlighting and folding, (at least my) brain became pretty good at ignoring them because they are optically separated and don't clutter the important information. I don't have much experience with the checker framework and have no idea if and how easy it can be adapted to obey such default annotations. And while "we" (in the sense of ESH) are not using it, I don't mind adding the necessary clues in order for you and others to be able to use it. However, IMHO we shouldn't do that to the price that it starts making our code less readable than strictly necessary. |
So, things will change if the Eclipse IDE supports folding / hiding of annotations some time? 😉 Joke... As written already above it is not about the Checker Framework / an external tool that makes some assumptions. I assume I can change my tooling (why not, it is only code) to interpret every argument as nullable and return type as non-null of the ESH package namespace. I have been told multiple times that we interpret every argument as could be null (which I don't think so), how do we handle functions like this one (just an example that I saw while looking at the latest PRs): I still think (not becaue it is the default of the Checker Framework) that more arguments are not allowed to be null then are handled null-tolerant. |
I think we also allowed
This example is a private function, and we only wanted to annotate our public API. But I agree that this method should have a check for null or if possible use |
THAT would be cool 😎
Yes, exactly, see this #3624 (comment).
Yes.
No - default is also nullable (as this is what you have to expect if there is no annotation at all).
Yes, absolutely! This is a perfect example where
Agreed. This is a best practice for framework code anyhow - independently of any annotation discussion. |
I actually didn't yet think about that. This might be a good compromise for all of us then - the classes which we want to (or already have) cover by annotations could be marked as |
IIRC @NonNullByDefault could be defined for package, class, method, ... (correct?) So, we should be strict how to use it because it could be very unclear which default is used. Now you confused me completely.
|
Sorry, that's not my intention 😕
|
Sure, we have to handle null anyhow. But I don't agree that the annotation "only" adds further cluttern.
public static void myFrameworkMethod(String paramB) {
System.out.print(paramB.length());
} This will NOT trigger a warning / error (wrt to the chosen severity) if we call a method on a potential nullable variable. Only if we add the annotation that paramB could be nullable, we will get a warning that Or do I miss the magic point here? Do we assume that our implementations are such perfect that we don't need the annotation for our method implementations? |
Thank you @maggu2810 for summing up our discussion on this wiki page. Since also other contributors are start to use null annotations we should come to an agreement on what to use where soon, so we do not follow different approaches in different parts of the code. The "problems" in our discussion are the fear that we might clutter the code and the need for some (potentially more) annotations for tools. @maggu2810 said that If we follow that path, this would leave our codebase "open" for every "null.checking tool". Since we are developing a framework that will be used by others in their solutions this might be a valid point to consider. However, if we would pick a default such as @NullableByDefault or @NonNullByDefault as @SJKA suggested we could potentially save around 50% of annotations. If we pick @NullableByDefault we would be in line with the default of Java, but instead of compiling the code without issues we would enable the compiler to raise a warning and the developer can be informed in his IDE. Every method would need null checks for its parameters (IMHO a good idea for framework code in general)), or we would have to clutter its signature with @nonnull annotations... In this scenario we would allow @nonnull on parameters AND return values. On the other side if we pick @NonNullByDefault we would say that in the case of method parameters, we would force the consumer of the method to do the null check and can remove them from our framework. I do not know if this is a good idea because we are a framework and users of it might not use null checkers and accidentally pass null values to our functions which do not have checks anymore. However, I think that most functions are designed in a way that they really need the information from their parameters and thus they should be not null. So with this option we might need less @nullable annotations, which we then would allow on return values AND parameters. |
Really? If a method uses nullness annotations it states that it assume that a parameter if not null or it allows that a parameter could be null. So the default of Java is that you can use null all the time as an argument but it could crash. So I don't think it is correct to say that default Java is equal to "assume nullable annotation for every type". There is no statement done that could be used by tools. It is not "nullable" and is also not "non-null". If the framework itself would check every parameter, what do you want to do if there is a violation? I agree that annotate every argument is noisy. |
Can't we allow We could also use I don't think it would make sense to use nullable by default for some classes and non-null by default for other classes. There should be only one default. |
Alright, so let's go for this option then. Since we have packages split over several bundles and to keep it readable, shall we add this @NonNullByDefault annotation to the class rather than to the package? We would also remove With the agreed default we would only allow @nullable on parameters and return values and prohibit the use of @nonnull there. The only place where we need to allow @nonnull would be this example:
In this example we need the @nonnull annotation together with the WDYT @maggu2810? Shall we create a PR with this decision for our coding guidelines? And shall we create a separate PR to remove the use of @nonnull from the code again? |
Sorry I was typing and trying things out in my IDE and didn't refresh my browser so I just read your comment now:
Agreed, but either we should have a PR that removes them or everyone who touches the locations removes the wroingly placed ones.
That's what I suggest.
Will that work with our split packages? Also I think it might be more readable if the class has the @NonNullByDefault annotation instead of a "hidden" |
Sorry, I was not aware of that. Really? Can you give me some examples?
Okay for me (regardless of split packages or not).
Give me some minutes to check something... |
@triller-telekom As long as public class MyStandardClass {
private @NonNull String getString() {
return "foo";
}
public void method() {
final String s = getString();
final MyNonNullClass cl = new MyNonNullClass();
cl.method(s);
}
} So, no need to suppress a warning. |
At least we have them in the test bundles and normal bundles, for example:
I was thinking of classes which we do not (yet) have annotated or which are external and we do not have an external annotation (yet). I don't know what is the better option: either force the developers to create these external annotations once they come across them or have a |
Hm, wouldn't be an external annotation the better solution as a suppressed warning in our code? I played just a little bit with the external annotation support in the Eclipse IDE, but it seems very easy to use. |
Indeed having an annotation is better than suppressing a warning, I agree. Let's start with requiring the developers to add external annotations rather than suppressing the warnings they get and see if its feasible. If not, we can still revert back to the suppressing a warning method. WDYT? So shall we create a PR to update our coding guidelines to what we now agreed on, regarding the use of null annotations? |
@triller-telekom Do you agree that Latest summary (2) is correct? |
Agreed.
Hmm, I thought we only keep those "wrongly" added @nullable and @nonnull annotations until the class gets its @NonNullByDefault and then remove them, but do not add further such annotations... If we do that, how do we decide on "when" is a good time to add the @NonNullByDefault?
If I add @NonNullByDefault on a class which has a non-annotated method in there that returns a
Agreed. |
Really? For me it seems that the annotations are not applied to generic type arguments. But I could be wrong.
We could also state that there is no transition phase. |
@triller-telekom It seems you are right about about the inheritance of the type parameter annotations. There is only one thing that IMHO is not really correctly handled by the Eclipse Nullness checker (but this does not change anything of our usage):
If someone receives a list that members could be nullable it should also be correct to provide a list with non-null entries. |
Regarding the "transition phase problem": How about we do not allow classes which are "half way annotated"? So while coding if you come across an interface method that you want to annotate you can try do add But if the effects of the
is that a warning in our current IDE and maven setup? If so we could live with it and wait until this has been fixed in the Eclipse nullness checker. |
Perhaps it would be the best option to keep things (not the ESH ones 😉 ) clean.
Currently I tested the IDE only.
Agree, perhaps my understanding is wrong or the Eclipse nullness checker needs a bugfix. FYI: import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.eclipse.jdt.annotation.NonNullByDefault;
class NullnessTest {
@NonNullByDefault
protected static class NonNullIsMyDefault {
private final List<String> lst;
public NonNullIsMyDefault(final List<String> lst) {
this.lst = lst;
}
protected boolean foo() {
final @Nullable String stringNullable = lst.get(0); // no warning - as expected
final @NonNull String stringNonNull = lst.get(0); // no warning - as expected
return Objects.equals(stringNullable, stringNonNull);
}
}
protected static class NullableList {
private final List<@Nullable String> lst;
public NullableList(final List<@Nullable String> lst) {
this.lst = lst;
}
protected boolean foo() {
final @Nullable String stringNullable = lst.get(0); // no warning - as expected
final @NonNull String stringNonNull = lst.get(0); // warning as expected
return Objects.equals(stringNullable, stringNonNull);
}
}
protected static class NonAnnotatedList {
private final List<String> lst;
public NonAnnotatedList(final List<String> lst) {
this.lst = lst;
}
protected boolean foo() {
final @Nullable String stringNullable = lst.get(0); // no warning - as expected
final @NonNull String stringNonNull = lst.get(0); // warning as expected
return Objects.equals(stringNullable, stringNonNull);
}
}
protected static void test() {
final List<@NonNull String> listWithNonNullEntries = new LinkedList<>();
final List<@Nullable String> listWithNullableEntries = new LinkedList<>();
new NonNullIsMyDefault(listWithNonNullEntries); // no warning - as expected
new NonNullIsMyDefault(listWithNullableEntries); // warning as expected
new NullableList(listWithNonNullEntries); // warning -- NOT EXPECTED
new NullableList(listWithNullableEntries); // no warning - as expected
new NonAnnotatedList(listWithNonNullEntries); // no warning - as expected
new NonAnnotatedList(listWithNullableEntries); // no warning - as expected
}
} |
So I think we finally have come to an agreement, right? :) Let's use @NonNullByDefault on classes and only allow @nullable on return values and parameters and generics. For third party code we will use the external annotations and create additional ones they do not exist yet. Since you have already created the wiki page, do you want to update its content and create a PR against our coding guidelines documentation with it? |
@triller-telekom Do you agree that Latest summary (3) is correct? |
@triller-telekom We should have a look at the external annotation support to get this into master as soon as possible. |
Agreed. I will take a look into the external annotations again. I remember that the options you can set in the IDE for the nullness checker (performed by the jdt compiler) somehow differ from the ones you can set on the command line, i.e. in our maven pom file. because those two settings are not consistent that might be the reason why the warning doesn't show up in maven in the first place... |
Perhaps interesting for the SAT I tried the Currently this is part of my "findbugs_exclude.xml":
|
In the light of #4217, I would say that the external annotation should be left out of scope (while being clear that we want to use them, but that we still have to work out the details), so that we can very quickly have a decision on the "internal" null annotation use.
@triller-telekom Yes, will you do so? We then would not have to block PRs like #4262 anymore... |
Done, see PR #4275 @maggu2810 I have used your summary on the wiki page, I hope that's fine for you. |
Also fixed a potential NPE.