Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5587,16 +5587,40 @@ public override AstVisitAction VisitAssignmentStatement(AssignmentStatementAst a
return AstVisitAction.StopVisit;
}

if (assignmentStatementAst.Left is ConvertExpressionAst convertExpression)
if (assignmentStatementAst.Left is AttributedExpressionAst attributedExpression)
{
if (convertExpression.Child is VariableExpressionAst variableExpression)
var firstConvertExpression = attributedExpression as ConvertExpressionAst;
ExpressionAst child = attributedExpression.Child;
while (child is AttributedExpressionAst attributeChild)
{
if (firstConvertExpression is null && attributeChild is ConvertExpressionAst convertExpression)
{
// Multiple type constraint can be set on a variable like this: [int] [string] $Var1 = 1
// But it's the left most type constraint that determines the final type.
firstConvertExpression = convertExpression;
}

child = attributeChild.Child;
}

if (child is VariableExpressionAst variableExpression)
{
if (variableExpression == CompletionVariableAst || s_specialVariablesCache.Value.Contains(variableExpression.VariablePath.UserPath))
{
return AstVisitAction.Continue;
}

SaveVariableInfo(variableExpression.VariablePath.UserPath, convertExpression.StaticType, isConstraint: true);
if (firstConvertExpression is not null)
{
SaveVariableInfo(variableExpression.VariablePath.UserPath, firstConvertExpression.StaticType, isConstraint: true);
}
else
{
Type lastAssignedType = assignmentStatementAst.Right is CommandExpressionAst commandExpression
? GetInferredVarTypeFromAst(commandExpression.Expression)
: null;
Copy link
Collaborator

Choose a reason for hiding this comment

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

  1. We can use only CommandExpressionAst for type inference? What other types can be in assignmentStatementAst.Right? I see GetInferredVarTypeFromAst() can process another types.
  2. null - no useful default we can use?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

All the types handled by GetInferredVarTypeFromAst() are wrapped in a CommandExpressionAst. Other possible types include if/else statements, loops, etc. while GetInferredVarTypeFromAst() could get updated to handle this, it's out of scope for this PR.
It's also worth keeping in mind that the original goal of this Ast finder was also just to do a quick and dirty Ast analysis without slowing down variable completion too much. The type inference is only used for the tooltip so I intentionally left out stuff like type inference from commands as that could potentially take several seconds (if not minutes) if many modules need to be imported.

As for useful defaults: I can't think of any. I mean I guess I could add [System.Object] for the rest, but that's not a particularly useful tooltip.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Other possible types include if/else statements, loops, etc. while GetInferredVarTypeFromAst() could get updated to handle this, it's out of scope for this PR.

If it makes sense to improve in future, I'd prefer to use case expression pattern with a todo comment for null default about types we can process in future.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

"If it makes sense" is subjective. If it was up to me alone I'd leave it exactly like it is now as it was never meant to be a comprehensive type analysis.

SaveVariableInfo(variableExpression.VariablePath.UserPath, lastAssignedType, isConstraint: false);
}
}
}
else if (assignmentStatementAst.Left is VariableExpressionAst variableExpression)
Expand Down
11 changes: 11 additions & 0 deletions test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,17 @@ Describe "TabCompletion" -Tags CI {
$res.CompletionMatches[0].CompletionText | Should -BeExactly '$CurrentItem'
}

It 'Should complete variables set with an attribute' {
$res = TabExpansion2 -inputScript '[ValidateNotNull()]$Var1 = 1; $Var'
$res.CompletionMatches[0].CompletionText | Should -BeExactly '$Var1'
}

It 'Should use the first type constraint in a variable assignment in the tooltip' {
$res = TabExpansion2 -inputScript '[int] [string] $Var1 = 1; $Var'
$res.CompletionMatches[0].CompletionText | Should -BeExactly '$Var1'
$res.CompletionMatches[0].ToolTip | Should -BeExactly '[int]$Var1'
}

It 'Should not complete parameter name' {
$res = TabExpansion2 -inputScript 'param($P'
$res.CompletionMatches.Count | Should -Be 0
Expand Down
Loading