Skip to content

Commit

Permalink
Flow declared type information to function arguments (#5188)
Browse files Browse the repository at this point in the history
* Flow declared type information to function arguments

* Fix up tests
  • Loading branch information
anthony-c-martin committed Nov 17, 2021
1 parent ba151dc commit c4fb1f8
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 13 deletions.
2 changes: 2 additions & 0 deletions src/Bicep.Core.UnitTests/Utils/BuiltInTestTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,8 @@ private static ResourceTypeComponents ListFunctionsType()
var withInputInput = new ObjectType("WithInputInput", TypeSymbolValidationFlags.Default,
new [] {
new TypeProperty("withInputInputVal", LanguageConstants.String, TypePropertyFlags.WriteOnly | TypePropertyFlags.Required, "Foo description"),
new TypeProperty("optionalVal", LanguageConstants.String, TypePropertyFlags.WriteOnly, "optionalVal description"),
new TypeProperty("optionalLiteralVal", TypeHelper.CreateTypeUnion(new StringLiteralType("either"), new StringLiteralType("or")), TypePropertyFlags.WriteOnly, "optionalLiteralVal description"),
}, null);
var withInputOutput = new ObjectType("WithInputOutput", TypeSymbolValidationFlags.Default,
new [] {
Expand Down
54 changes: 43 additions & 11 deletions src/Bicep.Core/TypeSystem/DeclaredTypeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ public DeclaredTypeManager(TypeManager typeManager, IBinder binder)

case StringSyntax @string:
return GetStringType(@string);

case FunctionArgumentSyntax functionArgument:
return GetFunctionArgumentType(functionArgument);
}

return null;
Expand Down Expand Up @@ -272,14 +275,17 @@ private DeclaredTypeAssignment GetModuleType(ModuleDeclarationSyntax syntax)
// we are only handling paths in the AST that are going to produce a declared type
// arrays can exist under a variable declaration, but variables don't have declared types,
// so we don't need to check that case
if (parent is ObjectPropertySyntax)
switch (parent)
{
// this array is a value of the property
// the declared type should be the same as the array and we should propagate the flags
return GetDeclaredTypeAssignment(parent)?.ReplaceDeclaringSyntax(syntax);
case ObjectPropertySyntax:
// this array is a value of the property
// the declared type should be the same as the array and we should propagate the flags
return GetDeclaredTypeAssignment(parent)?.ReplaceDeclaringSyntax(syntax);
case FunctionArgumentSyntax:
return GetDeclaredTypeAssignment(parent)?.ReplaceDeclaringSyntax(syntax);
default:
return null;
}

return null;
}

private DeclaredTypeAssignment? GetStringType(StringSyntax syntax)
Expand All @@ -289,14 +295,33 @@ private DeclaredTypeAssignment GetModuleType(ModuleDeclarationSyntax syntax)
// we are only handling paths in the AST that are going to produce a declared type
// strings can exist under a variable declaration, but variables don't have declared types,
// so we don't need to check that case
if (parent is ObjectPropertySyntax || parent is ArrayItemSyntax)
switch (parent)
{
// this string is a value of the property
// the declared type should be the same as the string and we should propagate the flags
return GetDeclaredTypeAssignment(parent)?.ReplaceDeclaringSyntax(syntax);
case ObjectPropertySyntax:
case ArrayItemSyntax:
// this string is a value of the property
// the declared type should be the same as the string and we should propagate the flags
return GetDeclaredTypeAssignment(parent)?.ReplaceDeclaringSyntax(syntax);
case FunctionArgumentSyntax:
return GetDeclaredTypeAssignment(parent)?.ReplaceDeclaringSyntax(syntax);
default:
return null;
}
}

return null;
private DeclaredTypeAssignment? GetFunctionArgumentType(FunctionArgumentSyntax syntax)
{
var parent = this.binder.GetParent(syntax);
if (parent is not FunctionCallSyntaxBase parentFunction ||
SymbolHelper.TryGetSymbolInfo(this.binder, this.GetDeclaredType, parent) is not FunctionSymbol functionSymbol)
{
return null;
}

var argIndex = parentFunction.Arguments.IndexOf(syntax);
var declaredType = functionSymbol.GetDeclaredArgumentType(argIndex);

return new DeclaredTypeAssignment(declaredType, declaringSyntax: null);
}

private DeclaredTypeAssignment? GetArrayItemType(ArrayItemSyntax syntax)
Expand Down Expand Up @@ -515,6 +540,13 @@ private DeclaredTypeAssignment GetModuleType(ModuleDeclarationSyntax syntax)
// the object is an item in an array
// use the item's type and propagate flags
return TryCreateAssignment(ResolveDiscriminatedObjects(namespaceType.ConfigurationType.Type, syntax), syntax, importAssignment.Flags);
case FunctionArgumentSyntax:
if (GetDeclaredTypeAssignment(parent) is not {} parentAssignment)
{
return null;
}

return TryCreateAssignment(ResolveDiscriminatedObjects(parentAssignment.Reference.Type, syntax), syntax, parentAssignment.Flags);
}

return null;
Expand Down
74 changes: 72 additions & 2 deletions src/Bicep.LangServer.IntegrationTests/CompletionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1307,7 +1307,7 @@ public async Task List_functions_accepting_inputs_suggest_api_version_param()
name: 'abc'
}
var outTest = abc.listWithInput('2020-01-01')
var outTest = abc.listWithInput('2020-01-01'|)
");
}

Expand Down Expand Up @@ -1341,6 +1341,75 @@ public async Task List_functions_accepting_inputs_suggest_required_properties()
");
}

[TestMethod]
public async Task List_functions_accepting_inputs_permit_object_key_completions()
{
var fileWithCursors = @"
resource abc 'Test.Rp/listFuncTests@2020-01-01' existing = {
name: 'abc'
}
var outTest = abc.listWithInput('2020-01-01', {
withInputInputVal: 'hello'
|
})
";

var (file, cursors) = ParserHelper.GetFileWithCursors(fileWithCursors);
var bicepFile = SourceFileFactory.CreateBicepFile(new Uri("file:https:///main.bicep"), file);
using var helper = await LanguageServerHelper.StartServerWithTextAsync(TestContext, file, bicepFile.FileUri, creationOptions: new LanguageServer.Server.CreationOptions(NamespaceProvider: BuiltInTestTypes.Create()));
var client = helper.Client;
var completions = await RequestCompletion(client, bicepFile, cursors.Single());
completions.Should().Contain(x => x.Label == "optionalVal");

var updatedFile = ApplyCompletion(bicepFile, completions.Single(x => x.Label == "optionalVal"));
updatedFile.Should().HaveSourceText(@"
resource abc 'Test.Rp/listFuncTests@2020-01-01' existing = {
name: 'abc'
}
var outTest = abc.listWithInput('2020-01-01', {
withInputInputVal: 'hello'
optionalVal:|
})
");
}

[TestMethod]
public async Task List_functions_accepting_inputs_permit_object_value_completions()
{
var fileWithCursors = @"
resource abc 'Test.Rp/listFuncTests@2020-01-01' existing = {
name: 'abc'
}
var outTest = abc.listWithInput('2020-01-01', {
withInputInputVal: 'hello'
optionalLiteralVal: |
})
";

var (file, cursors) = ParserHelper.GetFileWithCursors(fileWithCursors);
var bicepFile = SourceFileFactory.CreateBicepFile(new Uri("file:https:///main.bicep"), file);
using var helper = await LanguageServerHelper.StartServerWithTextAsync(TestContext, file, bicepFile.FileUri, creationOptions: new LanguageServer.Server.CreationOptions(NamespaceProvider: BuiltInTestTypes.Create()));
var client = helper.Client;
var completions = await RequestCompletion(client, bicepFile, cursors.Single());
completions.Should().Contain(x => x.Label == "'either'");
completions.Should().Contain(x => x.Label == "'or'");

var updatedFile = ApplyCompletion(bicepFile, completions.Single(x => x.Label == "'either'"));
updatedFile.Should().HaveSourceText(@"
resource abc 'Test.Rp/listFuncTests@2020-01-01' existing = {
name: 'abc'
}
var outTest = abc.listWithInput('2020-01-01', {
withInputInputVal: 'hello'
optionalLiteralVal: 'either'|
})
");
}

[TestMethod]
public async Task List_functions_return_property_completions()
{
Expand All @@ -1365,7 +1434,7 @@ public async Task List_functions_return_property_completions()
name: 'abc'
}
var outTest = abc.listWithInput().withInputOutputVal
var outTest = abc.listWithInput().withInputOutputVal|
");
}

Expand Down Expand Up @@ -1640,6 +1709,7 @@ private static BicepFile ApplyCompletion(BicepFile bicepFile, CompletionItem com
}
break;
case InsertTextFormat.PlainText:
textToInsert = textToInsert + "|";
break;
default:
throw new InvalidOperationException();
Expand Down

0 comments on commit c4fb1f8

Please sign in to comment.