-
Notifications
You must be signed in to change notification settings - Fork 157
feat: Add dependency analysis to fix command #4035
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
base: main
Are you sure you want to change the base?
Conversation
igor-ramazanov
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this will work with a code like1:
package foo // imports `foo.*`
package bar // imports `foo.bar.*`
import baz.* // imports `foo.bar.baz.*`
Gedochao
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: tests aren't working, commented on why.
| val output = os.proc( | ||
| TestUtil.cli, | ||
| "fix", | ||
| ".", | ||
| "--with-unused-deps", | ||
| extraOptions, | ||
| enableRulesOptions(enableScalafix = false) | ||
| ) | ||
| .call(cwd = root, mergeErrIntoOut = true).out.trim() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| val output = os.proc( | |
| TestUtil.cli, | |
| "fix", | |
| ".", | |
| "--with-unused-deps", | |
| extraOptions, | |
| enableRulesOptions(enableScalafix = false) | |
| ) | |
| .call(cwd = root, mergeErrIntoOut = true).out.trim() | |
| val output = os.proc( | |
| TestUtil.cli, | |
| "fix", | |
| ".", | |
| "--power", | |
| "--with-unused-deps", | |
| extraOptions, | |
| enableRulesOptions(enableScalafix = false) | |
| ) | |
| .call(cwd = root, mergeErrIntoOut = true).out.trim() |
this won't even run without the --power option, as --with-unused-deps is experimental... Please make sure to run the tests locally before pushing them to the CI.
| val output = os.proc( | ||
| TestUtil.cli, | ||
| "fix", | ||
| ".", | ||
| "--with-explicit-deps", | ||
| extraOptions, | ||
| enableRulesOptions(enableScalafix = false) | ||
| ) | ||
| .call(cwd = root, mergeErrIntoOut = true).out.trim() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| val output = os.proc( | |
| TestUtil.cli, | |
| "fix", | |
| ".", | |
| "--with-explicit-deps", | |
| extraOptions, | |
| enableRulesOptions(enableScalafix = false) | |
| ) | |
| .call(cwd = root, mergeErrIntoOut = true).out.trim() | |
| val output = os.proc( | |
| TestUtil.cli, | |
| "fix", | |
| ".", | |
| "--power", | |
| "--with-explicit-deps", | |
| extraOptions, | |
| enableRulesOptions(enableScalafix = false) | |
| ) | |
| .call(cwd = root, mergeErrIntoOut = true).out.trim() |
Same as in the other test, please test it locally first.
modules/integration/src/test/scala/scala/cli/integration/FixBuiltInRulesTestDefinitions.scala
Outdated
Show resolved
Hide resolved
|
@Gedochao : Apologies for the oversight, I missed that these experimental options require the |
|
@krrish175-byte they still seem to fail on the CI... |
|
@Gedochao: Hi! I have addressed the failing dependency analysis tests with the latest commit. Changes:
Fixed detect missing explicit dependencies test:
Waiting for next review! |
Code Review – Key FindingsSummary: Issues
|
Gedochao
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left some comments.
BTW it seems formatting is still failing.
| val possiblePackages = Set( | ||
| org.replace('-', '.').toLowerCase, | ||
| name.replace('-', '.').toLowerCase, | ||
| simpleName.replace('-', '.').toLowerCase, | ||
| s"$org.$name".replace('-', '.').toLowerCase, | ||
| s"$org.$simpleName".replace('-', '.').toLowerCase | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if guesswork such as this is necessary, when you could inspect what package names are actually there, in the dependency
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bump
modules/cli/src/main/scala/scala/cli/commands/fix/DependencyAnalyzer.scala
Outdated
Show resolved
Hide resolved
modules/cli/src/main/scala/scala/cli/commands/fix/FixOptions.scala
Outdated
Show resolved
Hide resolved
modules/cli/src/main/scala/scala/cli/commands/fix/FixOptions.scala
Outdated
Show resolved
Hide resolved
|
@Gedochao : Thanks for the comments. I have pushed the changes. I ended up ditching the regex approach entirely for a simple state machine tokenizer. It’s much more robust now and correctly ignores imports or usages inside strings and block comments. I also added some caching so we are not re-reading files multiple times, which should help with performance. As per your suggestion, I also moved the orchestration logic into BuiltInRules (cleaning up Fix.scala quite a bit) and renamed the flags to --check-unused-deps and --check-explicit-deps. Let me know what you think. Thanks again for being patient and reviewing my changes. |
4d53bc5 to
9cd1545
Compare
| def javaSemanticdb = "0.10.0" | ||
| def javaClassName = "0.1.9" | ||
| def bloop = "2.0.17" | ||
| def bloop = "2.0.18" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This bump is rather far from this simple, and out of scope here.
There are some tests are hanging for this Bloop version.
| val isCI: Boolean = System.getenv("CI") != null | ||
| val isM1: Boolean = sys.props.get("os.arch").contains("aarch64") | ||
| val cliPath: String = sys.props("test.scala-cli.path") | ||
| val cliPath: String = sys.props.getOrElse("test.scala-cli.path", "scala-cli") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In what circumstances is this necessary?
| object TestUtil { | ||
|
|
||
| val cliKind: String = sys.props("test.scala-cli.kind") | ||
| val cliKind: String = sys.props.getOrElse("test.scala-cli.kind", "jvm") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, in what circumstances is this necessary?
| logger.message("Running built-in rules...") | ||
| if options.check then | ||
| // TODO support --check for built-in rules: https://github.com/VirtusLab/scala-cli/issues/3423 | ||
| // built-in rules don't support --check yet |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the old comment is more informative, as it references a ticket and is caught by tooling because of the TODO word
| ) | ||
| val scopedSources = crossSources.scopedSources(sharedOptions).orExit(logger) | ||
| val sources = scopedSources.sources( | ||
| Scope.Main, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A potential follow-up would be to respect the --test flag and if it's enabled, include the test scope in the analysis as well.
Doesn't have to be in this PR, can create a ticket to track it instead.
| // Regex to extract imports from a code line (already stripped of strings/comments) | ||
| private val importPattern: Regex = """^\s*import\s+([^\s{(]+).*""".r | ||
| // Regex to extract simple package/object usages | ||
| private val usagePattern: Regex = | ||
| """\b([a-z][a-zA-Z0-9_]*+(?:\.[a-z][a-zA-Z0-9_]*+)*+)\b""".r |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I ended up ditching the regex approach entirely for a simple state machine tokenizer.
Uh... As far as I can tell, these regex patterns are still being used for detecing usage & imports.
Add Dependency Analysis to Fix Command
Overview
Implements dependency analysis for
scala-cli fixcommand to detect unused and missing explicit dependencies.Features
--with-unused-deps: Reports declared dependencies not directly imported--with-explicit-deps: Reports transitive dependencies used without explicit declarationChanges
DependencyAnalyzer.scalawith import analysisFixOptionsand integrated intoFix.scalafix.mddocumentationNotes
Closing issue
closes #3873