Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for static fields in contracts #1118

Merged
merged 14 commits into from
Dec 28, 2024

Conversation

armughan11
Copy link
Contributor

This PR adds support for static fields in @EnsureNonnull,EnsureNonnullIf,@RequiresNonnull annotations. Currently the following code will throw validation errors (as well as the annotation handlers are unable to handle static fields). However, after this change, static fields for all three annotations are supported

class Foo {
  @Nullable static Item staticNullableItem;

  @EnsuresNonNull("staticNullableItem")
  public static void initializeStaticField() {
    staticNullableItem = new Item();
  }

  @RequiresNonNull("staticNullableItem")
  public static void useStaticField() {
    staticNullableItem.call();
  }

  @EnsuresNonNullIf(value="staticNullableItem", result=true)
  public static boolean hasStaticNullableItem() {
    return staticNullableItem != null;
  }
}

Fixes #431

@armughan11 armughan11 changed the title Add support for static field in contracts Add support for static fields in contracts Dec 26, 2024
Copy link

codecov bot commented Dec 26, 2024

Codecov Report

Attention: Patch coverage is 97.56098% with 2 lines in your changes missing coverage. Please review.

Project coverage is 88.26%. Comparing base (43054bb) to head (a776d48).
Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
...in/java/com/uber/nullaway/dataflow/AccessPath.java 75.00% 0 Missing and 1 partial ⚠️
...ullaway/handlers/AbstractFieldContractHandler.java 95.65% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master    #1118      +/-   ##
============================================
+ Coverage     88.17%   88.26%   +0.08%     
- Complexity     2238     2259      +21     
============================================
  Files            85       85              
  Lines          7239     7302      +63     
  Branches       1439     1455      +16     
============================================
+ Hits           6383     6445      +62     
  Misses          430      430              
- Partials        426      427       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Collaborator

@msridhar msridhar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot! I'm starting with a couple quick comments on the tests

@armughan11 armughan11 requested a review from msridhar December 26, 2024 19:09
Copy link
Collaborator

@msridhar msridhar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good! A few more minor comments

return getReceiverFields(Nullness.NONNULL, false);
}

public Set<Element> getNonNullStaticReceiverFields() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public Set<Element> getNonNullStaticReceiverFields() {
public Set<Element> getNonNullStaticFields() {

No receiver for a static field

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed!

@@ -278,7 +283,7 @@ public Set<Element> getNonNullReceiverFields() {
* @param nullness The {@code Nullness} state
* @return Set of fields (represented as {@code Element}s) with the given {@code nullness}.
*/
public Set<Element> getReceiverFields(Nullness nullness) {
public Set<Element> getReceiverFields(Nullness nullness, boolean staticOnly) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel the code reuse here via adding the boolean parameter is not worth it; the method is no longer about receiver fields so it gets confusing. Maybe just implement the logic in getNonNullStaticFields(), even though there will be a bit of code duplication.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed!

Comment on lines 171 to 173
"Currently, @"
+ annotName
+ " does not support this. annotations for static fields. ";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The message can just be "Cannot reference to static field " + fieldName + " using this"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed!

@@ -85,11 +86,21 @@ protected boolean validateAnnotationSemantics(
.stream()
.map(e -> e.getSimpleName().toString())
.collect(Collectors.toSet());
Set<String> nonnullStaticFieldsOfReceiverAtExit =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Set<String> nonnullStaticFieldsOfReceiverAtExit =
Set<String> nonnullStaticFieldsAtExit =

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed!

.stream()
.map(e -> e.getSimpleName().toString())
.collect(Collectors.toSet());
nonnullFieldsOfReceiverAtExit.addAll(nonnullStaticFieldsOfReceiverAtExit);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
nonnullFieldsOfReceiverAtExit.addAll(nonnullStaticFieldsOfReceiverAtExit);
nonnullFieldsAtExit.addAll(nonnullStaticFieldsOfReceiverAtExit);

They are not just on the receiver anymore

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed!

Comment on lines 172 to 178
AccessPath accessPath;
if (field.getModifiers().contains(Modifier.STATIC)) {
accessPath = AccessPath.fromStaticField(field);
} else {
accessPath =
AccessPath.fromBaseAndElement(node.getTarget().getReceiver(), field, apContext);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be simpler to just use a conditional expression, AccessPath accessPath = field.getModifiers().contains(Modifier.STATIC) ? ... : ...;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed!

Comment on lines 311 to 317
AccessPath accessPath;

if (field.getModifiers().contains(Modifier.STATIC)) {
accessPath = AccessPath.fromStaticField(field);
} else {
accessPath = AccessPath.fromFieldElement(field);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, use a conditional expression

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed!

Comment on lines 238 to 243
AccessPath accessPath;
if (field.getModifiers().contains(Modifier.STATIC)) {
accessPath = AccessPath.fromStaticField(field);
} else {
accessPath = AccessPath.fromFieldElement(field);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

conditional expression

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed!

@armughan11 armughan11 requested a review from msridhar December 27, 2024 12:17
Copy link
Collaborator

@msridhar msridhar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost there; a few more comments

if (isStaticThisAnnotationField(classSymbol, fieldName)) {

message =
"Cannot reference to static field "
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"Cannot reference to static field "
"Cannot refer to static field "

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

}
return null;
}

public boolean isStaticThisAnnotationField(Symbol.ClassSymbol classSymbol, String fieldName) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renames:

Suggested change
public boolean isStaticThisAnnotationField(Symbol.ClassSymbol classSymbol, String fieldName) {
public boolean isThisDotStaticField(Symbol.ClassSymbol classSymbol, String expression) {

I suggest expression since fieldName might not be just a field name but could start with this..

Also can this method be private?

Copy link
Contributor Author

@armughan11 armughan11 Dec 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@msridhar did the name change but we can't have this as private since we are using it RequiresNonNullHandler to build the access path only if it is not this. annotation. Otherwise we end up honoring this. annotations through AP even though we raise a validation error (the issue you had raised in the first round of review).

Changed it to protected though.

@armughan11 armughan11 requested a review from msridhar December 28, 2024 11:13
@msridhar
Copy link
Collaborator

Thanks for the contribution!

@msridhar msridhar enabled auto-merge (squash) December 28, 2024 17:02
@msridhar msridhar merged commit aaf9f08 into uber:master Dec 28, 2024
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support static fields in @EnsuresNonnull/@RequiresNonnull annotations
2 participants