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

Perform additional validation & fail earlier for incorrect arguments #87

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from

Conversation

Marcono1234
Copy link
Contributor

@Marcono1234 Marcono1234 commented Sep 26, 2024

Before this PR

  • Builder methods were not consistently checking for null
    This would probably often have lead to delayed NullPointerExceptions, or might have even caused different behavior in case null had internally a special meaning.
  • Thrown exception types were not consistent
    Sometimes IllegalArgumentException was thrown for null; in two cases UnsupportedOperationException was thrown instead of IllegalStateException.
  • Validation was missing for adding record constructors
  • Some validation only happened during build(), and not already when calling builder methods
    Note: I only changed a few cases; maybe would be worth to investigate this more in depth in the future. But this applies mainly to immutable state of a builder, e.g. a TypeSpec.Builder for class will keep that kind and it cannot be changed to enum. For other validation is probably fine and better to do this in build().

After this PR

  • NullPointerException is thrown for unexpected null
  • IllegalStateException is thrown when methods are called on wrong type, e.g. adding record constructor to regular class
  • Exceptions are thrown for incorrect record constructor usage
  • Added addModifier(Iterable) overloads, see Fix incomplete toBuilder implementations and add tests #85 (comment)

Possible downsides?

I hope these changes do not accidentally disallow arguments which should be allowed.

@palantirtech
Copy link
Member

Thanks for your interest in palantir/javapoet, @Marcono1234! Before we can accept your pull request, you need to sign our contributor license agreement - just visit https://cla.palantir.com/ and follow the instructions. Once you sign, I'll automatically update this pull request.

@changelog-app
Copy link

changelog-app bot commented Sep 26, 2024

Generate changelog in changelog/@unreleased

What do the change types mean?
  • feature: A new feature of the service.
  • improvement: An incremental improvement in the functionality or operation of the service.
  • fix: Remedies the incorrect behaviour of a component of the service in a backwards-compatible way.
  • break: Has the potential to break consumers of this service's API, inclusive of both Palantir services
    and external consumers of the service's API (e.g. customer-written software or integrations).
  • deprecation: Advertises the intention to remove service functionality without any change to the
    operation of the service itself.
  • manualTask: Requires the possibility of manual intervention (running a script, eyeballing configuration,
    performing database surgery, ...) at the time of upgrade for it to succeed.
  • migration: A fully automatic upgrade migration task with no engineer input required.

Note: only one type should be chosen.

How are new versions calculated?
  • ❗The break and manual task changelog types will result in a major release!
  • 🐛 The fix changelog type will result in a minor release in most cases, and a patch release version for patch branches. This behaviour is configurable in autorelease.
  • ✨ All others will result in a minor version release.

Type

  • Feature
  • Improvement
  • Fix
  • Break
  • Deprecation
  • Manual task
  • Migration

Description

Perform additional validation & fail earlier for incorrect arguments

Check the box to generate changelog(s)

  • Generate changelog entry

Comment on lines +72 to +73
checkState(enclosingClassName == null || enclosingClassName.packageName.equals(packageName),
"enclosing class is in wrong package");
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this is impossible with the public API currently, but I added this check nonetheless to be safe.

Comment on lines -122 to +125
protected final List<AnnotationSpec> concatAnnotations(List<AnnotationSpec> annotations) {
final List<AnnotationSpec> concatAnnotations(List<AnnotationSpec> annotations) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed this because if I understand it correctly TypeName cannot be subclassed by users, so it makes no sense that they see this method if they cannot use it anyway.

Comment on lines 102 to +103
static ArrayTypeName get(ArrayType mirror, Map<TypeParameterElement, TypeVariableName> typeVariables) {
checkNotNull(mirror, "mirror == null");
Copy link
Contributor Author

Choose a reason for hiding this comment

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

For all these get(... mirror, Map typeVariables) methods in the ...TypeName classes I wasn't completely sure if typeVariables can be null either. So for now I did not add a null check.

Comment on lines -434 to +445
// Compact constructor.
// Canonical record constructor.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This just writes the canonical constructor, whether it is also "compact" depends on whether MethodSpec.compactConstructorBuilder() was used.

throw new UnsupportedOperationException(kind + " can't have record constructor");
}
checkState(this.kind == Kind.RECORD, "%s can't have record constructor", this.kind);
checkState(this.recordConstructor == null, "record constructor already set to %s", this.recordConstructor);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This "record constructor already set" is probably consistent with most other builder methods, which don't allow overwriting values:

  • TypeSpec.Builder#superclass
  • MethodSpec.Builder#defaultValue
  • FieldSpec.Builder#initializer

(but there are some exceptions)

@@ -786,19 +810,23 @@ public Builder addEnumConstant(String name) {
}

public Builder addEnumConstant(String name, TypeSpec typeSpec) {
checkNotNull(name, "name == null");
checkNotNull(typeSpec, "typeSpec == null");
checkState(kind == Kind.ENUM, "only enums can have enum constants");
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Side note: This "only enums can have enum constants" is currently also checked in build() (with message "%s is not enum" there)

Comment on lines +865 to +866
checkArgument(methodSpec.defaultValue() == null || kind == Kind.ANNOTATION,
"method with default value is only allowed for annotation type");
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Side note: This defaultValue() check already exists in build() as well

Comment on lines -60 to +61
a = ParameterSpec.builder(int.class, "i").addModifiers(Modifier.STATIC).build();
b = ParameterSpec.builder(int.class, "i").addModifiers(Modifier.STATIC).build();
a = ParameterSpec.builder(int.class, "i").addModifiers(Modifier.FINAL).build();
b = ParameterSpec.builder(int.class, "i").addModifiers(Modifier.FINAL).build();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This includes the fix from #85 (comment), where it was previously not delegating the call, and this addModifiers overload did not validate the modifiers.

@Marcono1234
Copy link
Contributor Author

Currently still waiting for an answer to my questions regarding the CLA, see #84 (comment).

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.

2 participants