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

Detect filename is not compliant with PascalCase convention #1117

Merged
merged 14 commits into from May 31, 2022

Conversation

bedla
Copy link
Contributor

@bedla bedla commented Mar 21, 2021

Hi,
I found that FilenameRule does not check PascalCase convention mentioned in https://kotlinlang.org/docs/coding-conventions.html#source-file-names
What do you think?
Thx
Ivos

@bedla bedla force-pushed the filename-pascalcase branch 3 times, most recently from 3308b6c to 3f81397 Compare March 21, 2021 16:32
@shashachu
Copy link
Contributor

hi @bedla thanks for the PR. Reading the Kotlin docs, my take is that filenames should always conform to Pascal case, regardless of the contents. Is that not also your understanding?

@bedla
Copy link
Contributor Author

bedla commented Aug 5, 2021

Yep, from my understanding PascalCase is only viable name of the file.

I have rebased to current master.

@bedla
Copy link
Contributor Author

bedla commented Nov 7, 2021

I have update PR to current master

@paul-dingemans
Copy link
Collaborator

The code seems unnecessary complex to me. Basically, I would expect it do the following:

  1. Determine the number of top level declarations
  2. Determine the name of the first class
  3. If one top level declaration was found and the first class name is not null than the name of file should be identical to the name of the class followed by ".kt". Otherwise check if the filename adheres to the the PascalCase convention.

@paul-dingemans
Copy link
Collaborator

@bedla Do you plan to follow up on my remarks on this PR?

@bedla
Copy link
Contributor Author

bedla commented Feb 25, 2022

Yes, I will. Sorry for being late, I had some rush in work.

@bedla
Copy link
Contributor Author

bedla commented Feb 28, 2022

Just pushed requested changes. I have rewritten original logic to make it more explicit from my point of view. Pls, take a look. Thx

paul-dingemans added a commit to bedla/ktlint that referenced this pull request Mar 2, 2022
@paul-dingemans
Copy link
Collaborator

paul-dingemans commented Mar 2, 2022

Please see commits for changes. The logic changes that I wanted was too difficult to describe in words, so I choose to implement it to check whether it worked.

Code and tests did not comply with KtLints code formatting rules. So please ensure to also run the gradle "ktlint".

Linting did pint out that also four KtLint files do not comply with the naming standard of this rule. I changed one of them, for which I had not objections to change it. But remaining three files, made me doubt about the rule (https://github.com/pinterest/ktlint/runs/5397719779?check_suite_focus=true):

ktlint/ktlint/src/main/kotlin/com/pinterest/ktlint/internal/CommandLineExt.kt:1:1: Extension function printHelpOrVersionUsage should be declared in a file named printHelpOrVersionUsage.kt (cannot be auto-corrected) (filename)
ktlint/ktlint/src/main/kotlin/com/pinterest/ktlint/internal/ByteArrayExt.kt:1:1: Extension property hex should be declared in a file named hex.kt (cannot be auto-corrected) (filename)
ktlint/buildSrc/src/main/kotlin/ToolchainForTests.kt:1:1: All elements with receiver Project should be declared in a file named Project.kt (cannot be auto-corrected) (filename)

https://kotlinlang.org/docs/coding-conventions.html#source-file-names is not very clear about files contain 1 toplevel function which is not a class. The current implementation enforces PascalName with the name of that toplevel function while the coding convention says that you should choose a name describing what the file contains and use uppercase PascalNotation. Based on that, and the example given above, I think this should be changed. Please let me know what you think of it.

@bedla
Copy link
Contributor Author

bedla commented Mar 3, 2022

I see. Let me check it and add more tests. I will be at vacations next week, so result of my changes will be available after.

bedla and others added 4 commits March 19, 2022 19:33
Remove intermediary classes and functions which add complexity but
do no assist in understanding the code more easily (although this
is subjective of course).
Replace for-loops to "List.foreach { ... }"
…rsion" to "KtlintVersion.kt" with method "ktlintVersion" so that file adheres to PascalNaming convention in the modified FilenameRule.
@bedla
Copy link
Contributor Author

bedla commented Mar 19, 2022

Hmm, interesting "edge cases".

  • CommandLineExt & ByteArrayExt - actually I like more this convention with DescriptiveName+Ext.kt ... could we extends original Kotlin's convention with exception to Ext suffix in this case? It seems reasonable to me.
  • ToolchainForTests - I agree with rule that filename should be Project.kt, but on the other hand there should be possible to add exceptions from the rule.

What do you thing?

@paul-dingemans
Copy link
Collaborator

  • CommandLineExt & ByteArrayExt - actually I like more this convention with DescriptiveName+Ext.kt ... could we extends original Kotlin's convention with exception to Ext suffix in this case? It seems reasonable to me.
  • ToolchainForTests - I agree with rule that filename should be Project.kt, but on the other hand there should be possible to add exceptions from the rule.

I think we should handle both cases similar. If a file does not contain any class and has filename ending with "Ext" or "Extension" and is following the PascalCase convention then there should be no violation about the file name. Would that be feasible?

@bedla
Copy link
Contributor Author

bedla commented Apr 9, 2022

Ok, I will take a look at it. I have also created https://youtrack.jetbrains.com/issue/KT-51919 to know Kotlin creator's opinion on this edge case (I do not know if they will join discussion, but why not try :) ).

@bedla
Copy link
Contributor Author

bedla commented Apr 17, 2022

Mr. Elizarov responded in Youtrack issue. I think that he is right.

But it means that should remove checks around allOfSameReceiver. And also his response invalidate what was mentioned in original code https://github.com/pinterest/ktlint/blob/0.45.2/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/FilenameRule.kt#L67 ... If I get it correctly, it is because of situation when we want to name file with same name like Receiver name, when we have only extensions in the file. But this one is not explicitly noted in lang-docs.

What do you think?
Do you know why exactly is that note at line 67?

@paul-dingemans
Copy link
Collaborator

It looks like that https://github.com/pinterest/ktlint/blob/0.45.2/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/FilenameRule.kt#L67 is just the example of a file in which extension functions have the same receiver. There can be a good reason to group such functions together in one file, but I agree that we should not force that the file is named after the receiver. It would be enough to enforce the upper PascalCase convention here.

When applying above, a project can still use a filename like SomeReceiverExt.kt as it adheres to the upper PascalCase convention. But the other way around, a file which only contain extension functions on the Foo receiver does not necessarily to be named as Foo.kt but could be named as RedundantBar.kt.

@paul-dingemans
Copy link
Collaborator

@bedla Do you have some time to finish up the issue? I expect a new release to be build in a couple of weeks.

@bedla
Copy link
Contributor Author

bedla commented May 22, 2022

ahh, sorry I missed your previous comment. let me re-check it.

@bedla
Copy link
Contributor Author

bedla commented May 22, 2022

I have removed that all-top-level receivers exception, but it still fails on:

C:\Users\ivo.smid\IdeaProjects\ktlint\ktlint\src\main\kotlin\com\pinterest\ktlint\internal\ByteArrayExt.kt:1:1: Extension property hex should be declared in a file named hex.kt (cannot be auto-corrected) (filename)
C:\Users\ivo.smid\IdeaProjects\ktlint\ktlint\src\main\kotlin\com\pinterest\ktlint\internal\CommandLineExt.kt:1:1: Extension function printHelpOrVersionUsage should be declared in a file named printHelpOrVersionUsage.kt (cannot be auto-corrected) (filename)

So if I get it correctly, I should remove also this sub-rule for Extension functions & Extension property? What do you think?

@paul-dingemans
Copy link
Collaborator

I have removed that all-top-level receivers exception, but it still fails on:

C:\Users\ivo.smid\IdeaProjects\ktlint\ktlint\src\main\kotlin\com\pinterest\ktlint\internal\ByteArrayExt.kt:1:1: Extension property hex should be declared in a file named hex.kt (cannot be auto-corrected) (filename)
C:\Users\ivo.smid\IdeaProjects\ktlint\ktlint\src\main\kotlin\com\pinterest\ktlint\internal\CommandLineExt.kt:1:1: Extension function printHelpOrVersionUsage should be declared in a file named printHelpOrVersionUsage.kt (cannot be auto-corrected) (filename)

So if I get it correctly, I should remove also this sub-rule for Extension functions & Extension property? What do you think?

Yes they should be removed as well for two reasons:

  • The rule forces the name the file to start with a lowercase character as that is the convention for function and property names
  • Only when the file contains exactly one class, then the file should be named identical to that class name. In all other cases a descriptive name has to be chosen.

= added 2 commits May 24, 2022 20:01
@paul-dingemans
Copy link
Collaborator

There is one more file ("RuleExtension") which needs a better name. I will rename that file in the future as it is heavily refactored in another open PR.

bedla and others added 5 commits May 26, 2022 07:13
…/interface then the file should be named same as that class/interface. In all other cases a descriptive PascalCase name should be used.

Modifications below were required to comply Ktlint code with change above:
* Extract class KtlintCommandLine from file "Main.kt" to separate class
* Rename files "main.kt" in Test resources to "Main.kt"
* Rename file "BaselineSupport" to "CurrentBaseline" as the class CurrentBaseline is the dominant result of the file
* Rename file "SuppressedRegionLocator" to "SuppressionLocatorBuilder". Functions had to be wrapped in an object as otherwise the file had to be named identical to the private data class SuppressionHint
# Conflicts:
#	ktlint/src/main/kotlin/com/pinterest/ktlint/Main.kt
@paul-dingemans paul-dingemans merged commit 1b0e535 into pinterest:master May 31, 2022
@paul-dingemans paul-dingemans added this to the 0.46.0 milestone May 31, 2022
@vanniktech
Copy link
Contributor

Is it desired that with this change, the following file:

package com.vanniktech.feature.boardmoney.data

import app.cash.sqldelight.adapter.primitive.IntColumnAdapter
import app.cash.sqldelight.db.SqlDriver
import com.vanniktech.boardmoney.QueryWrapper

internal fun createQueryWrapper(driver: SqlDriver) = QueryWrapper(
  driver = driver,
  boardMoneyPlayerAdapter = BoardMoneyPlayer.Adapter(
    colorAdapter = IntColumnAdapter,
  ),
)

gets linted with the following:

/Users/niklas/dev/GitHub/vanniktech/feature-board-money/src/commonMain/kotlin/com/vanniktech/feature/boardmoney/data/Db.kt:1:1: File 'Db.kt' contains a single top level declaration and should be named 'CreateQueryWrapper.kt' (cannot be auto-corrected) (filename)

That does not seem right to me.

@paul-dingemans
Copy link
Collaborator

It is debatable. According to the Kotlin styleguide a meaningfull name should be chosen. Depending on the context, Db.kt and CreateQueryWrapper.kt can be meaningfull names. On top of the style guide, ktlint enforces the file to be named after the top level declaration if the file only contains one top level declaration. In general, I see no added value for having a different filename in such cases.

@vanniktech
Copy link
Contributor

If a Kotlin file contains a single class or interface (potentially with related top-level declarations), its name should be the same as the name of the class, with the .kt extension appended. It applies to all types of classes and interfaces. If a file contains multiple classes, or only top-level declarations, choose a name describing what the file contains, and name the file accordingly. Use upper camel case with an uppercase first letter (also known as Pascal case), for example, ProcessDeclarations.kt.

I'd agree for a Class / Interface, but not for a given function (as in my case).

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.

None yet

4 participants