Skip to content

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

Merged
Nathan Shively-Sanders (sandersn) merged 19 commits into
microsoft:mainfrom
komyg:fix/47753-organize-imports-with-jsdoc
Apr 29, 2022
Merged

Nathan Shively-Sanders (sandersn) merged 19 commits into
microsoft:mainfrom
komyg:fix/47753-organize-imports-with-jsdoc

Conversation

@komyg

@komyg Felipe Armoni (komyg) commented Feb 9, 2022 •

Copy link
Copy Markdown
Contributor

Fixes #47753, #47558

@typescript-bot TypeScript Bot (typescript-bot) added the For Backlog Bug PRs that fix a backlog bug label Feb 9, 2022
@komyg
Felipe Armoni (komyg) marked this pull request as ready for review February 17, 2022 19:48

Copy link
Copy Markdown
Member

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.

Comment thread src/services/organizeImports.ts Outdated
@komyg

Copy link
Copy Markdown
Contributor Author

Hi Nathan Shively-Sanders (@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
Copy Markdown

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
Copy Markdown
Contributor Author

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

@DanielSWolf

Copy link
Copy Markdown

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

@komyg

Copy link
Copy Markdown
Contributor Author

Hi Nathan Shively-Sanders (@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
Copy Markdown
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

Nathan Shively-Sanders (sandersn) commented Mar 30, 2022 •

Copy link
Copy Markdown
Member

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
Copy Markdown
Contributor Author

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
Copy Markdown
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
Copy Markdown
Contributor Author

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
Copy Markdown
Member

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
Copy Markdown
Member

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
Copy Markdown
Member

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

Felipe Armoni (komyg) commented Apr 7, 2022 •

Copy link
Copy Markdown
Contributor Author

Hey Nathan Shively-Sanders (@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

Nathan Shively-Sanders (sandersn) commented Apr 7, 2022 •

Copy link
Copy Markdown
Member

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

Nathan Shively-Sanders (sandersn) commented Apr 7, 2022 •

Copy link
Copy Markdown
Member

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

@komyg

Felipe Armoni (komyg) commented Apr 8, 2022 •

Copy link
Copy Markdown
Contributor Author

Hi Nathan Shively-Sanders (@sandersn), I've done the changes we've discussed. Could you review again, please?

@komyg

Copy link
Copy Markdown
Contributor Author

Hi Nathan Shively-Sanders (@sandersn), sorry to bother you. I've done the changes we've discussed. Could you review again, please?

@sandersn
Nathan Shively-Sanders (sandersn) merged commit fd06132 into microsoft:main Apr 29, 2022
@sandersn

Copy link
Copy Markdown
Member

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

@komyg

Copy link
Copy Markdown
Contributor Author

No worries! I am happy to have contributed!

@komyg
Felipe Armoni (komyg) deleted the fix/47753-organize-imports-with-jsdoc branch May 26, 2022 12:48
@microsoft Microsoft (microsoft) locked as resolved and limited conversation to collaborators Oct 22, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

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