Skip to content

Fix #47753 - Organize imports removes type imports that are only referenced in @link (jsdoc) #47824

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

Merged
merged 19 commits into from
Apr 29, 2022

Conversation

komyg
Copy link
Contributor

@komyg komyg commented Feb 9, 2022

Fixes #47753, #47558

@typescript-bot typescript-bot added the For Backlog Bug PRs that fix a backlog bug label Feb 9, 2022
@komyg komyg marked this pull request as ready for review February 17, 2022 19:48
Copy link
Member

@sandersn sandersn left a comment

Choose a reason for hiding this comment

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

This works, but it should actually happen in FindAllReferences, and use the normal FindAllReference machinery.

@sandersn sandersn self-assigned this Feb 24, 2022
@komyg
Copy link
Contributor Author

komyg commented Mar 4, 2022

Hi @sandersn, thanks for the review. I will update the PR accordingly, but I just need a little time, because things are kind of crazy right now.

@DanielSWolf
Copy link

This is related to the larger issue that TS considers @linked imports as unused, as described in #47558. I wonder whether it might make more sense to solve the root cause rather than fixing this specific symptom.

@komyg
Copy link
Contributor Author

komyg commented Mar 16, 2022

Hey @DanielSWolf, I think I found the root cause of both problems in the compiler. I will try to solve both issues.

@DanielSWolf
Copy link

@komyg That would be great! The current behavior makes it really hard to write links that consistently work.

@komyg komyg requested a review from sandersn March 30, 2022 09:57
@komyg
Copy link
Contributor Author

komyg commented Mar 30, 2022

Hi @sandersn, I've fixed the FindAllReference function as you requested and all test cases for organize imports have passed.

However, I also got an error for a JSDoc baseline that I didn't understand, so I don't know if I inadvertently broke something or if the fix also corrected it.

Could you please review the PR again?

@sandersn
Copy link
Member

The JSDoc baseline change shows that the link target is now different for imports. For this code:

// @filename: one.ts
export class C { }
// @filename: two.ts
import { C } from './one'
/** {@link C} */
var x = 1

{@link C} used to link to the actual declaration of C in one.ts (export class C). Now, because aliases are allowed, it returns C from two.ts instead: import { C }. In effect, it stops at the alias instead of following it like it used to.

That's not right, so this PR needs more work. I'll see if I can figure out what.

@sandersn
Copy link
Member

sandersn commented Mar 30, 2022

WIthout debugging the newly-failing test case, I don't know how the old code resolves C to the original class. What tests fail if you change /*dontResolveAlias*/ !isJSDoc to /*dontResolveAlias*/ false? It's possible that it doesn't actually ever need to be true.

@komyg
Copy link
Contributor Author

komyg commented Mar 31, 2022

WIthout debugging the newly-failing test case, I don't know how the old code resolves C to the original class. What tests fail if you change /*dontResolveAlias*/ !isJSDoc to /*dontResolveAlias*/ false? It's possible that it doesn't actually ever need to be true.

Well, I since the dontResolveAlias param is !JSDoc it will probably be always false.

I think that the problem lies in the resolveEntityName function. I've followed its execution through the debugger, and it resolves the symbol for the {@link ZodType}, however at its end there is the following conditional:

return (symbol.flags & meaning) || dontResolveAlias ? symbol : resolveAlias(symbol);

By setting dontResolveAlias to true or by adding the Symbol.Alias meaning when calling resolveEntityName, we fall into the first part of the ternary and just return the symbol we found. Which in our case means the import statement that is referenced by the JSDoc {@link ZodType} and the import from the C type in the failing test.

Since the original code did not have the Symbol.Alias in the meaning and dontResolveAlias was false, we fell into the second part of the ternary, which meant we tried to resolve our JSDoc {@link ZodType} import alias twice, resulting in an unknown type and the bug that was reported.

A possible solution would be for us to find what is unique about each situation to make a better choice whether to set dontResolveAlias to true or false or to update the meaning accordingly. But I don't know if this is possible, since we have two different use cases of the same {@link} element.

@sandersn
Copy link
Member

So: in the old code, jsdoc references, like {@link C} in my recent simplified example, do resolve aliases? Then why doesn't calling resolveAlias on import { C } return export class C? It seems like that should succeed.

You are right, though: for the purpose of organise imports, we don't actually want to resolve aliases. It's enough to know that {@link C} resolves to an imported alias, even if it's an incorrect import. But the code is so distant from the actual name resolution, I don't see how you could differentiate that use from the normal find-all-references use.

@komyg
Copy link
Contributor Author

komyg commented Mar 31, 2022

How can we fix this, then? It seems that organize imports is an "edgier" case than resolving aliases that you mentioned, so maybe we could do some logic outside the findAllReferences code.

My original solution did not rely on the findAllReferences code. Maybe we can repurpose that somehow or add it to the checker code? What do you think?

Note: this is the commit in which I erased my previous attempt at a solution.

@sandersn
Copy link
Member

sandersn commented Apr 5, 2022

I looked again at your test cases, and the first problem is that they import things that aren't present in the program. The test cases need to have multiple files. See jsdocLink3.ts for an example.

It might be nice if organise imports didn't rely on find-all-refs, and therefore on the the target of the alias being defined.

@sandersn
Copy link
Member

sandersn commented Apr 5, 2022

From manual testing, I can see that find-all-references doesn't work correctly for @link references, so it should be fixed too. I still believe that fixing find-all-refs will fix organise-imports. Here's the test I used:

// @filename:ex.d.ts
export type A = 1 | 2
export type B = 3 | 4
// @filename: main.ts
import { A, B } from './ex'
/** {@link A} */
class C {
  b: B = 3
}

Find-all-refs on main's B finds 3 refs; find-all-refs on main's A only finds the import alias.

@sandersn
Copy link
Member

sandersn commented Apr 6, 2022

WIthout debugging the newly-failing test case, I don't know how the old code resolves C to the original class. What tests fail if you change /dontResolveAlias/ !isJSDoc to /dontResolveAlias/ false? It's possible that it doesn't actually ever need to be true.

I tested this locally and it is the solution. It does change the baselines for jsdocLink3, but I'm pretty sure those were wrong, because I manually tested the test code in jsdocLink3 and it behaves right with false, wrong with !isJSDoc. When I wrote the code, I didn't understand how it was supposed to follow (or not) aliases.

@komyg
Copy link
Contributor Author

komyg commented Apr 7, 2022

Hey @sandersn, from your comments, I understood that the solution that we've implemented here is correct, and I just have to make the following changes:

  1. Update my test cases, so that they have types defined in multiple files, as per your example and the jsdocLink3.ts test.

  2. Change the call to findAllReferences so that the dontResolveAlias is always false, but also keep the change that I made that adds Symbol.Alias to the meaning.

  3. Update the jsdocLink3.ts baseline.

  4. Add the test case that you created above.

Am I right?

@sandersn
Copy link
Member

sandersn commented Apr 7, 2022

Everything is right except skipping the addition of Symbol.Alias. You only need dontResolveAlias: false.

Edit: Also I think jsdoclink3 covers enough that you don't need to add the test case in (4).

@sandersn
Copy link
Member

sandersn commented Apr 7, 2022

Sorry, true. dontResolveAlias should be true.
(Just like it is a few lines below in the isTypeReferenceIdentifier branch)

@komyg
Copy link
Contributor Author

komyg commented Apr 8, 2022

Hi @sandersn, I've done the changes we've discussed. Could you review again, please?

@komyg
Copy link
Contributor Author

komyg commented Apr 26, 2022

Hi @sandersn, sorry to bother you. I've done the changes we've discussed. Could you review again, please?

@sandersn sandersn merged commit fd06132 into microsoft:main Apr 29, 2022
@sandersn
Copy link
Member

Woops, sorry this fell off my list. Thanks for your patience!

@komyg
Copy link
Contributor Author

komyg commented Apr 29, 2022

No worries! I am happy to have contributed!

@komyg komyg deleted the fix/47753-organize-imports-with-jsdoc branch May 26, 2022 12:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
For Backlog Bug PRs that fix a backlog bug
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

Organize imports removes type imports that are only referenced in @link (jsdoc)
4 participants