Repository: objectionary/lints
Pull request #462: bug(#460): relatively `program/@source` check in `object-does-not-match-filename`
Author: login: h1alexbel
Created: 2025-04-04T09:14:11Z | Merged: 2025-04-08T15:56:33Z

In this PR I've updated `object-does-not-match-filename` to check only `program/@source` contains object name, instead of checking full path `program/@source` starts with object name.

closes #460


Linked issue #460: `object-does-not-match-filename` complains when the name is almost right
I see this warning:

```
org.eolang.sys.posix:17 It is expected that the "org.eolang.sys.posix" object is
coming from either "org/eolang/sys/posix.eo" or "org/eolang/sys/posix.phi" file, 
however its "program/@source" points to ".eoc/2-pull/org/eolang/sys/posix.eo" 
(object-does-not-match-filename)
```

Looks like a false alarm to me. Maybe `object-does-not-match-filename` should only check the tail of the `@source` attribute?
Linked issue comments: user: login: yegor256
created_at: 2025-04-03T11:48:47Z
body: @h1alexbel what do you think?
author_association: MEMBER
user: login: h1alexbel
created_at: 2025-04-03T13:41:01Z
body: @yegor256 indeed, its a bug, we should check only relative path in the lint
author_association: MEMBER

Diff:
diff --git a/src/main/resources/org/eolang/lints/names/object-does-not-match-filename.xsl b/src/main/resources/org/eolang/lints/names/object-does-not-match-filename.xsl
index 1aee64a27..bb20f670b 100644
--- a/src/main/resources/org/eolang/lints/names/object-does-not-match-filename.xsl
+++ b/src/main/resources/org/eolang/lints/names/object-does-not-match-filename.xsl
@@ -28,7 +28,7 @@
       </xsl:choose>
     </xsl:variable>
     <xsl:variable name="source" as="xs:string" select="replace($opath, '\.', '/')"/>
-    <xsl:if test="not(starts-with(/program/@source, concat($source, '.')))">
+    <xsl:if test="not(contains(/program/@source, concat($source, concat('.', substring-after(substring-after(/program/@source, '.'), '.')))))">
       <defect>
         <xsl:variable name="line" select="eo:lineno(@line)"/>
         <xsl:attribute name="line">
diff --git a/src/test/resources/org/eolang/lints/packs/single/object-does-not-match-filename/allows-good-name-relatively.yaml b/src/test/resources/org/eolang/lints/packs/single/object-does-not-match-filename/allows-good-name-relatively.yaml
new file mode 100644
index 000000000..2e262b376
--- /dev/null
+++ b/src/test/resources/org/eolang/lints/packs/single/object-does-not-match-filename/allows-good-name-relatively.yaml
@@ -0,0 +1,20 @@
+# SPDX-FileCopyrightText: Copyright (c) 2016-2025 Objectionary.com
+# SPDX-License-Identifier: MIT
+---
+sheets:
+  - /org/eolang/lints/names/object-does-not-match-filename.xsl
+asserts:
+  - /defects[count(defect[@severity='warning'])=0]
+document: |
+  <program source=".eoc/2-pull/org/eolang/sys/posix.eo">
+    <metas>
+      <meta>
+        <head>package</head>
+        <tail>org.eolang.sys</tail>
+        <part>org.eolang.sys</part>
+      </meta>
+    </metas>
+    <objects>
+      <o line="2" name="posix" pos="0"/>
+    </objects>
+  </program>
diff --git a/src/test/resources/org/eolang/lints/packs/single/object-does-not-match-filename/catches-bad-name-relatively.yaml b/src/test/resources/org/eolang/lints/packs/single/object-does-not-match-filename/catches-bad-name-relatively.yaml
new file mode 100644
index 000000000..c31a8e12b
--- /dev/null
+++ b/src/test/resources/org/eolang/lints/packs/single/object-does-not-match-filename/catches-bad-name-relatively.yaml
@@ -0,0 +1,21 @@
+# SPDX-FileCopyrightText: Copyright (c) 2016-2025 Objectionary.com
+# SPDX-License-Identifier: MIT
+---
+sheets:
+  - /org/eolang/lints/names/object-does-not-match-filename.xsl
+asserts:
+  - /defects[count(defect[@severity='warning'])=1]
+  - /defects/defect[@line='2']
+document: |
+  <program source=".eoc/2-pull/org/eolang/broken/posix.eo">
+    <metas>
+      <meta>
+        <head>package</head>
+        <tail>org.eolang.sys</tail>
+        <part>org.eolang.sys</part>
+      </meta>
+    </metas>
+    <objects>
+      <o line="2" name="posix" pos="0"/>
+    </objects>
+  </program>
diff --git a/src/test/resources/org/eolang/lints/packs/single/object-does-not-match-filename/catches-bad-name-with-many-dots.yaml b/src/test/resources/org/eolang/lints/packs/single/object-does-not-match-filename/catches-bad-name-with-many-dots.yaml
new file mode 100644
index 000000000..43efeedb6
--- /dev/null
+++ b/src/test/resources/org/eolang/lints/packs/single/object-does-not-match-filename/catches-bad-name-with-many-dots.yaml
@@ -0,0 +1,21 @@
+# SPDX-FileCopyrightText: Copyright (c) 2016-2025 Objectionary.com
+# SPDX-License-Identifier: MIT
+---
+sheets:
+  - /org/eolang/lints/names/object-does-not-match-filename.xsl
+asserts:
+  - /defects[count(defect[@severity='warning'])=1]
+  - /defects/defect[@line='2']
+document: |
+  <program source="foo/bar/foo.seq.bar.baz.xyz.eo">
+    <metas>
+      <meta>
+        <head>package</head>
+        <tail>foo.bar</tail>
+        <part>foo.bar</part>
+      </meta>
+    </metas>
+    <objects>
+      <o line="2" name="foo" pos="0"/>
+    </objects>
+  </program>
diff --git a/src/test/resources/org/eolang/lints/packs/single/object-does-not-match-filename/catches-bad-name-with-package.yaml b/src/test/resources/org/eolang/lints/packs/single/object-does-not-match-filename/catches-name-with-dots.yaml
similarity index 74%
rename from src/test/resources/org/eolang/lints/packs/single/object-does-not-match-filename/catches-bad-name-with-package.yaml
rename to src/test/resources/org/eolang/lints/packs/single/object-does-not-match-filename/catches-name-with-dots.yaml
index 3c3556abb..7932e4ab6 100644
--- a/src/test/resources/org/eolang/lints/packs/single/object-does-not-match-filename/catches-bad-name-with-package.yaml
+++ b/src/test/resources/org/eolang/lints/packs/single/object-does-not-match-filename/catches-name-with-dots.yaml
@@ -7,15 +7,15 @@ asserts:
   - /defects[count(defect[@severity='warning'])=1]
   - /defects/defect[@line='2']
 document: |
-  <program source="app/app/app.eo">
+  <program source="foo/bar/seq.some.eo">
     <metas>
       <meta>
         <head>package</head>
-        <tail>app</tail>
-        <part>app</part>
+        <tail>foo.bar</tail>
+        <part>foo.bar</part>
       </meta>
     </metas>
     <objects>
-      <o line="2" name="app" pos="0"/>
+      <o line="2" name="seq" pos="0"/>
     </objects>
   </program>


Reviews:
user: login: maxonfjvipon
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-04-04T09:35:15Z
user: login: maxonfjvipon
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-04-04T10:50:18Z
user: login: h1alexbel
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-04T10:52:30Z
user: login: h1alexbel
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-04T10:59:20Z
user: login: maxonfjvipon
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-04-04T19:30:54Z
user: login: maxonfjvipon
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-07T15:25:43Z
user: login: h1alexbel
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-07T16:24:46Z
user: login: maxonfjvipon
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-08T13:19:43Z
user: login: yegor256
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-08T15:56:09Z

Review comments:
diff_hunk: @@ -28,7 +28,7 @@
       </xsl:choose>
     </xsl:variable>
     <xsl:variable name="source" as="xs:string" select="replace($opath, '\.', '/')"/>
-    <xsl:if test="not(starts-with(/program/@source, concat($source, '.')))">
+    <xsl:if test="not(contains(/program/@source, concat($source, '.')))">
path: src/main/resources/org/eolang/lints/names/object-does-not-match-filename.xsl
user: login: maxonfjvipon
body: @h1alexbel I didn't get why we need `concat($source, '.')`. Maybe it should be `concat($source, '.eo')`?
created_at: 2025-04-04T09:34:57Z
author_association: MEMBER
diff_hunk: @@ -28,7 +28,7 @@
       </xsl:choose>
     </xsl:variable>
     <xsl:variable name="source" as="xs:string" select="replace($opath, '\.', '/')"/>
-    <xsl:if test="not(starts-with(/program/@source, concat($source, '.')))">
+    <xsl:if test="not(contains(/program/@source, concat($source, '.eo')) or contains(/program/@source, concat($source, '.phi')))">
path: src/main/resources/org/eolang/lints/names/object-does-not-match-filename.xsl
user: login: maxonfjvipon
body: @h1alexbel I just realized that there are many ways we can get XMIR from (bytecode for example). So it's not really correct to specify a file extension here. Maybe variant with `.` (dot) wasn't such bad. But it's not really strong because file path may contain dots in file name: `org/eolang/seq.some.eo`. In such case `concat($source, '.')` produces false positive
created_at: 2025-04-04T10:50:01Z
author_association: MEMBER
diff_hunk: @@ -28,7 +28,7 @@
       </xsl:choose>
     </xsl:variable>
     <xsl:variable name="source" as="xs:string" select="replace($opath, '\.', '/')"/>
-    <xsl:if test="not(starts-with(/program/@source, concat($source, '.')))">
+    <xsl:if test="not(contains(/program/@source, concat($source, '.eo')) or contains(/program/@source, concat($source, '.phi')))">
path: src/main/resources/org/eolang/lints/names/object-does-not-match-filename.xsl
user: login: h1alexbel
body: @maxonfjvipon got it. But we can't have `org/eolang/seq.some.eo` at this point - it will be translated to: `org/eolang/seq/some.eo`
created_at: 2025-04-04T10:52:30Z
author_association: MEMBER
diff_hunk: @@ -28,7 +28,7 @@
       </xsl:choose>
     </xsl:variable>
     <xsl:variable name="source" as="xs:string" select="replace($opath, '\.', '/')"/>
-    <xsl:if test="not(starts-with(/program/@source, concat($source, '.')))">
+    <xsl:if test="not(contains(/program/@source, concat($source, '.eo')) or contains(/program/@source, concat($source, '.phi')))">
path: src/main/resources/org/eolang/lints/names/object-does-not-match-filename.xsl
user: login: h1alexbel
body: @maxonfjvipon also, how about we will check all other extensions XMIR can be get from, like we did with `.phi` and `.eo`. Thus, both lint check and its message will be synchronized and controlled. WDYT?
created_at: 2025-04-04T10:59:20Z
author_association: MEMBER
diff_hunk: @@ -28,7 +28,7 @@
       </xsl:choose>
     </xsl:variable>
     <xsl:variable name="source" as="xs:string" select="replace($opath, '\.', '/')"/>
-    <xsl:if test="not(starts-with(/program/@source, concat($source, '.')))">
+    <xsl:if test="not(contains(/program/@source, concat($source, '.eo')) or contains(/program/@source, concat($source, '.phi')))">
path: src/main/resources/org/eolang/lints/names/object-does-not-match-filename.xsl
user: login: maxonfjvipon
body: @h1alexbel there are so many of them, we can't cover all... Imagine your `/program/@source` is `foo/bar/seq.some.eo`, your `$source` is `foo/bar/seq` (since it's built from package and object name). Then there's a mismatch between file name and object name
created_at: 2025-04-04T19:30:51Z
author_association: MEMBER
diff_hunk: @@ -28,7 +28,7 @@
       </xsl:choose>
     </xsl:variable>
     <xsl:variable name="source" as="xs:string" select="replace($opath, '\.', '/')"/>
-    <xsl:if test="not(contains(/program/@source, concat($source, '.')))">
+    <xsl:if test="not(contains(/program/@source, concat($source, concat('.', substring-after(substring-after(/program/@source, '.'), '.')))))">
path: src/main/resources/org/eolang/lints/names/object-does-not-match-filename.xsl
user: login: maxonfjvipon
body: @h1alexbel what if file name ends with `foo.seq.bar.baz.xyz.eo`?
created_at: 2025-04-07T15:25:38Z
author_association: MEMBER
diff_hunk: @@ -28,7 +28,7 @@
       </xsl:choose>
     </xsl:variable>
     <xsl:variable name="source" as="xs:string" select="replace($opath, '\.', '/')"/>
-    <xsl:if test="not(contains(/program/@source, concat($source, '.')))">
+    <xsl:if test="not(contains(/program/@source, concat($source, concat('.', substring-after(substring-after(/program/@source, '.'), '.')))))">
path: src/main/resources/org/eolang/lints/names/object-does-not-match-filename.xsl
user: login: h1alexbel
body: @maxonfjvipon it will fail with the following message:

```text
It is expected that the "foo.bar.foo" object is coming from either "foo/bar/foo.eo" or "foo/bar/foo.phi" file, however its "program/@source" points to "foo/bar/foo.seq.bar.baz.xyz.eo"
```

added new test case
created_at: 2025-04-07T16:24:46Z
author_association: MEMBER

PR comments:
user: login: h1alexbel
created_at: 2025-04-04T09:29:04Z
body: @maxonfjvipon take a look, please
author_association: MEMBER
user: login: h1alexbel
created_at: 2025-04-04T10:22:50Z
body: @maxonfjvipon updated. Take a look, please
author_association: MEMBER
user: login: h1alexbel
created_at: 2025-04-07T08:38:52Z
body: @maxonfjvipon updated. Take a look, please
author_association: MEMBER
user: login: maxonfjvipon
created_at: 2025-04-07T12:41:02Z
body: @h1alexbel please read this one more time:
> Imagine your `/program/@source` is `foo/bar/seq.some.eo`, your `$source` is `foo/bar/seq` (since it's built from package and object name). Then there's a mismatch between file name and object name

In such case if you do just `not(contains(/program/@source, $source))` - it returns false and there's no defect added, but it's not correct because file name is `foo/bar/seq.some.eo`, but package + object name is `foo.bar.seq`. False negative. There's an obvious defect, object name does not match to file name.


author_association: MEMBER
user: login: h1alexbel
created_at: 2025-04-07T14:37:36Z
body: @maxonfjvipon updated. Take a look, please
author_association: MEMBER
user: login: h1alexbel
created_at: 2025-04-08T13:22:16Z
body: @yegor256 take a look, please
author_association: MEMBER
user: login: 0crat
created_at: 2025-04-09T07:14:24Z
body: @maxonfjvipon 🌟 Great job on the review! You've earned +4 points, boosting your balance to [+112](https://www.eolang.org/zerocracy/objectionary-vitals.html). Keep up the excellent work!

<!-- Thanks for the review! You've earned +4 points.  Your running balance is [+112](https://www.eolang.org/zerocracy/objectionary-vitals.html).  -->
author_association: NONE
user: login: 0crat
created_at: 2025-04-09T10:14:39Z
body: @yegor256 Thanks for the review! You've earned +4 points, the minimum reward as per our policy. While there weren't enough comments or hits-of-code to boost your bonus this time, remember that each review contributes to your skills and our project's quality. Your running balance is now [-848](https://www.eolang.org/zerocracy/objectionary-vitals.html). Keep up the good work, and aim for more detailed reviews to maximize your future bonuses! 💻👍

<!-- Thanks for the review! You've earned +4 points.  Your running balance is [-848](https://www.eolang.org/zerocracy/objectionary-vitals.html).  -->
author_association: NONE
user: login: 0crat
created_at: 2025-04-14T16:16:50Z
body: @h1alexbel Thank you for your contribution! We appreciate your effort, but we need to adjust the points awarded based on our team policy. The initial +4 points have been reduced due to factors such as the number of hits-of-code, comments, and reviews. Please review our [policy](https://www.yegor256.com/2018/03/06/speed-vs-quality.html) for more details on point calculations. Your current running balance stands at [+124](https://www.eolang.org/zerocracy/objectionary-vitals.html). We encourage you to keep contributing and aim for more comprehensive code reviews in the future. 🚀

<!-- Thanks for the contribution! You've earned +4 points. Please, [keep](https://www.yegor256.com/2018/03/06/speed-vs-quality.html) them coming. Your running balance is [+124](https://www.eolang.org/zerocracy/objectionary-vitals.html).  -->
author_association: NONE