From fb22c189f4b09dd3085d3a6572cc93f270ae67a5 Mon Sep 17 00:00:00 2001 From: ivamly Date: Thu, 25 Jul 2024 14:35:00 +0300 Subject: [PATCH] Add rule to prevent calls to Objects.requireNonNull() See gh-41611 --- .../build/architecture/ArchitectureCheck.java | 23 +++++++++++- .../architecture/ArchitectureCheckTests.java | 32 ++++++++++++++++ .../NoRequireNonNullWithMessageUsage.java | 37 +++++++++++++++++++ .../NoRequireNonNullWithSupplierUsage.java | 37 +++++++++++++++++++ .../RequireNonNullWithMessageUsage.java | 37 +++++++++++++++++++ .../RequireNonNullWithSupplierUsage.java | 37 +++++++++++++++++++ 6 files changed, 201 insertions(+), 2 deletions(-) create mode 100644 buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/noRequireNonNullWithMessage/NoRequireNonNullWithMessageUsage.java create mode 100644 buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/noRequireNonNullWithSupplier/NoRequireNonNullWithSupplierUsage.java create mode 100644 buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/requireNonNullWithMessage/RequireNonNullWithMessageUsage.java create mode 100644 buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/requireNonNullWithSupplier/RequireNonNullWithSupplierUsage.java diff --git a/buildSrc/src/main/java/org/springframework/boot/build/architecture/ArchitectureCheck.java b/buildSrc/src/main/java/org/springframework/boot/build/architecture/ArchitectureCheck.java index d671304708..ae88a3e8c8 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/architecture/ArchitectureCheck.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/architecture/ArchitectureCheck.java @@ -23,6 +23,8 @@ import java.net.URLEncoder; import java.nio.file.Files; import java.nio.file.StandardOpenOption; import java.util.List; +import java.util.Objects; +import java.util.function.Supplier; import java.util.stream.Collectors; import com.tngtech.archunit.base.DescribedPredicate; @@ -75,7 +77,9 @@ public abstract class ArchitectureCheck extends DefaultTask { allBeanFactoryPostProcessorBeanMethodsShouldBeStaticAndHaveNoParameters(), noClassesShouldCallStepVerifierStepVerifyComplete(), noClassesShouldConfigureDefaultStepVerifierTimeout(), noClassesShouldCallCollectorsToList(), - noClassesShouldCallURLEncoderWithStringEncoding(), noClassesShouldCallURLDecoderWithStringEncoding()); + noClassesShouldCallURLEncoderWithStringEncoding(), noClassesShouldCallURLDecoderWithStringEncoding(), + noClassesShouldCallObjectsRequireNonNullWithMessage(), + noClassesShouldCallObjectsRequireNonNullWithSupplier()); getRuleDescriptions().set(getRules().map((rules) -> rules.stream().map(ArchRule::getDescription).toList())); } @@ -208,6 +212,20 @@ public abstract class ArchitectureCheck extends DefaultTask { .because("java.net.URLDecoder.decode(String s, Charset charset) should be used instead"); } + private ArchRule noClassesShouldCallObjectsRequireNonNullWithMessage() { + return ArchRuleDefinition.noClasses() + .should() + .callMethod(Objects.class, "requireNonNull", Object.class, String.class) + .because("Use org.springframework.utils.Assert.notNull(Object, String) should be used instead"); + } + + private ArchRule noClassesShouldCallObjectsRequireNonNullWithSupplier() { + return ArchRuleDefinition.noClasses() + .should() + .callMethod(Objects.class, "requireNonNull", Object.class, Supplier.class) + .because("Use org.springframework.utils.Assert.notNull(Object, Supplier) should be used instead"); + } + public void setClasses(FileCollection classes) { this.classes = classes; } @@ -237,7 +255,8 @@ public abstract class ArchitectureCheck extends DefaultTask { public abstract ListProperty getRules(); @Input - // The rules themselves can't be an input as they aren't serializable so we use their + // The rules themselves can't be an input as they aren't serializable so we use + // their // descriptions instead abstract ListProperty getRuleDescriptions(); diff --git a/buildSrc/src/test/java/org/springframework/boot/build/architecture/ArchitectureCheckTests.java b/buildSrc/src/test/java/org/springframework/boot/build/architecture/ArchitectureCheckTests.java index 1294d61929..b10d8009e7 100644 --- a/buildSrc/src/test/java/org/springframework/boot/build/architecture/ArchitectureCheckTests.java +++ b/buildSrc/src/test/java/org/springframework/boot/build/architecture/ArchitectureCheckTests.java @@ -121,6 +121,38 @@ class ArchitectureCheckTests { }); } + @Test + void whenClassCallsObjectsRequireNonNullWithMessageTaskFailsAndWritesReport() throws Exception { + prepareTask("objects/requireNonNullWithMessage", (architectureCheck) -> { + assertThatExceptionOfType(GradleException.class).isThrownBy(architectureCheck::checkArchitecture); + assertThat(failureReport(architectureCheck)).isNotEmpty(); + }); + } + + @Test + void whenClassDoesNotCallObjectsRequireNonNullWithMessageTaskSucceedsAndWritesAnEmptyReport() throws Exception { + prepareTask("objects/noRequireNonNullWithMessage", (architectureCheck) -> { + architectureCheck.checkArchitecture(); + assertThat(failureReport(architectureCheck)).isEmpty(); + }); + } + + @Test + void whenClassCallsObjectsRequireNonNullWithSupplierTaskFailsAndWritesReport() throws Exception { + prepareTask("objects/requireNonNullWithSupplier", (architectureCheck) -> { + assertThatExceptionOfType(GradleException.class).isThrownBy(architectureCheck::checkArchitecture); + assertThat(failureReport(architectureCheck)).isNotEmpty(); + }); + } + + @Test + void whenClassDoesNotCallObjectsRequireNonNullWithSupplierTaskSucceedsAndWritesAnEmptyReport() throws Exception { + prepareTask("objects/noRequireNonNullWithSupplier", (architectureCheck) -> { + architectureCheck.checkArchitecture(); + assertThat(failureReport(architectureCheck)).isEmpty(); + }); + } + private void prepareTask(String classes, Callback callback) throws Exception { File projectDir = new File(this.temp, "project"); projectDir.mkdirs(); diff --git a/buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/noRequireNonNullWithMessage/NoRequireNonNullWithMessageUsage.java b/buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/noRequireNonNullWithMessage/NoRequireNonNullWithMessageUsage.java new file mode 100644 index 0000000000..066faa7746 --- /dev/null +++ b/buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/noRequireNonNullWithMessage/NoRequireNonNullWithMessageUsage.java @@ -0,0 +1,37 @@ +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.build.architecture.objects.noRequireNonNullWithMessage; + +import org.springframework.util.Assert; + +/** + * This class uses `Assert.notNull(Object, String)` instead of + * `Objects.requireNonNull(Object, String)`, and should pass the architecture check. + * + * @author Ivan Malutin + */ +public class NoRequireNonNullWithMessageUsage { + + /** + * Example method that uses `Assert.notNull(Object, String)`, which should not be + * flagged by the architecture check. + */ + public void exampleMethod() { + Assert.notNull(new Object(), "Object must not be null"); + } + +} diff --git a/buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/noRequireNonNullWithSupplier/NoRequireNonNullWithSupplierUsage.java b/buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/noRequireNonNullWithSupplier/NoRequireNonNullWithSupplierUsage.java new file mode 100644 index 0000000000..36ab315e41 --- /dev/null +++ b/buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/noRequireNonNullWithSupplier/NoRequireNonNullWithSupplierUsage.java @@ -0,0 +1,37 @@ +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.build.architecture.objects.noRequireNonNullWithSupplier; + +import org.springframework.util.Assert; + +/** + * This class uses `Assert.notNull(Object, Supplier)` instead of + * `Objects.requireNonNull(Object, Supplier)`, and should pass the architecture check. + * + * @author Ivan Malutin + */ +public class NoRequireNonNullWithSupplierUsage { + + /** + * Example method that uses `Assert.notNull(Object, Supplier)`, which should not be + * flagged by the architecture check. + */ + public void exampleMethod() { + Assert.notNull(new Object(), () -> "Object must not be null"); + } + +} diff --git a/buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/requireNonNullWithMessage/RequireNonNullWithMessageUsage.java b/buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/requireNonNullWithMessage/RequireNonNullWithMessageUsage.java new file mode 100644 index 0000000000..9e7ee8241f --- /dev/null +++ b/buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/requireNonNullWithMessage/RequireNonNullWithMessageUsage.java @@ -0,0 +1,37 @@ +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.build.architecture.objects.requireNonNullWithMessage; + +import java.util.Objects; + +/** + * This class is intentionally designed to test the use of `Objects.requireNonNull(Object, + * String)`, which should trigger a failure in the architecture check. + * + * @author Ivan Malutin + */ +public class RequireNonNullWithMessageUsage { + + /** + * Example method that uses `Objects.requireNonNull(Object, String)`, which should be + * flagged by the architecture check. + */ + public void exampleMethod() { + Objects.requireNonNull(new Object(), "Object cannot be null"); + } + +} diff --git a/buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/requireNonNullWithSupplier/RequireNonNullWithSupplierUsage.java b/buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/requireNonNullWithSupplier/RequireNonNullWithSupplierUsage.java new file mode 100644 index 0000000000..5f6529b22f --- /dev/null +++ b/buildSrc/src/test/java/org/springframework/boot/build/architecture/objects/requireNonNullWithSupplier/RequireNonNullWithSupplierUsage.java @@ -0,0 +1,37 @@ +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.build.architecture.objects.requireNonNullWithSupplier; + +import java.util.Objects; + +/** + * This class is intentionally designed to test the use of `Objects.requireNonNull(Object, + * Supplier)`, which should trigger a failure in the architecture check. + * + * @author Ivan Malutin + */ +public class RequireNonNullWithSupplierUsage { + + /** + * Example method that uses `Objects.requireNonNull(Object, Supplier)`, which should + * be flagged by the architecture check. + */ + public void exampleMethod() { + Objects.requireNonNull(new Object(), () -> "Object cannot be null"); + } + +}