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

Decorators #1324

Merged
merged 25 commits into from
Jan 28, 2021
Merged

Decorators #1324

merged 25 commits into from
Jan 28, 2021

Conversation

shenglol
Copy link
Contributor

@shenglol shenglol commented Jan 15, 2021

Added support to decorators. There are some features that I'm planning on adding in follow-up PRs:

  • Code intelligence
  • Decompiling

The other thing worth mentioning is that the current parameter modifier syntax will continue working. My idea is to give users sometime to migrate to the new syntax. We can drop modifier support before the 0.3 release if there's no major concerns. I've added semantic checking to ban using decorators and modifier at the same time. Once the PR is merged, I'll be working on updating the examples and test files to use decorators (this can take a while and I would expect some helps from the OSS community). Once they are all updated, I'll implement another checker to warn users that parameter modifier is deprecated.

Partially addresses #64.

@shenglol shenglol force-pushed the shenglol/decorators branch from 07a5675 to f5a9a32 Compare January 19, 2021 23:21
@codecov-io
Copy link

codecov-io commented Jan 19, 2021

Codecov Report

Merging #1324 (70907f4) into main (7207ddf) will decrease coverage by 0.05%.
The diff coverage is 93.22%.

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1324      +/-   ##
==========================================
- Coverage   94.81%   94.76%   -0.06%     
==========================================
  Files         343      351       +8     
  Lines       17564    18188     +624     
  Branches       14       14              
==========================================
+ Hits        16653    17235     +582     
- Misses        911      953      +42     
Flag Coverage Δ
dotnet 95.26% <93.22%> (-0.08%) ⬇️
typescript 27.20% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
....Core.UnitTests/Utils/SyntaxTreeGroupingFactory.cs 100.00% <ø> (ø)
src/Bicep.Core/Syntax/SyntaxRewriteVisitor.cs 71.31% <46.15%> (-2.65%) ⬇️
src/Bicep.Core/Syntax/SyntaxFactory.cs 58.13% <58.13%> (ø)
src/Bicep.Core/Semantics/DecoratorBuilder.cs 77.77% <77.77%> (ø)
src/Bicep.Core/TypeSystem/CyclicCheckVisitor.cs 95.23% <88.88%> (+1.12%) ⬆️
src/Bicep.Core/TypeSystem/TypeAssignmentVisitor.cs 96.33% <90.00%> (-0.46%) ⬇️
src/Bicep.Core/Syntax/ObjectSyntaxExtensions.cs 93.93% <90.47%> (-6.07%) ⬇️
src/Bicep.Core/Semantics/Decorator.cs 92.30% <92.30%> (ø)
src/Bicep.Core/TypeSystem/DecoratorResolver.cs 92.85% <92.85%> (ø)
src/Bicep.Core/Semantics/SymbolValidator.cs 83.33% <96.15%> (+6.25%) ⬆️
... and 47 more

@shenglol shenglol marked this pull request as ready for review January 22, 2021 00:42
@shenglol shenglol changed the title [WIP] Decorators Decorators Jan 22, 2021

namespace Bicep.Core.Syntax
{
public static class SyntaxFactory
Copy link
Contributor Author

@shenglol shenglol Jan 22, 2021

Choose a reason for hiding this comment

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

We now have multiple similar classes defined in different projects:

  • Bicep.Core.Syntax.SyntaxFactory
  • Bicep.Decompiler.BicepHelpers.SyntaxHelpers
  • Bicep.Core.UnitTests.Utils.TestSyntaxFactory

To make this PR less overwhelming, I'll send a separate PR to reduce them to one class.

@shenglol shenglol linked an issue Jan 22, 2021 that may be closed by this pull request
Comment on lines 1 to 18
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization;
using System.Linq;
using Bicep.Core.Diagnostics;
using Bicep.Core.Extensions;
using Bicep.Core.Navigation;
using Bicep.Core.Syntax;

namespace Bicep.Core.Parsing
{
public class Parser
{
private readonly TokenReader reader;

Copy link
Member

Choose a reason for hiding this comment

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

@shenglol - do you know why your PRs show large whitespace diffs? Are there line ending changes here? If so, any way to avoid this? I notice that when I modify code afterwards, I also get large whitespace diffs (presumably to change back to the previous format). It would be good if we can converge on / enforce a standard here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I suspect that the line endings of this file got normalized by someone's Visual Studio settings. The whitespace diffs showed up after I merged the main branch. I feel like we should put a .editorconfig file at the root level and add that as a VS solution item. I submit a PR to do that later.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Created #1401 to track this.

Comment on lines 100 to 105
LanguageConstants.TargetScopeKeyword => this.TargetScope(leadingNodes),
LanguageConstants.ParameterKeyword => this.ParameterDeclaration(leadingNodes),
LanguageConstants.VariableKeyword => this.VariableDeclaration(leadingNodes),
LanguageConstants.ResourceKeyword => this.ResourceDeclaration(leadingNodes),
LanguageConstants.OutputKeyword => this.OutputDeclaration(leadingNodes),
LanguageConstants.ModuleKeyword => this.ModuleDeclaration(leadingNodes),
Copy link
Member

Choose a reason for hiding this comment

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

Out of interest, what are the pros/cons of modelling decorators as part of the declaration syntax, vs as a top-level syntax independent of the declarations? Do you happen to know how this is handled in similar languages (C# or TS for example)?

Copy link
Member

Choose a reason for hiding this comment

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

Roslyn includes under the declaration:
image

For the missing declaration case, they create an IncompleteMember node:
image

Copy link
Member

Choose a reason for hiding this comment

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

I suspect having the decorators under the declaration makes it easier to process them. Otherwise, you would always need a visitor to collect the preceding decorators. (Or the ability to efficiently get the preceding peer node in the AST.)

Copy link
Contributor Author

@shenglol shenglol Jan 25, 2021

Choose a reason for hiding this comment

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

TypeScript also puts them under declarations probably for the reason that Marcin mentioned:
image

TypeSymbol targetType,
ObjectSyntax? targetObject);

public class Decorator
Copy link
Member

Choose a reason for hiding this comment

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

Decorator [](start = 17, length = 9)

Is there any value in making decorators symbols? It might be helpful with some of the navigation features like hovers to avoid special casing...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought about creating a new DecoratorSymbol type originally, but on second thought modelling them as FunctionSymbols makes it easier to leverage all semantic checkers and LSP capablilities we have implemented for function calls (decorators are essentially higher order functions IMHO). Features like hovering is already working:
image

Do you mind clarifying what "to avoid special casing" means?

Copy link
Member

Choose a reason for hiding this comment

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

Can ignore that comment if hovers are already working. I think the hover text will be confusing to our users. We should probably make it more obvious that it's a decorator. (Higher order functions are an advanced concept after all.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed that we should make it more obvious that it's a decorator. Maybe showing descriptions below type signatures would help, but I feel like that's something to be implemented in another PR and we probably need to discuss it.


namespace Bicep.Core.Syntax
{
public abstract class StatementSyntax : SyntaxBase
Copy link
Member

Choose a reason for hiding this comment

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

StatementSyntax [](start = 26, length = 15)

Should we call it DeclarationSyntax to match the file name?

Copy link
Contributor Author

@shenglol shenglol Jan 28, 2021

Choose a reason for hiding this comment

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

Ah good catch...too bad my ReSharper license expired which should have caught this. I renamed the file to StatementSyntax though. It's kind of weird if we call it DeclarationSyntax while it has nothing to do with the IDeclaration interface. Let me know if StatementSyntax works.

this.LeadingNodes = leadingNodes.ToImmutableArray();
}

public ImmutableArray<SyntaxBase> LeadingNodes { get; }
Copy link
Member

@majastrz majastrz Jan 27, 2021

Choose a reason for hiding this comment

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

LeadingNodes [](start = 42, length = 12)

I like how adding this class cleaned up all the declarations btw!

@majastrz
Copy link
Member

At some point (not necessarily in this PR), we should update the parameter snippets and switch them to the decorator syntax.

@majastrz
Copy link
Member

It looks like you have to type @secure() and it seems @secure is not enough. Is that intentional and should we relax that in the future?

@majastrz
Copy link
Member

I tried doing a dangling decorator:
image

Can we make it more obvious that the decorator requires a declaration to follow? (The error message is in the right position, so that definitely helps to figure what is going on.)

@majastrz
Copy link
Member

majastrz commented Jan 28, 2021

Doing @secure on an int parameter didn't give me any errors btw (tried in VS code and CLI):
image

I think we have a test case for this with the modifier syntax. Should probably copy and convert all those tests to decorators so we preserve coverage.

@shenglol
Copy link
Contributor Author

shenglol commented Jan 28, 2021

I tried doing a dangling decorator:
image

Can we make it more obvious that the decorator requires a declaration to follow? (The error message is in the right position, so that definitely helps to figure what is going on.)

Makes sense. The current one is a bit confusing when there's no declaration under decorators. I tweaked the parser to achieve this:

image

@shenglol
Copy link
Contributor Author

shenglol commented Jan 28, 2021

Doing @secure on an int parameter didn't give me any errors btw (tried in VS code and CLI):
image

I think we have a test case for this with the modifier syntax. Should probably copy and convert all those tests to decorators so we preserve coverage.

Yeah we should. I copied and converted all parameter tests and it did help capture some edge cases that I missed. I'll keep updating more as I start converting other test files (PRs to come).

@shenglol
Copy link
Contributor Author

shenglol commented Jan 28, 2021

It looks like you have to type @secure() and it seems @secure is not enough. Is that intentional and should we relax that in the future?

Had a discussion offline with Marcin. The reason why I didn't implement @secure in this PR is that we should also support @sys.secure, but currently name binding doesn't work for property access syntax. We'll talk about this during our next DSL discussion meeting.

@alex-frankel
Copy link
Collaborator

The reason why I didn't implement @secure in this PR is that we should also want to support @sys.secure, but currently naming binding doesn't work for property access syntax. We'll talk about this during our next DSL discussion meeting.

Can we create an issue to track this?

@shenglol
Copy link
Contributor Author

The reason why I didn't implement @secure in this PR is that we should also want to support @sys.secure, but currently naming binding doesn't work for property access syntax. We'll talk about this during our next DSL discussion meeting.

Can we create an issue to track this?

Yep. Created an issue: #1405.

// If there are dangling decorators and we hit EOF, ask users to add a declration.
// Set the span of the diagnostic so that it's on the line below the last decorator.
var lastNewLine = missingDeclaration.LeadingNodes.Last(node => node is Token { Type: TokenType.NewLine });
var diagnosticSpan = new TextSpan(lastNewLine.Span.Position + 2, 0);
Copy link
Member

Choose a reason for hiding this comment

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

The + 2 approach only works on CRLF files. On LF, the position is off by 1:

image

If you don't have a newline following the decorator, the parser will tell you to add it, so it seems like you can rely on it and use its position, but not sure if there are other edge cases.

Copy link
Member

Choose a reason for hiding this comment

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

I'm fine if this goes into the next PR also.


In reply to: 566350593 [](ancestors = 566350593)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is to handle an edge case where there are multiple newlines after a decorator:

image

Not sure why the error span is [3, 2]. On my machine it's [3, 1]...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's due to different line endings.

Copy link
Member

@majastrz majastrz left a comment

Choose a reason for hiding this comment

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

Found an off by 1 bug with LF files (added a comment), but otherwise looks great!

@shenglol
Copy link
Contributor Author

Found an off by 1 bug with LF files (added a comment), but otherwise looks great!

Will send a separate PR to fix that soon.

@shenglol shenglol merged commit 3c12faa into main Jan 28, 2021
@shenglol shenglol deleted the shenglol/decorators branch January 28, 2021 19:38
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.

Decorator syntax
6 participants