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

C#: Roslyn-based stub generation #14095

Merged
merged 12 commits into from
Sep 22, 2023
Merged

Conversation

hvitved
Copy link
Contributor

@hvitved hvitved commented Aug 30, 2023

This PR introduced a Roslyn-based stub generator, based on the existing (but much much slower) QL based stub generator. The make_stubs_nuget.py script is updated to use the new generator, and this PR updates some of the existing stubs with newly generated stubs.

Commit-by-commit review is suggested.

Comment on lines 621 to 677
if (symbol.IsAbstract)
{
if (
// exclude interface declarations
(symbol is not INamedTypeSymbol type || type.TypeKind != TypeKind.Interface) &&
// exclude non-static interface members
(symbol.ContainingType is not INamedTypeSymbol containingType || containingType.TypeKind != TypeKind.Interface || symbol.IsStatic))
{
stubWriter.Write("abstract ");
}
}

Check notice

Code scanning / CodeQL

Nested 'if' statements can be combined Note

These 'if' statements can be combined.
Comment on lines 639 to 689
if (symbol.IsSealed)
{
if (!(symbol is INamedTypeSymbol type && (type.TypeKind == TypeKind.Enum || type.TypeKind == TypeKind.Delegate || type.TypeKind == TypeKind.Struct)))
stubWriter.Write("sealed ");
}

Check notice

Code scanning / CodeQL

Nested 'if' statements can be combined Note

These 'if' statements can be combined.

private void StubModifiers(ISymbol symbol, bool skipAccessibility = false)
{
if (symbol.ContainingType is ITypeSymbol containing && containing.TypeKind == TypeKind.Interface)

Check warning

Code scanning / CodeQL

Useless type test Warning

There is no need to test whether an instance of
INamedTypeSymbol
is also an instance of
ITypeSymbol
- it always is.
@hvitved hvitved force-pushed the csharp/stub-generator branch 2 times, most recently from db4d1c3 to 73075e3 Compare August 31, 2023 08:56
Comment on lines +29 to +46
type is INamedTypeSymbol named && (named.IsTupleType || named.TypeArguments.Any(ContainsTupleType)) ||
type is IArrayTypeSymbol array && ContainsTupleType(array.ElementType) ||
type is IPointerTypeSymbol pointer && ContainsTupleType(pointer.PointedAtType);

Check notice

Code scanning / CodeQL

Complex condition Note

Complex condition: too many logical operations in this expression.
Comment on lines +34 to +66
SymbolEqualityComparer.Default.Equals(t1, t2) ||
(
t1 is INamedTypeSymbol named1 &&
t2 is INamedTypeSymbol named2 &&
EqualsModuloTupleElementNames(named1.ConstructedFrom, named2.ConstructedFrom) &&
named1.TypeArguments.Length == named2.TypeArguments.Length &&
named1.TypeArguments.Zip(named2.TypeArguments).All(p => EqualsModuloTupleElementNames(p.First, p.Second))
) ||
(
t1 is IArrayTypeSymbol array1 &&
t2 is IArrayTypeSymbol array2 &&
EqualsModuloTupleElementNames(array1.ElementType, array2.ElementType)
) ||
(
t1 is IPointerTypeSymbol pointer1 &&
t2 is IPointerTypeSymbol pointer2 &&
EqualsModuloTupleElementNames(pointer1.PointedAtType, pointer2.PointedAtType)
);

Check notice

Code scanning / CodeQL

Complex condition Note

Complex condition: too many logical operations in this expression.
@hvitved hvitved force-pushed the csharp/stub-generator branch from 73075e3 to aff8207 Compare September 5, 2023 11:40
@github-actions github-actions bot removed the Ruby label Sep 5, 2023
@hvitved hvitved force-pushed the csharp/stub-generator branch 2 times, most recently from e806ae9 to f8ace6a Compare September 6, 2023 07:32
@hvitved hvitved force-pushed the csharp/stub-generator branch from f8ace6a to c06b515 Compare September 6, 2023 12:21
@hvitved hvitved force-pushed the csharp/stub-generator branch from c06b515 to 2beb829 Compare September 6, 2023 12:23
@github-actions github-actions bot removed the QL-for-QL label Sep 6, 2023
@hvitved hvitved force-pushed the csharp/stub-generator branch 2 times, most recently from 6db4dc5 to 591f055 Compare September 8, 2023 08:50
@hvitved hvitved force-pushed the csharp/stub-generator branch 3 times, most recently from 3381f33 to b3ac833 Compare September 20, 2023 09:26
@hvitved hvitved added the no-change-note-required This PR does not need a change note label Sep 20, 2023
@hvitved hvitved marked this pull request as ready for review September 20, 2023 10:16
@hvitved hvitved requested a review from a team as a code owner September 20, 2023 10:16
Copy link
Contributor

@tamasvajk tamasvajk left a comment

Choose a reason for hiding this comment

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

Looks good to me. I've added some comments/questions.

One general question: we're not explicitly ordering the printed members. Do we know if the roslyn provided order is stable? If we used alphabetical ordering, then we would probably match what the previous stub generator did, and then verifying the correctness of the newly generated stubs would be easy.

@michaelnebel michaelnebel self-requested a review September 21, 2023 07:46
Copy link
Contributor

@michaelnebel michaelnebel left a comment

Choose a reason for hiding this comment

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

Approximately half way through. Will continue later today.
This looks really really cool @hvitved . Really great (and impressive) work!

Besides the inlined questions and comments:

  • Maybe run DCA since we change existing production code.
  • Is it possible to make some unit tests somehow of the StubVisitor?

@hvitved hvitved force-pushed the csharp/stub-generator branch from b3ac833 to e14898c Compare September 21, 2023 10:57
@hvitved
Copy link
Contributor Author

hvitved commented Sep 21, 2023

Looks good to me. I've added some comments/questions.

One general question: we're not explicitly ordering the printed members. Do we know if the roslyn provided order is stable? If we used alphabetical ordering, then we would probably match what the previous stub generator did, and then verifying the correctness of the newly generated stubs would be easy.

I have addressed your comments, and the members are now sorted alphabetically. I simply updated the original stub regeneration commits with the newly generated stubs, instead of adding extra commits.

@hvitved hvitved force-pushed the csharp/stub-generator branch from e14898c to 04c4e73 Compare September 21, 2023 11:33
@hvitved
Copy link
Contributor Author

hvitved commented Sep 21, 2023

Approximately half way through. Will continue later today.
This looks really really cool @hvitved . Really great (and impressive) work!

Thanks :-)

I have (force) pushed changes to address your comments as well.

Besides the inlined questions and comments:

Maybe run DCA since we change existing production code.
Will do
Is it possible to make some unit tests somehow of the StubVisitor?
Possible, yes, but I think I would rather add an integration test that runs a QL test using regenerated stubs.

Copy link
Contributor

@tamasvajk tamasvajk left a comment

Choose a reason for hiding this comment

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

I have addressed your comments, and the members are now sorted alphabetically. I simply updated the original stub regeneration commits with the newly generated stubs, instead of adding extra commits.

Thanks. It looks like the original stub generator did a bit more than ordering alphabetically. ( I think it might have generated the full signatures in memory, and then order them before writing.) I think it would be difficult to repro this based on Roslyn, which is a shame, it would have been great to compare the original and new stubs. (BTW, we might not have a stable order for overloads either.)

I've added some comments where I found differences between the old and new stubs.

@hvitved
Copy link
Contributor Author

hvitved commented Sep 21, 2023

Thanks. It looks like the original stub generator did a bit more than ordering alphabetically. ( I think it might have generated the full signatures in memory, and then order them before writing.) I think it would be difficult to repro this based on Roslyn, which is a shame, it would have been great to compare the original and new stubs. (BTW, we might not have a stable order for overloads either.)

Indeed, that is what the QL based stub generator does, and we very much don't want to first compute things in memory and then write to disk later.

@hvitved hvitved force-pushed the csharp/stub-generator branch from 6b11ee0 to 208616e Compare September 21, 2023 13:14
@hvitved hvitved force-pushed the csharp/stub-generator branch from 208616e to f07d02b Compare September 21, 2023 13:36
@hvitved hvitved force-pushed the csharp/stub-generator branch from d8a806a to 0108aef Compare September 22, 2023 07:13
@hvitved hvitved force-pushed the csharp/stub-generator branch from 0108aef to 831baa8 Compare September 22, 2023 07:15
Copy link
Contributor

@tamasvajk tamasvajk left a comment

Choose a reason for hiding this comment

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

LGTM.
How much faster is this solution than the previous QL based one?

public ANTLRStringStream(System.Char[] data, int numberOfActualCharsInArray) => throw null;
protected ANTLRStringStream() => throw null;
public virtual int CharPositionInLine { get => throw null; set => throw null; }
public virtual int CharPositionInLine { get => throw null; set { } }
Copy link
Contributor

Choose a reason for hiding this comment

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

set doesn't throw null?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No reason to, it is just a matter of making the stubs as simple as possible in terms of generated code.

@michaelnebel
Copy link
Contributor

LGTM. How much faster is this solution than the previous QL based one?

Yeah, I am also really curious!

@hvitved
Copy link
Contributor Author

hvitved commented Sep 22, 2023

How much faster is this solution than the previous QL based one?

It takes just a few seconds to generate stubs for the full standard library, so definitely much faster, but I don't have actual numbers from before.

Copy link
Contributor

@michaelnebel michaelnebel left a comment

Choose a reason for hiding this comment

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

Great work Tom!
LGTM 👍

Non-blocking question:
Are you planning on making it unit testable or introduce some integration like tests for? We do have tests for the queries used by the old stub generator.

@hvitved
Copy link
Contributor Author

hvitved commented Sep 22, 2023

Generating stubs for Swashbuckle.AspNetCore.Swagger (which includes NetCore and AspNetCore) takes 50 seconds for the QL based stub generator, while it takes 5 seconds for the new stub generator.

@hvitved
Copy link
Contributor Author

hvitved commented Sep 22, 2023

Are you planning on making it unit testable or introduce some integration like tests for? We do have tests for the queries used by the old stub generator.

Unit tests, no. But I was thinking about adding an integration test.

@hvitved hvitved merged commit 9638a6c into github:main Sep 22, 2023
10 checks passed
@hvitved hvitved deleted the csharp/stub-generator branch September 22, 2023 10:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C# no-change-note-required This PR does not need a change note
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants