RFR: 8331051: Add an `@since` checker test for `java.base` module

Nizar Benalla duke at openjdk.org
Mon Apr 29 09:16:30 UTC 2024


On Wed, 24 Apr 2024 21:59:02 GMT, Jonathan Gibbons <jjg at openjdk.org> wrote:

>> This checker checks the values of the `@since` tag found in the documentation comment for an element against the release in which the element first appeared.
>> 
>> Real since value of an API element is computed as the oldest release in which the given API element was introduced. That is:
>> - for modules, classes and interfaces, the release in which the element with the given qualified name was introduced
>> - for constructors, the release in which the constructor with the given VM descriptor was introduced
>> - for methods and fields, the release in which the given method or field with the given VM descriptor became a member of its enclosing class or interface, whether direct or inherited
>> 
>> Effective since value of an API element is computed as follows:
>> - if the given element has a `@since` tag in its javadoc, it is used
>> - in all other cases, return the effective since value of the enclosing element
>> 
>> The since checker verifies that for every API element, the real since value and the effective since value are the same, and reports an error if they are not.
>> 
>> Important note : We only check code written since `JDK 9` as the releases used to determine the expected value of `@since` tags are taken from the historical data built into `javac` which only goes back that far
>> 
>> The intial comment at the beginning of `SinceChecker.java` holds more information into the program.
>> 
>> I already have filed issues and fixed some wrong tags like in #18640, #18032, #18030, #18055, #18373, #18954, #18972.
>
> test/jdk/tools/sincechecker/SinceChecker.java line 49:
> 
>> 47: import java.util.regex.Matcher;
>> 48: import java.util.regex.Pattern;
>> 49: import java.util.stream.Collectors;
> 
> The imports are somewhat unordered.   I recommend the follooing order
> 
> * `java.*` imports first, 
> * then `javax.*` imports
> * then `com.sun.*` imports
> * and finally the `jtreg` import.
> 
> And, alphabetically sorted in each group.

Thanks, fixed in 32edb4d811978f87bd2f850c214191e8fc7b8c88

> test/jdk/tools/sincechecker/SinceChecker.java line 52:
> 
>> 50: 
>> 51: /*
>> 52: The `@since` checker works as a two-step process:
> 
> Insert an initial sentence or paragraph, such as the following:
> 
> 
> This checker checks the values of the `@since` tag found in the documentation comment for an element
> against the release in which the element first appeared.
> 
> The source code containing the documentation comments is read from `src.zip` in the release of
> JDK used to run the test.  The releases used to determine the expected value of `@since` tags
> are taken from the historical data built into `javac`.

Added in  32edb4d811978f87bd2f850c214191e8fc7b8c88

> test/jdk/tools/sincechecker/SinceChecker.java line 79:
> 
>> 77: - When an element is still marked as preview, the `@since` should be the first JDK release where the element was added.
>> 78: - If the element is no longer marked as preview, the `@since` should be the first JDK release where it was no longer preview.
>> 79: 
> 
> Add a comment about need for special treatment of early preview features.

Thanks, added it

> test/jdk/tools/sincechecker/SinceChecker.java line 116:
> 
>> 114:     }
>> 115: 
>> 116:     private void processModuleRecord(ModuleElement moduleElement, String releaseVersion, JavacTask ct) {
> 
> for this method, and similarly named methods, the use of `Record` seems confusing, if only because there do not seem to be any record classes being processed or analyzed.   Consider dropping `Record` or changing it to `Element`. Same for `analyzePackageRecord`, `analyzeClassRecord`.
> 
> Also, consider being consistent with `process` or analyze`.

Fixed in 32edb4d811978f87bd2f850c214191e8fc7b8c88

> test/jdk/tools/sincechecker/SinceChecker.java line 140:
> 
>> 138:         persistElement(te.getEnclosingElement(), te, types, version);
>> 139:         elements.getAllMembers(te).stream()
>> 140:                 .filter(element -> element.getModifiers().contains(Modifier.PUBLIC) || element.getModifiers().contains(Modifier.PROTECTED))
> 
> Consider writing and using a predicate method:
> 
> 
> boolean isDocumented(Element e) {
>     var mods = e.getModifiers();
>     return mods.contains(Modifier,.PUBLIC) || mods.contains(Modifier.PROTECTED);
> }
> 
> 
> You could then use that method in the obvious way for the class declaration(line 134) and in a lambda as follows:
> 
>     .filter(this::isDocumented)

Added in 32edb4d811978f87bd2f850c214191e8fc7b8c88

> test/jdk/tools/sincechecker/SinceChecker.java line 143:
> 
>> 141:                 .filter(element -> element.getKind().isField()
>> 142:                         || element.getKind() == ElementKind.METHOD
>> 143:                         || element.getKind() == ElementKind.CONSTRUCTOR)
> 
> Consider writing and using another predicate method, 
> 
>     boolean isMember(Element e) {
>         var k = e.getKind();
>         return k.isField() || switch (k) {
>             case CONSTRUCTOR, METHOD -> true;
>             default -> false;
>         };
>      }

Added in 32edb4d811978f87bd2f850c214191e8fc7b8c88

> test/jdk/tools/sincechecker/SinceChecker.java line 178:
> 
>> 176:     }
>> 177: 
>> 178:     private void testThisModule(String moduleName) throws Exception {
> 
> The word `this` is a bit distracting/unnecessary here.  It's enough to say `testModule(String moduleName)` although `checkModule(String moduleName)` would be even better

Thanks, renamed it to `CheckModule`

> test/jdk/tools/sincechecker/SinceChecker.java line 190:
> 
>> 188:         if (!f.exists() && !f.isDirectory()) {
>> 189:             throw new SkippedException("Skipping Test because src.zip wasn't found");
>> 190:         }
> 
> For modern/new code I would recommend using `java.nio.file.Path` instead of `java.io.File`.
> 
> I see you actually have a `Path` in `srcZip`, so use `Files.exists(srcZip)` and `Files.isDirectory(srcZip)`

Fixed in 32edb4d811978f87bd2f850c214191e8fc7b8c88

> test/jdk/tools/sincechecker/SinceChecker.java line 214:
> 
>> 212:                     }
>> 213:                     if (!wrongTagsList.isEmpty()) {
>> 214:                         throw new Exception(wrongTagsList.toString());
> 
> This looks like it might be the exception to end the test, right, and the wrongTags could be long.
> I'd suggest writing a heading to `System.err`, then`wrongTagsList.forEach(System.err::println);` and then throw the exception with a summary string, like `throw new Exception(wrongTagsList.size() + " problems found");

Done. Added in 32edb4d811978f87bd2f850c214191e8fc7b8c88

> test/jdk/tools/sincechecker/SinceChecker.java line 223:
> 
>> 221:     private void processModuleCheck(ModuleElement moduleElement, JavacTask ct, Path packagePath, EffectiveSourceSinceHelper javadocHelper) {
>> 222:         if (moduleElement == null) {
>> 223:             wrongTagsList.add("Module element: was null because `elements.getModuleElement(moduleName)` returns null." +
> 
> This doesn't look like a "Wrong tag" so much as a general error message.
> 
> Consider this as a different paradigm -- instead of saving up strings in `wrongTagsList`, consider having a method to report (and count) error messages as you find them:
> 
> 
>     private int errorCount;
>     void error(String message) {
>         System.err.println(message);
>         errorCount++;
>    }
> 
> 
> then at the end of `testThisModule`, just check if `errorCount>0` to decide whether to throw the exception.
> 
> Note, in this model, you need not have a `\n` at the end of every string argument, the way you do for add strings added into `wrongTagsList`.

Changed it and removed '\n'

> test/jdk/tools/sincechecker/SinceChecker.java line 274:
> 
>> 272:             try {
>> 273:                 byte[] packageAsBytes = Files.readAllBytes(pkgInfo);
>> 274:                 String packageContent = new String(packageAsBytes, StandardCharsets.UTF_8);
> 
> Easier to just use `Files.readString`

Fixed it, thanks

> test/jdk/tools/sincechecker/SinceChecker.java line 358:
> 
>> 356:     private void checkEquals(String sinceVersion, String mappedVersion, String id) {
>> 357:         if (sinceVersion == null || mappedVersion == null) {
>> 358:             wrongTagsList.add("For " + id + " NULL value for either mapped or real `@since` . mapped version is="
> 
> Here and elsewhere, instead of `"For " + name + ....`  consider using the format `name + ":  " + ...` for the format of the messages

Fixed it.

> test/jdk/tools/sincechecker/SinceChecker.java line 421:
> 
>> 419:         public String introducedPreview;
>> 420:         public String introducedStable;
>> 421:     }
> 
> Suggest move this nearer the map at the top of the class -- either move this class up, or those members down.

Thanks, moved it up

> test/jdk/tools/sincechecker/SinceChecker.java line 543:
> 
>> 541:     }
>> 542: 
>> 543:     private final class EffectiveSourceSinceHelper implements AutoCloseable {
> 
> Consider putting a doc comment describing the purpose of this class. The name  hints at something useful, but is not specific enough by itself.

Added it, thanks

> test/jdk/tools/sincechecker/SinceChecker.java line 559:
> 
>> 557:                     fm.close();
>> 558:                 } catch (IOException closeEx) {
>> 559:                 }
> 
> Consider using `ex.addSuppressed(closeEx);` instead of just dropping the exception

Fixed in 32edb4d811978f87bd2f850c214191e8fc7b8c88

> test/jdk/tools/sincechecker/SinceChecker.java line 716:
> 
>> 714:         }
>> 715: 
>> 716:         private static final class PatchModuleFileManager
> 
> provide a short doc comment describing the purpose/use of this class

Added it, thanks

> test/jdk/tools/sincechecker/testjavabase/SinceChecker.java line 37:
> 
>> 35: 
>> 36: public class SinceChecker {
>> 37: }
> 
> You don't need this class, do you?
> The comment should be enough to run the validator

Thanks, didn't know that.

-------------

PR Review Comment: https://git.openjdk.org/jdk/pull/18934#discussion_r1582576708
PR Review Comment: https://git.openjdk.org/jdk/pull/18934#discussion_r1582576954
PR Review Comment: https://git.openjdk.org/jdk/pull/18934#discussion_r1582577120
PR Review Comment: https://git.openjdk.org/jdk/pull/18934#discussion_r1582577505
PR Review Comment: https://git.openjdk.org/jdk/pull/18934#discussion_r1582578042
PR Review Comment: https://git.openjdk.org/jdk/pull/18934#discussion_r1582578390
PR Review Comment: https://git.openjdk.org/jdk/pull/18934#discussion_r1582585880
PR Review Comment: https://git.openjdk.org/jdk/pull/18934#discussion_r1582579802
PR Review Comment: https://git.openjdk.org/jdk/pull/18934#discussion_r1582580425
PR Review Comment: https://git.openjdk.org/jdk/pull/18934#discussion_r1582580782
PR Review Comment: https://git.openjdk.org/jdk/pull/18934#discussion_r1582582577
PR Review Comment: https://git.openjdk.org/jdk/pull/18934#discussion_r1582583030
PR Review Comment: https://git.openjdk.org/jdk/pull/18934#discussion_r1582578684
PR Review Comment: https://git.openjdk.org/jdk/pull/18934#discussion_r1582581513
PR Review Comment: https://git.openjdk.org/jdk/pull/18934#discussion_r1582581661
PR Review Comment: https://git.openjdk.org/jdk/pull/18934#discussion_r1582581840
PR Review Comment: https://git.openjdk.org/jdk/pull/18934#discussion_r1578128902


More information about the core-libs-dev mailing list