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

feat(#165): reserved-name lint #440

Merged
merged 37 commits into from
Apr 4, 2025
Merged

feat(#165): reserved-name lint #440

merged 37 commits into from
Apr 4, 2025

Conversation

h1alexbel
Copy link
Member

In this PR I've implemented new lint: reserved-name, that issues defect with warning severity if object uses name, already reserved by object in org.eolang.* package.

closes #165
History:

@h1alexbel h1alexbel changed the title feat(#165): reserved-name lint feat(#165): reserved-name lint Mar 28, 2025
@h1alexbel h1alexbel marked this pull request as draft March 28, 2025 17:51
@h1alexbel h1alexbel marked this pull request as ready for review April 1, 2025 15:35
@h1alexbel
Copy link
Member Author

@volodya-lombrozo updated. Take a look, please

private static final Pattern NON_UNIX = Pattern.compile("\\\\");

/**
* Reserved names.
Copy link
Member

Choose a reason for hiding this comment

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

@h1alexbel I was reading this file and asked myself several times what the keys and values are. I guess a key here is an object name and a value is an object path. Am I right? If so, it would be better to clarify this in the Javadocs.

* @checkstyle IllegalCatchCheck (15 lines)
*/
@SuppressWarnings("PMD.AvoidCatchingGenericException")
private static Consumer<Path> jar(final Map<String, String> names) {
Copy link
Member

Choose a reason for hiding this comment

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

@h1alexbel What is the difference between file and jar methods? Aren't they the same?

This code looks similar to me:

parsed = new EoSyntax(
   "reserved", new UncheckedInput(new InputOf(eo.toFile()))
).parsed();

and

InputStream input = Files.newInputStream(eo)
parsed = new EoSyntax(
  "reserved", new TextOf(input).asString()
).parsed();

Moreover, under the hood EoSyntax will wrap the string from the last example to the same Input:

public EoSyntax(final String nme, final String ipt) {
  this(nme, new InputOf(ipt));
}

Maybe I'm missing something?

Copy link
Member Author

@h1alexbel h1alexbel Apr 3, 2025

Choose a reason for hiding this comment

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

@volodya-lombrozo in the jar method, we need to read the content from the input first, and only then parse it, otherwise we are getting:

Caused by: java.io.EOFException: Unexpected end of ZLIB input stream
	at jdk.zipfs/jdk.nio.zipfs.ZipFileSystem$2.fill(ZipFileSystem.java:2275)
	at java.base/java.util.zip.InflaterInputStream.read(InflaterInputStream.java:175)
	at org.cactoos.io.TeeInputStream.read(TeeInputStream.java:61)
	at org.cactoos.io.TeeInputStream.read(TeeInputStream.java:55)
	at org.cactoos.bytes.InputAsBytes.asBytes(InputAsBytes.java:63)
	at org.cactoos.bytes.BytesOf.asBytes(BytesOf.java:732)
	at org.cactoos.text.TextOf.lambda$new$0(TextOf.java:272)
	at org.cactoos.text.TextOfScalar.asString(TextOfScalar.java:39)
	at org.cactoos.text.TextEnvelope.asString(TextEnvelope.java:32)
	at org.cactoos.text.Split.lambda$new$0(Split.java:108)
	at org.cactoos.scalar.Checked.value(Checked.java:56)
	at org.cactoos.scalar.IoChecked.value(IoChecked.java:44)
	at org.cactoos.scalar.Unchecked.value(Unchecked.java:37)
	... 34 more

Also, we cannot convert Path in jar to file:

Caused by: java.lang.UnsupportedOperationException
	at jdk.zipfs/jdk.nio.zipfs.ZipPath.toFile(ZipPath.java:673)
	at org.eolang.lints.LtReservedName.lambda$jar$3(LtReservedName.java:199)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
	at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.Iterator.forEachRemaining(Iterator.java:133)
	at java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1939)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596)
	at org.eolang.lints.LtReservedName.home(LtReservedName.java:148)
	at org.eolang.lints.LtReservedName.<init>(LtReservedName.java:61)
	at org.eolang.lints.MonoLints.<clinit>(MonoLints.java:35)
	... 6 more

* @param names Names
* @return File consumer from path
*/
private static Consumer<Path> file(final Map<String, String> names) {
Copy link
Member

Choose a reason for hiding this comment

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

@h1alexbel To be honest, I have some doubts about the design of file(), jar(), and processXmir(). In all of these methods, you pass a final Map<String, String> names to populate it. This approach works well and is efficient. However, it feels a bit overcomplicated to me and relies on indirection through Consumer<Path>.

Could we transform these methods from manipulators to builders (similar to the home() method)? What I mean is that it might be better to change their signatures from

Consumer<Path> file(final Map<String, String> names)

to something like
Map<String, String> file(final Path path)

or
Entry<Map<String, String>> file(final Path path)

Then, instead of the following code:

Files.walk(Paths.get(resource.toURI()))
  .filter(sources)
  .forEach(LtReservedName.file(names));

You could use something like:

Files.walk(Paths.get(resource.toURI()))
  .filter(sources)
  .map(LtReservedName::file)
  .toMap()

What do you think?

Copy link
Member Author

@h1alexbel h1alexbel Apr 3, 2025

Choose a reason for hiding this comment

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

@volodya-lombrozo yes, indeed it overcomplicated, refactoring to Map<String, String> namesInFile(Path path) now.

@h1alexbel
Copy link
Member Author

@volodya-lombrozo updated. Take a look, please

Copy link
Member

@volodya-lombrozo volodya-lombrozo left a comment

Choose a reason for hiding this comment

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

@h1alexbel Thank you for the changes. Now it's much better. I've left one more question.

.toString().replace("\\", "/")
);
};
if ("jar".equals(resource.getProtocol())) {
Copy link
Member

Choose a reason for hiding this comment

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

@h1alexbel Can you somehow get a jar here? As I understood, you copy all the .eo files from https://github.com/objectionary/home.git and then just look object inside of them. Please, correct me if I'm wrong. Thus, how can you can get a jar here? Moreover, you have the only one constructor:

LtReservedName() {
    this(LtReservedName.home(Paths.get("cloned", "home").toString()));
}

Copy link
Member Author

@h1alexbel h1alexbel Apr 4, 2025

Choose a reason for hiding this comment

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

@volodya-lombrozo during the build of the lints project, we are cloning home using git clone https://github.com/objectionary/home.git to clone it to the build directory: ${project.build.directory}/cloned/home. Then, to be able reuse those files outside of lints, when we are importing lints project - we copy ${project.build.directory}/cloned/home to classes in lints JAR: ${project.build.directory}/classes/cloned/home.

This expression if ("jar".equals(resource.getProtocol())) { returns true, when dependent on lint project executes linting via Program. Such example is our invoker integration test. While, in local tests, cloned home repo will be accessed as normal file.

Both methods depend on the same directory, which we pass in the ctor: cloned/home, the only difference in the term of access - for JAR we need to "mount" the file system using java.nio.file.FileSystem.

Copy link
Member

Choose a reason for hiding this comment

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

@h1alexbel I see, thank you for the comprehensive explanation. Now it's clear. Could you add this explanation to the JavaDocs, please? It will greatly help other developers to understand what is going on.

Also I would highly recommend simplifying the solution. For example, you can generate a list of objects during the lints build and just reuse it in runtime. Yes, it's a less dynamic approach and each time when eo changes we will need to rebuild and release lints, but it's much more reliable solution (KISS principle). However, It's better to discuss in a separate issue. What do you think?

Copy link
Member Author

Choose a reason for hiding this comment

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

@volodya-lombrozo yes, I will add this information to the JavaDocs. About simplification, let's discuss in a separate issue.

.toString().replace("\\", "/")
);
};
if ("jar".equals(resource.getProtocol())) {
Copy link
Member

Choose a reason for hiding this comment

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

@h1alexbel I see, thank you for the comprehensive explanation. Now it's clear. Could you add this explanation to the JavaDocs, please? It will greatly help other developers to understand what is going on.

Also I would highly recommend simplifying the solution. For example, you can generate a list of objects during the lints build and just reuse it in runtime. Yes, it's a less dynamic approach and each time when eo changes we will need to rebuild and release lints, but it's much more reliable solution (KISS principle). However, It's better to discuss in a separate issue. What do you think?

@h1alexbel
Copy link
Member Author

@volodya-lombrozo added JavaDoc. Take a look, please

Copy link
Member

@volodya-lombrozo volodya-lombrozo left a comment

Choose a reason for hiding this comment

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

@h1alexbel Looks good to me. Thanks.

@h1alexbel
Copy link
Member Author

@yegor256 take a look, please

pom.xml Outdated
<configuration>
<executable>git</executable>
<arguments>
<argument>clone</argument>
Copy link
Member

Choose a reason for hiding this comment

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

@h1alexbel maybe a shallow copy will be enough? Full git clone is very time/space expensive.

pom.xml Outdated
<arguments>
<argument>clone</argument>
<argument>https://github.com/objectionary/home.git</argument>
<argument>${project.build.directory}/cloned/home</argument>
Copy link
Member

Choose a reason for hiding this comment

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

@h1alexbel try to do this: mvn clean install && mvn install and see the result

Copy link
Member

Choose a reason for hiding this comment

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

@h1alexbel maybe it's better to use git submodule instead of git clone? Or maybe just wget to download master from home in ZIP archive?

Copy link
Member Author

Choose a reason for hiding this comment

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

@yegor256 wget will fail on windows. Am I understood correctly that we should add home repo as Git submodule to the lints repo?

Copy link
Member

Choose a reason for hiding this comment

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

@yegor256
Copy link
Member

yegor256 commented Apr 4, 2025

@h1alexbel many thanks for the contribution! See a few comments above.

@h1alexbel
Copy link
Member Author

@yegor256 updated. Take a look, please

@h1alexbel h1alexbel requested a review from yegor256 April 4, 2025 13:50
@yegor256
Copy link
Member

yegor256 commented Apr 4, 2025

@rultor merge

@rultor
Copy link
Contributor

rultor commented Apr 4, 2025

@rultor merge

@yegor256 OK, I'll try to merge now. You can check the progress of the merge here.

@rultor rultor merged commit f56b643 into objectionary:master Apr 4, 2025
17 checks passed
@rultor
Copy link
Contributor

rultor commented Apr 4, 2025

@rultor merge

@yegor256 Done! FYI, the full log is here (took me 15min).

@h1alexbel h1alexbel deleted the 165 branch April 4, 2025 14:32
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.

prohibit reserved names
4 participants