Skip to content

Commit

Permalink
Add support for implicit object creation expressions (i. e. `... = ne…
Browse files Browse the repository at this point in the history
…w(...)`)

Closes #6
  • Loading branch information
mykolav committed Feb 18, 2023
1 parent 09dfc1c commit 06d049d
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 33 deletions.
39 changes: 39 additions & 0 deletions RequireNamedArgs.Tests/Analyzer/AnalyzerTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -202,5 +202,44 @@ module AnalyzerTests =
paramNamesByType=[[ "line"; "column" ]],
fileName="Test0.cs", line=15u, column=36u)

[| expectedDiag |] |> ExpectDiags.toBeEmittedFrom testCodeSnippet
}
test "Constructor w/ [RequireNamedArgs] invoked implicitly w/ named args does not trigger diagnostic" {
ExpectDiags.emptyDiagnostics @"
class Wombat
{
public string Name { get; }
public int PowerLevel { get; }
[RequireNamedArgs]
public Wombat(string name, int powerLevel) => (Name, PowerLevel) = (name, powerLevel);
public static Wombat Create()
{
return new(name: ""Goku"", powerLevel: 5000);
}
}
"
}
test "Constructor w/ [RequireNamedArgs] invoked implicitly w/ positional args triggers diagnostic" {
let testCodeSnippet = @"
class Wombat
{
public string Name { get; }
public int PowerLevel { get; }
[RequireNamedArgs]
public Wombat(string name, int powerLevel) => (Name, PowerLevel) = (name, powerLevel);
public static Wombat Create()
{
return new(""Goku"", 5000);
}
}
"
let expectedDiag = RequireNamedArgsDiagResult.Create(invokedMethod=".ctor",
paramNamesByType=[[ "line"; "column" ]],
fileName="Test0.cs", line=15u, column=36u)

[| expectedDiag |] |> ExpectDiags.toBeEmittedFrom testCodeSnippet
}]
2 changes: 1 addition & 1 deletion RequireNamedArgs.Tests/RequireNamedArgs.Tests.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion RequireNamedArgs.Tests/Support/DocumentFactory.fs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ module DocumentFactory =

let parseOptions =
if lang = Langs.CSharp
then CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp7_2) :> ParseOptions
then CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp9) :> ParseOptions
else VisualBasicParseOptions.Default :> ParseOptions

let solution = (new AdhocWorkspace())
Expand Down
59 changes: 31 additions & 28 deletions RequireNamedArgs/Analysis/InvocationAnalyzer.fs
Original file line number Diff line number Diff line change
Expand Up @@ -105,33 +105,36 @@ type InvocationAnalyzer private(_sema: SemanticModel,
/// we should stop analysis of the current expression.
/// </returns>
member this.GetArgsMissingNames(): Res<ArgWithParamSymbol[]> =
let argSyntaxes = _exprSyntax.GetArguments()

if not (argSyntaxes.Any())
then
Ok NoArgsMissingNames
else
let argSyntaxes =
match _exprSyntax with
| :? ImplicitObjectCreationExpressionSyntax as it -> it.ArgumentList.Arguments
| _ -> _exprSyntax.GetArguments()

if not (argSyntaxes.Any())
then
Ok NoArgsMissingNames
else

let lastArgIndex = argSyntaxes.Count - 1
let lastParamInfoRes = _sema.GetParameterInfo(_methodSymbol, lastArgIndex, argSyntaxes.[lastArgIndex])
if lastParamInfoRes.ShouldStopAnalysis
then
StopAnalysis
else
if lastParamInfoRes.Value.ParamSymbol.IsParams
then
Ok NoArgsMissingNames
else
let lastArgIndex = argSyntaxes.Count - 1
let lastParamInfoRes = _sema.GetParameterInfo(_methodSymbol, lastArgIndex, argSyntaxes.[lastArgIndex])
if lastParamInfoRes.ShouldStopAnalysis
then
StopAnalysis
else

if lastParamInfoRes.Value.ParamSymbol.IsParams
then
Ok NoArgsMissingNames
else

let argWithParamSymbolsRes = zipArgSyntaxesWithParamSymbols argSyntaxes
if argWithParamSymbolsRes.ShouldStopAnalysis
then
StopAnalysis
else
let argsMissingNames =
argWithParamSymbolsRes.Value
|> Array.filter (fun it -> isNull it.ArgSyntax.NameColon)
Ok argsMissingNames
let argWithParamSymbolsRes = zipArgSyntaxesWithParamSymbols argSyntaxes
if argWithParamSymbolsRes.ShouldStopAnalysis
then
StopAnalysis
else

let argsMissingNames =
argWithParamSymbolsRes.Value
|> Array.filter (fun it -> isNull it.ArgSyntax.NameColon)

Ok argsMissingNames
2 changes: 1 addition & 1 deletion RequireNamedArgs/RequireNamedArgs.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis" Version="2.8.2" />
<PackageReference Include="Microsoft.CodeAnalysis" Version="4.4.0" />
</ItemGroup>

<ItemGroup>
Expand Down
5 changes: 3 additions & 2 deletions RequireNamedArgs/RequireNamedArgsAnalyzer.fs
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,10 @@ type public RequireNamedArgsAnalyzer() =
context.RegisterSyntaxNodeAction(
(fun c -> this.Analyze c),
SyntaxKind.InvocationExpression,
SyntaxKind.ObjectCreationExpression)

SyntaxKind.ObjectCreationExpression,
SyntaxKind.ImplicitObjectCreationExpression)


member private this.Analyze(context: SyntaxNodeAnalysisContext) =
let invocationAnalyzerRes = InvocationAnalyzer.Create(context.SemanticModel, context.Node)
if invocationAnalyzerRes.ShouldStopAnalysis
Expand Down

0 comments on commit 06d049d

Please sign in to comment.