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