MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections.Generic;5using System.Collections.ObjectModel;6using System.Diagnostics.CodeAnalysis;7using System.Linq;8using System.Reflection;9using System.Runtime.CompilerServices;10using System.Text;11using System.Text.RegularExpressions;12 13using Microsoft.PowerShell;14using System.Management.Automation.Security;15using System.Management.Automation.Subsystem;16using System.Management.Automation.Subsystem.DSC;17using Microsoft.PowerShell.DesiredStateConfiguration.Internal;18 19namespace System.Management.Automation.Language20{21 internal sealed partial class SemanticChecks : AstVisitor2, IAstPostVisitHandler22 {23 private readonly Parser _parser;24 25 private static readonly IsConstantValueVisitor s_isConstantAttributeArgVisitor = new IsConstantValueVisitor26 {27 CheckingAttributeArgument = true,28 };29 30 private static readonly IsConstantValueVisitor s_isConstantAttributeArgForClassVisitor = new IsConstantValueVisitor31 {32 CheckingAttributeArgument = true,33 CheckingClassAttributeArguments = true34 };35 36 private readonly Stack<MemberAst> _memberScopeStack;37 private readonly Stack<ScriptBlockAst> _scopeStack;38 39 internal static void CheckAst(Parser parser, ScriptBlockAst ast)40 {41 SemanticChecks semanticChecker = new SemanticChecks(parser);42 semanticChecker._scopeStack.Push(ast);43 ast.InternalVisit(semanticChecker);44 semanticChecker._scopeStack.Pop();45 Diagnostics.Assert(semanticChecker._memberScopeStack.Count == 0, "Unbalanced push/pop of member scope stack");46 Diagnostics.Assert(semanticChecker._scopeStack.Count == 0, "Unbalanced push/pop of scope stack");47 }48 49 private SemanticChecks(Parser parser)50 {51 _parser = parser;52 _memberScopeStack = new Stack<MemberAst>();53 _scopeStack = new Stack<ScriptBlockAst>();54 }55 56 private bool AnalyzingStaticMember()57 {58 MemberAst currentMember;59 if (_memberScopeStack.Count == 0 || (currentMember = _memberScopeStack.Peek()) == null)60 {61 return false;62 }63 64 var fnMemberAst = currentMember as FunctionMemberAst;65 return fnMemberAst != null ? fnMemberAst.IsStatic : ((PropertyMemberAst)currentMember).IsStatic;66 }67 68 private static bool IsValidAttributeArgument(Ast ast, IsConstantValueVisitor visitor)69 {70 return (bool)ast.Accept(visitor);71 }72 73 private static (string id, string msg) GetNonConstantAttributeArgErrorExpr(IsConstantValueVisitor visitor)74 {75 if (visitor.CheckingClassAttributeArguments)76 {77 return (nameof(ParserStrings.ParameterAttributeArgumentNeedsToBeConstant),78 ParserStrings.ParameterAttributeArgumentNeedsToBeConstant);79 }80 81 return (nameof(ParserStrings.ParameterAttributeArgumentNeedsToBeConstantOrScriptBlock),82 ParserStrings.ParameterAttributeArgumentNeedsToBeConstantOrScriptBlock);83 }84 85 private void CheckForDuplicateParameters(ReadOnlyCollection<ParameterAst> parameters)86 {87 if (parameters.Count > 0)88 {89 HashSet<string> parametersSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);90 foreach (var parameter in parameters)91 {92 string parameterName = parameter.Name.VariablePath.UserPath;93 if (!parametersSet.Add(parameterName))94 {95 _parser.ReportError(parameter.Name.Extent,96 nameof(ParserStrings.DuplicateFormalParameter),97 ParserStrings.DuplicateFormalParameter,98 parameterName);99 }100 101 var voidConstraint =102 parameter.Attributes.OfType<TypeConstraintAst>().FirstOrDefault(static t => typeof(void) == t.TypeName.GetReflectionType());103 104 if (voidConstraint != null)105 {106 _parser.ReportError(voidConstraint.Extent,107 nameof(ParserStrings.VoidTypeConstraintNotAllowed),108 ParserStrings.VoidTypeConstraintNotAllowed);109 }110 }111 }112 }113 114 public override AstVisitAction VisitParamBlock(ParamBlockAst paramBlockAst)115 {116 CheckForDuplicateParameters(paramBlockAst.Parameters);117 118 return AstVisitAction.Continue;119 }120 121 public override AstVisitAction VisitTypeConstraint(TypeConstraintAst typeConstraintAst)122 {123 CheckArrayTypeNameDepth(typeConstraintAst.TypeName, typeConstraintAst.Extent, _parser);124 125 return AstVisitAction.Continue;126 }127 128 public override AstVisitAction VisitAttribute(AttributeAst attributeAst)129 {130 HashSet<string> names = new HashSet<string>(StringComparer.OrdinalIgnoreCase);131 132 bool checkingAttributeOnClass = false;133 AttributeTargets attributeTargets = default(AttributeTargets);134 135 var parent = attributeAst.Parent;136 TypeDefinitionAst typeDefinitionAst = parent as TypeDefinitionAst;137 if (typeDefinitionAst != null)138 {139 checkingAttributeOnClass = true;140 attributeTargets = typeDefinitionAst.IsClass141 ? AttributeTargets.Class142 : typeDefinitionAst.IsEnum143 ? AttributeTargets.Enum144 : AttributeTargets.Interface;145 }146 else if (parent is PropertyMemberAst)147 {148 checkingAttributeOnClass = true;149 attributeTargets = AttributeTargets.Property | AttributeTargets.Field;150 }151 else152 {153 var functionMemberAst = parent as FunctionMemberAst;154 if (functionMemberAst != null)155 {156 checkingAttributeOnClass = true;157 attributeTargets = functionMemberAst.IsConstructor158 ? AttributeTargets.Constructor159 : AttributeTargets.Method;160 }161 else if (parent is ParameterAst && _memberScopeStack.Peek() is FunctionMemberAst)162 {163 checkingAttributeOnClass = true;164 165 // TODO: we aren't actually generating any attributes in the class metadata166 attributeTargets = AttributeTargets.Parameter;167 }168 }169 170 var constantValueVisitor = checkingAttributeOnClass171 ? s_isConstantAttributeArgForClassVisitor172 : s_isConstantAttributeArgVisitor;173 174 if (checkingAttributeOnClass)175 {176 var attributeType = attributeAst.TypeName.GetReflectionAttributeType();177 if (attributeType == null)178 {179 Diagnostics.Assert(_parser.ErrorList.Count > 0, "Symbol resolve should have reported error already");180 }181 else182 {183 var usage = attributeType.GetCustomAttribute<AttributeUsageAttribute>(true);184 if (usage != null && (usage.ValidOn & attributeTargets) == 0)185 {186 _parser.ReportError(attributeAst.Extent,187 nameof(ParserStrings.AttributeNotAllowedOnDeclaration),188 ParserStrings.AttributeNotAllowedOnDeclaration,189 ToStringCodeMethods.Type(attributeType),190 usage.ValidOn);191 }192 193 foreach (var namedArg in attributeAst.NamedArguments)194 {195 var name = namedArg.ArgumentName;196 var members = attributeType.GetMember(name, MemberTypes.Field | MemberTypes.Property,197 BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance |198 BindingFlags.FlattenHierarchy);199 if (members.Length != 1200 || (members[0] is not PropertyInfo && members[0] is not FieldInfo))201 {202 _parser.ReportError(namedArg.Extent,203 nameof(ParserStrings.PropertyNotFoundForAttribute),204 ParserStrings.PropertyNotFoundForAttribute,205 name,206 ToStringCodeMethods.Type(attributeType),207 GetValidNamedAttributeProperties(attributeType));208 209 continue;210 }211 212 var propertyInfo = members[0] as PropertyInfo;213 if (propertyInfo != null)214 {215 if (propertyInfo.GetSetMethod() == null)216 {217 _parser.ReportError(namedArg.Extent,218 nameof(ExtendedTypeSystem.ReadOnlyProperty),219 ExtendedTypeSystem.ReadOnlyProperty,220 name);221 }222 223 continue;224 }225 226 var fieldInfo = (FieldInfo)members[0];227 if (fieldInfo.IsInitOnly || fieldInfo.IsLiteral)228 {229 _parser.ReportError(namedArg.Extent,230 nameof(ExtendedTypeSystem.ReadOnlyProperty),231 ExtendedTypeSystem.ReadOnlyProperty,232 name);233 }234 }235 }236 }237 238 foreach (var namedArg in attributeAst.NamedArguments)239 {240 string name = namedArg.ArgumentName;241 if (!names.Add(name))242 {243 _parser.ReportError(namedArg.Extent,244 nameof(ParserStrings.DuplicateNamedArgument),245 ParserStrings.DuplicateNamedArgument,246 name);247 }248 else249 {250 if (!namedArg.ExpressionOmitted && !IsValidAttributeArgument(namedArg.Argument, constantValueVisitor))251 {252 var error = GetNonConstantAttributeArgErrorExpr(constantValueVisitor);253 _parser.ReportError(namedArg.Argument.Extent, error.id, error.msg);254 }255 }256 }257 258 foreach (var posArg in attributeAst.PositionalArguments)259 {260 if (!IsValidAttributeArgument(posArg, constantValueVisitor))261 {262 var error = GetNonConstantAttributeArgErrorExpr(constantValueVisitor);263 _parser.ReportError(posArg.Extent, error.id, error.msg);264 }265 }266 267 return AstVisitAction.Continue;268 }269 270 private static string GetValidNamedAttributeProperties(Type attributeType)271 {272 var propertyNames = new List<string>();273 PropertyInfo[] properties = attributeType.GetProperties(BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy);274 for (int i = 0; i < properties.Length; i++)275 {276 PropertyInfo propertyInfo = properties[i];277 if (propertyInfo.GetSetMethod() != null)278 {279 propertyNames.Add(propertyInfo.Name);280 }281 }282 283 FieldInfo[] fields = attributeType.GetFields(BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy);284 for (int i = 0; i < fields.Length; i++)285 {286 FieldInfo fieldInfo = fields[i];287 if (!fieldInfo.IsInitOnly && !fieldInfo.IsLiteral)288 {289 propertyNames.Add(fieldInfo.Name);290 }291 }292 293 return string.Join(", ", propertyNames);294 }295 296 public override AstVisitAction VisitParameter(ParameterAst parameterAst)297 {298 bool isClassMethod = parameterAst.Parent.Parent is FunctionMemberAst;299 bool isParamTypeDefined = false;300 foreach (AttributeBaseAst attribute in parameterAst.Attributes)301 {302 if (attribute is TypeConstraintAst)303 {304 if (attribute.TypeName.FullName.Equals(LanguagePrimitives.OrderedAttribute, StringComparison.OrdinalIgnoreCase))305 {306 _parser.ReportError(attribute.Extent,307 nameof(ParserStrings.OrderedAttributeOnlyOnHashLiteralNode),308 ParserStrings.OrderedAttributeOnlyOnHashLiteralNode,309 attribute.TypeName.FullName);310 }311 else312 {313 if (isClassMethod)314 {315 // attribute represent parameter type.316 if (isParamTypeDefined)317 {318 _parser.ReportError(attribute.Extent,319 nameof(ParserStrings.MultipleTypeConstraintsOnMethodParam),320 ParserStrings.MultipleTypeConstraintsOnMethodParam);321 }322 323 isParamTypeDefined = true;324 }325 }326 }327 }328 329 return AstVisitAction.Continue;330 }331 332 public override AstVisitAction VisitTypeExpression(TypeExpressionAst typeExpressionAst)333 {334 CheckArrayTypeNameDepth(typeExpressionAst.TypeName, typeExpressionAst.Extent, _parser);335 336 // If this is access to the [Type] class, it may be suspicious337 if (typeof(Type) == typeExpressionAst.TypeName.GetReflectionType())338 {339 MarkAstParentsAsSuspicious(typeExpressionAst);340 }341 342 return AstVisitAction.Continue;343 }344 345 internal static void CheckArrayTypeNameDepth(ITypeName typeName, IScriptExtent extent, Parser parser)346 {347 int count = 0;348 ITypeName type = typeName;349 while (type is not TypeName)350 {351 count++;352 if (count > 200)353 {354 parser.ReportError(extent,355 nameof(ParserStrings.ScriptTooComplicated),356 ParserStrings.ScriptTooComplicated);357 break;358 }359 360 if (type is ArrayTypeName)361 {362 type = ((ArrayTypeName)type).ElementType;363 }364 else365 {366 break;367 }368 }369 }370 371 public override AstVisitAction VisitTypeDefinition(TypeDefinitionAst typeDefinitionAst)372 {373 AttributeAst dscResourceAttributeAst = null;374 for (int i = 0; i < typeDefinitionAst.Attributes.Count; i++)375 {376 var attr = typeDefinitionAst.Attributes[i];377 if (attr.TypeName.GetReflectionAttributeType() == typeof(DscResourceAttribute))378 {379 dscResourceAttributeAst = attr;380 break;381 }382 }383 384 if (dscResourceAttributeAst != null)385 {386 DscResourceChecker.CheckType(_parser, typeDefinitionAst, dscResourceAttributeAst);387 }388 389 return AstVisitAction.Continue;390 }391 392 public override AstVisitAction VisitFunctionMember(FunctionMemberAst functionMemberAst)393 {394 _memberScopeStack.Push(functionMemberAst);395 396 var body = functionMemberAst.Body;397 if (body.ParamBlock != null)398 {399 _parser.ReportError(body.ParamBlock.Extent,400 nameof(ParserStrings.ParamBlockNotAllowedInMethod),401 ParserStrings.ParamBlockNotAllowedInMethod);402 }403 404 if (body.BeginBlock != null405 || body.ProcessBlock != null406 || body.CleanBlock != null407 || body.DynamicParamBlock != null408 || !body.EndBlock.Unnamed)409 {410 _parser.ReportError(Parser.ExtentFromFirstOf(body.DynamicParamBlock, body.BeginBlock, body.ProcessBlock, body.EndBlock),411 nameof(ParserStrings.NamedBlockNotAllowedInMethod),412 ParserStrings.NamedBlockNotAllowedInMethod);413 }414 415 if (functionMemberAst.IsConstructor && functionMemberAst.ReturnType != null)416 {417 _parser.ReportError(functionMemberAst.ReturnType.Extent,418 nameof(ParserStrings.ConstructorCantHaveReturnType),419 ParserStrings.ConstructorCantHaveReturnType);420 }421 422 // Analysis determines if all paths return and do data flow for variables.423 var allCodePathsReturned = VariableAnalysis.AnalyzeMemberFunction(functionMemberAst);424 if (!allCodePathsReturned && !functionMemberAst.IsReturnTypeVoid())425 {426 _parser.ReportError(functionMemberAst.NameExtent ?? functionMemberAst.Extent,427 nameof(ParserStrings.MethodHasCodePathNotReturn),428 ParserStrings.MethodHasCodePathNotReturn);429 }430 431 return AstVisitAction.Continue;432 }433 434 public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst functionDefinitionAst)435 {436 if (functionDefinitionAst.Parameters != null437 && functionDefinitionAst.Body.ParamBlock != null)438 {439 _parser.ReportError(functionDefinitionAst.Body.ParamBlock.Extent,440 nameof(ParserStrings.OnlyOneParameterListAllowed),441 ParserStrings.OnlyOneParameterListAllowed);442 }443 else if (functionDefinitionAst.Parameters != null)444 {445 CheckForDuplicateParameters(functionDefinitionAst.Parameters);446 }447 448 if (functionDefinitionAst.IsWorkflow)449 {450 _parser.ReportError(functionDefinitionAst.Extent,451 nameof(ParserStrings.WorkflowNotSupportedInPowerShellCore),452 ParserStrings.WorkflowNotSupportedInPowerShellCore);453 }454 455 return AstVisitAction.Continue;456 }457 458 public override AstVisitAction VisitSwitchStatement(SwitchStatementAst switchStatementAst)459 {460 // Parallel flag not allowed461 if ((switchStatementAst.Flags & SwitchFlags.Parallel) == SwitchFlags.Parallel)462 {463 _parser.ReportError(464 switchStatementAst.Extent,465 nameof(ParserStrings.KeywordParameterReservedForFutureUse),466 ParserStrings.KeywordParameterReservedForFutureUse,467 "switch",468 "parallel");469 }470 471 return AstVisitAction.Continue;472 }473 474 private static IEnumerable<string> GetConstantDataStatementAllowedCommands(DataStatementAst dataStatementAst)475 {476 yield return "ConvertFrom-StringData";477 foreach (var allowed in dataStatementAst.CommandsAllowed)478 {479 yield return ((StringConstantExpressionAst)allowed).Value;480 }481 }482 483 public override AstVisitAction VisitDataStatement(DataStatementAst dataStatementAst)484 {485 IEnumerable<string> allowedCommands =486 dataStatementAst.HasNonConstantAllowedCommand ? null : GetConstantDataStatementAllowedCommands(dataStatementAst);487 RestrictedLanguageChecker checker = new RestrictedLanguageChecker(_parser, allowedCommands, null, false);488 dataStatementAst.Body.InternalVisit(checker);489 490 return AstVisitAction.Continue;491 }492 493 public override AstVisitAction VisitForEachStatement(ForEachStatementAst forEachStatementAst)494 {495 // Parallel flag not allowed496 if ((forEachStatementAst.Flags & ForEachFlags.Parallel) == ForEachFlags.Parallel)497 {498 _parser.ReportError(499 forEachStatementAst.Extent,500 nameof(ParserStrings.KeywordParameterReservedForFutureUse),501 ParserStrings.KeywordParameterReservedForFutureUse,502 "foreach",503 "parallel");504 }505 506 if (forEachStatementAst.ThrottleLimit != null)507 {508 _parser.ReportError(509 forEachStatementAst.Extent,510 nameof(ParserStrings.KeywordParameterReservedForFutureUse),511 ParserStrings.KeywordParameterReservedForFutureUse,512 "foreach",513 "throttlelimit");514 }515 516 // Throttle limit must be combined with Parallel flag517 if ((forEachStatementAst.ThrottleLimit != null) &&518 ((forEachStatementAst.Flags & ForEachFlags.Parallel) != ForEachFlags.Parallel))519 {520 _parser.ReportError(521 forEachStatementAst.Extent,522 nameof(ParserStrings.ThrottleLimitRequiresParallelFlag),523 ParserStrings.ThrottleLimitRequiresParallelFlag);524 }525 526 return AstVisitAction.Continue;527 }528 529 public override AstVisitAction VisitTryStatement(TryStatementAst tryStatementAst)530 {531 if (tryStatementAst.CatchClauses.Count <= 1)532 {533 return AstVisitAction.Continue;534 }535 536 for (int i = 0; i < tryStatementAst.CatchClauses.Count - 1; ++i)537 {538 CatchClauseAst block1 = tryStatementAst.CatchClauses[i];539 for (int j = i + 1; j < tryStatementAst.CatchClauses.Count; ++j)540 {541 CatchClauseAst block2 = tryStatementAst.CatchClauses[j];542 543 if (block1.IsCatchAll)544 {545 _parser.ReportError(Parser.Before(block2.Extent),546 nameof(ParserStrings.EmptyCatchNotLast),547 ParserStrings.EmptyCatchNotLast);548 break;549 }550 551 if (block2.IsCatchAll)552 {553 continue;554 }555 556 foreach (TypeConstraintAst typeLiteral1 in block1.CatchTypes)557 {558 Type type1 = typeLiteral1.TypeName.GetReflectionType();559 // If the type can't be resolved yet, there isn't much we can do, so skip it.560 if (type1 == null)561 continue;562 563 foreach (TypeConstraintAst typeLiteral2 in block2.CatchTypes)564 {565 Type type2 = typeLiteral2.TypeName.GetReflectionType();566 // If the type can't be resolved yet, there isn't much we can do, so skip it.567 if (type2 == null)568 continue;569 570 if (type1 == type2 || type2.IsSubclassOf(type1))571 {572 _parser.ReportError(typeLiteral2.Extent,573 nameof(ParserStrings.ExceptionTypeAlreadyCaught),574 ParserStrings.ExceptionTypeAlreadyCaught,575 type2.FullName);576 }577 }578 }579 }580 }581 582 return AstVisitAction.Continue;583 }584 585 /// <summary>586 /// Check that label exists inside the method.587 /// Only call it, when label is present and can be calculated in compile time.588 /// </summary>589 /// <param name="ast">BreakStatementAst or ContinueStatementAst.</param>590 /// <param name="label">Label name. Can be null.</param>591 private void CheckLabelExists(StatementAst ast, string label)592 {593 if (string.IsNullOrEmpty(label))594 {595 return;596 }597 598 Ast parent;599 for (parent = ast.Parent; parent != null; parent = parent.Parent)600 {601 if (parent is FunctionDefinitionAst)602 {603 if (parent.Parent is FunctionMemberAst)604 {605 _parser.ReportError(ast.Extent,606 nameof(ParserStrings.LabelNotFound),607 ParserStrings.LabelNotFound,608 label);609 }610 611 break;612 }613 614 var loop = parent as LoopStatementAst;615 if (loop != null)616 {617 if (LoopFlowException.MatchLoopLabel(label, loop.Label ?? string.Empty))618 break;619 }620 }621 }622 623 /// <summary>624 /// Check that flow doesn't leave finally.625 /// </summary>626 /// <param name="ast"></param>627 /// <param name="label">If label is null, either it's a break/continue to an unknown label628 /// (and unknown does not mean not specified, it means it's an expression we can't evaluate) or we have a return statement.629 /// </param>630 private void CheckForFlowOutOfFinally(Ast ast, string label)631 {632 Ast parent;633 for (parent = ast.Parent; parent != null; parent = parent.Parent)634 {635 if (parent is NamedBlockAst || parent is TrapStatementAst || parent is ScriptBlockAst)636 {637 // Script blocks, traps, and named blocks are all top level asts for a complete method,638 // so if we didn't find a try/catch, the control flow is just leaving the method,639 // it is not leaving some finally, even if the script lock/trap is nested in the finally.640 break;641 }642 643 // If label is not null, we have a break/continue where we know the loop label at compile644 // time. If we can match the label before finding the finally, then we're not flowing out645 // of the finally.646 if (label != null && parent is LabeledStatementAst)647 {648 if (LoopFlowException.MatchLoopLabel(label, ((LabeledStatementAst)parent).Label ?? string.Empty))649 break;650 }651 652 var stmtBlock = parent as StatementBlockAst;653 if (stmtBlock != null)654 {655 var tryStatementAst = stmtBlock.Parent as TryStatementAst;656 if (tryStatementAst != null && tryStatementAst.Finally == stmtBlock)657 {658 _parser.ReportError(ast.Extent,659 nameof(ParserStrings.ControlLeavingFinally),660 ParserStrings.ControlLeavingFinally);661 break;662 }663 }664 }665 }666 667 private static string GetLabel(ExpressionAst expr)668 {669 // We only return null from this method if the label is unknown. If no label is specified,670 // we just use the empty string.671 if (expr == null)672 {673 return string.Empty;674 }675 676 var str = expr as StringConstantExpressionAst;677 return str?.Value;678 }679 680 public override AstVisitAction VisitBreakStatement(BreakStatementAst breakStatementAst)681 {682 string label = GetLabel(breakStatementAst.Label);683 CheckForFlowOutOfFinally(breakStatementAst, label);684 CheckLabelExists(breakStatementAst, label);685 686 return AstVisitAction.Continue;687 }688 689 public override AstVisitAction VisitContinueStatement(ContinueStatementAst continueStatementAst)690 {691 string label = GetLabel(continueStatementAst.Label);692 CheckForFlowOutOfFinally(continueStatementAst, label);693 CheckLabelExists(continueStatementAst, label);694 695 return AstVisitAction.Continue;696 }697 698 private void CheckForReturnStatement(ReturnStatementAst ast)699 {700 if (!(_memberScopeStack.Peek() is FunctionMemberAst functionMemberAst))701 {702 return;703 }704 705 if (ast.Pipeline != null)706 {707 if (functionMemberAst.IsReturnTypeVoid())708 {709 _parser.ReportError(ast.Extent,710 nameof(ParserStrings.VoidMethodHasReturn),711 ParserStrings.VoidMethodHasReturn);712 }713 }714 else715 {716 if (!functionMemberAst.IsReturnTypeVoid())717 {718 _parser.ReportError(ast.Extent,719 nameof(ParserStrings.NonVoidMethodMissingReturnValue),720 ParserStrings.NonVoidMethodMissingReturnValue);721 }722 }723 }724 725 public override AstVisitAction VisitReturnStatement(ReturnStatementAst returnStatementAst)726 {727 CheckForFlowOutOfFinally(returnStatementAst, null);728 CheckForReturnStatement(returnStatementAst);729 return AstVisitAction.Continue;730 }731 732 /// <summary>733 /// Check if the ast is a valid target for assignment. If not, the action reportError is called.734 /// </summary>735 /// <param name="ast">The target of an assignment.</param>736 /// <param name="simpleAssignment">True if the operator '=' is used, false otherwise (e.g. false on '+=' or '++'.).</param>737 /// <param name="reportError">The action called to report any errors.</param>738 private void CheckAssignmentTarget(ExpressionAst ast, bool simpleAssignment, Action<Ast> reportError)739 {740 ArrayLiteralAst arrayLiteralAst = ast as ArrayLiteralAst;741 Ast errorAst = null;742 if (arrayLiteralAst != null)743 {744 if (simpleAssignment)745 {746 CheckArrayLiteralAssignment(arrayLiteralAst, reportError);747 }748 else749 {750 errorAst = arrayLiteralAst;751 }752 }753 else754 {755 ParenExpressionAst parenExpressionAst = ast as ParenExpressionAst;756 if (parenExpressionAst != null)757 {758 ExpressionAst expr = parenExpressionAst.Pipeline.GetPureExpression();759 if (expr == null)760 {761 errorAst = parenExpressionAst.Pipeline;762 }763 else764 {765 CheckAssignmentTarget(expr, simpleAssignment, reportError);766 }767 }768 else if (ast is not ISupportsAssignment)769 {770 errorAst = ast;771 }772 else if (ast is MemberExpressionAst memberExprAst && memberExprAst.NullConditional)773 {774 errorAst = ast;775 }776 else if (ast is IndexExpressionAst indexExprAst && indexExprAst.NullConditional)777 {778 errorAst = ast;779 }780 else if (ast is AttributedExpressionAst)781 {782 // Check for multiple types combined with [ref].783 ExpressionAst expr = ast;784 int converts = 0;785 IScriptExtent errorPosition = null;786 Type lastConvertType = null;787 while (expr is AttributedExpressionAst)788 {789 var convertExpr = expr as ConvertExpressionAst;790 if (convertExpr != null)791 {792 converts += 1;793 lastConvertType = convertExpr.Type.TypeName.GetReflectionType();794 if (typeof(PSReference) == lastConvertType)795 {796 errorPosition = convertExpr.Type.Extent;797 }798 else if (typeof(void) == lastConvertType)799 {800 _parser.ReportError(convertExpr.Type.Extent,801 nameof(ParserStrings.VoidTypeConstraintNotAllowed),802 ParserStrings.VoidTypeConstraintNotAllowed);803 }804 }805 806 expr = ((AttributedExpressionAst)expr).Child;807 }808 809 if ((errorPosition != null) && converts > 1)810 {811 _parser.ReportError(errorPosition,812 nameof(ParserStrings.ReferenceNeedsToBeByItselfInTypeConstraint),813 ParserStrings.ReferenceNeedsToBeByItselfInTypeConstraint);814 }815 else816 {817 var varExprAst = expr as VariableExpressionAst;818 if (varExprAst != null)819 {820 var varPath = varExprAst.VariablePath;821 if (varPath.IsVariable && varPath.IsAnyLocal())822 {823 var specialIndex = 0;824 while (specialIndex < (int)AutomaticVariable.NumberOfAutomaticVariables)825 {826 if (varPath.UnqualifiedPath.Equals(SpecialVariables.AutomaticVariables[specialIndex], StringComparison.OrdinalIgnoreCase))827 {828 var expectedType = SpecialVariables.AutomaticVariableTypes[specialIndex];829 if (expectedType != lastConvertType)830 {831 _parser.ReportError(ast.Extent,832 nameof(ParserStrings.AssignmentStatementToAutomaticNotSupported),833 ParserStrings.AssignmentStatementToAutomaticNotSupported,834 varPath.UnqualifiedPath,835 expectedType);836 }837 838 break;839 }840 841 specialIndex += 1;842 }843 }844 }845 846 CheckAssignmentTarget(expr, simpleAssignment, reportError);847 }848 }849 }850 851 if (errorAst != null)852 {853 reportError(errorAst);854 }855 }856 857 private void CheckArrayLiteralAssignment(ArrayLiteralAst ast, Action<Ast> reportError)858 {859 RuntimeHelpers.EnsureSufficientExecutionStack();860 foreach (var element in ast.Elements)861 {862 CheckAssignmentTarget(element, true, reportError);863 }864 }865 866 public override AstVisitAction VisitAssignmentStatement(AssignmentStatementAst assignmentStatementAst)867 {868 // Make sure LHS is something that can be assigned to.869 CheckAssignmentTarget(assignmentStatementAst.Left, assignmentStatementAst.Operator == TokenKind.Equals,870 ast => _parser.ReportError(ast.Extent,871 nameof(ParserStrings.InvalidLeftHandSide),872 ParserStrings.InvalidLeftHandSide));873 874 return AstVisitAction.Continue;875 }876 877 public override AstVisitAction VisitBinaryExpression(BinaryExpressionAst binaryExpressionAst)878 {879 if (binaryExpressionAst.Operator == TokenKind.AndAnd880 || binaryExpressionAst.Operator == TokenKind.OrOr)881 {882 _parser.ReportError(binaryExpressionAst.ErrorPosition,883 nameof(ParserStrings.InvalidEndOfLine),884 ParserStrings.InvalidEndOfLine,885 binaryExpressionAst.Operator.Text());886 }887 888 return AstVisitAction.Continue;889 }890 891 public override AstVisitAction VisitUnaryExpression(UnaryExpressionAst unaryExpressionAst)892 {893 switch (unaryExpressionAst.TokenKind)894 {895 case TokenKind.PlusPlus:896 case TokenKind.PostfixPlusPlus:897 case TokenKind.MinusMinus:898 case TokenKind.PostfixMinusMinus:899 CheckAssignmentTarget(unaryExpressionAst.Child, false,900 ast => _parser.ReportError(ast.Extent,901 nameof(ParserStrings.OperatorRequiresVariableOrProperty),902 ParserStrings.OperatorRequiresVariableOrProperty,903 unaryExpressionAst.TokenKind.Text()));904 break;905 }906 907 return AstVisitAction.Continue;908 }909 910 public override AstVisitAction VisitConvertExpression(ConvertExpressionAst convertExpressionAst)911 {912 if (convertExpressionAst.Type.TypeName.FullName.Equals(LanguagePrimitives.OrderedAttribute, StringComparison.OrdinalIgnoreCase))913 {914 if (convertExpressionAst.Child is not HashtableAst)915 {916 // We allow the ordered attribute only on hashliteral node.917 // This check covers the following scenario918 // $a = [ordered]10919 _parser.ReportError(convertExpressionAst.Extent,920 nameof(ParserStrings.OrderedAttributeOnlyOnHashLiteralNode),921 ParserStrings.OrderedAttributeOnlyOnHashLiteralNode,922 convertExpressionAst.Type.TypeName.FullName);923 }924 925 // Currently, the type name '[ordered]' is handled specially in PowerShell.926 // When used in a conversion expression, it's only allowed on a hashliteral node, and it's927 // always interpreted as an initializer for a case-insensitive928 // 'System.Collections.Specialized.OrderedDictionary' by the compiler.929 // So, we can return early from here.930 return AstVisitAction.Continue;931 }932 933 if (typeof(PSReference) == convertExpressionAst.Type.TypeName.GetReflectionType())934 {935 // Check for [ref][ref]936 ExpressionAst child = convertExpressionAst.Child;937 bool multipleRefs = false;938 while (true)939 {940 var childAttrExpr = child as AttributedExpressionAst;941 if (childAttrExpr != null)942 {943 var childConvert = childAttrExpr as ConvertExpressionAst;944 if (childConvert != null && typeof(PSReference) == childConvert.Type.TypeName.GetReflectionType())945 {946 multipleRefs = true;947 _parser.ReportError(childConvert.Type.Extent,948 nameof(ParserStrings.ReferenceNeedsToBeByItselfInTypeSequence),949 ParserStrings.ReferenceNeedsToBeByItselfInTypeSequence);950 }951 952 child = childAttrExpr.Child;953 continue;954 }955 956 break;957 }958 959 // Check for [int][ref], but don't add an extra error for [ref][ref].960 var parent = convertExpressionAst.Parent as AttributedExpressionAst;961 while (parent != null)962 {963 var parentConvert = parent as ConvertExpressionAst;964 if (parentConvert != null && !multipleRefs)965 {966 if (typeof(PSReference) == parentConvert.Type.TypeName.GetReflectionType())967 {968 break;969 }970 971 // Don't complain if on the lhs of an assign, there is a different error message, and972 // that is checked as part of assignment.973 var ast = parent.Parent;974 bool skipError = false;975 while (ast != null)976 {977 var statementAst = ast as AssignmentStatementAst;978 if (statementAst != null)979 {980 skipError = statementAst.Left.Find(ast1 => ast1 == convertExpressionAst, searchNestedScriptBlocks: true) != null;981 break;982 }983 984 if (ast is CommandExpressionAst)985 {986 break;987 }988 989 ast = ast.Parent;990 }991 992 if (!skipError)993 {994 _parser.ReportError(convertExpressionAst.Type.Extent,995 nameof(ParserStrings.ReferenceNeedsToBeLastTypeInTypeConversion),996 ParserStrings.ReferenceNeedsToBeLastTypeInTypeConversion);997 }998 }999 1000 parent = parent.Child as AttributedExpressionAst;1001 }1002 }1003 1004 // Converting to Type is suspicious1005 if (typeof(Type) == convertExpressionAst.Type.TypeName.GetReflectionType())1006 {1007 MarkAstParentsAsSuspicious(convertExpressionAst);1008 }1009 1010 return AstVisitAction.Continue;1011 }1012 1013 public override AstVisitAction VisitUsingExpression(UsingExpressionAst usingExpressionAst)1014 {1015 // The parser will parse anything that could start with a variable when1016 // creating a UsingExpressionAst, but we will only support "simple"1017 // property and array references with no side effects.1018 1019 var exprAst = usingExpressionAst.SubExpression;1020 var badExpr = CheckUsingExpression(exprAst);1021 if (badExpr != null)1022 {1023 _parser.ReportError(badExpr.Extent,1024 nameof(ParserStrings.InvalidUsingExpression),1025 ParserStrings.InvalidUsingExpression);1026 }1027 1028 return AstVisitAction.Continue;1029 }1030 1031 private static ExpressionAst CheckUsingExpression(ExpressionAst exprAst)1032 {1033 RuntimeHelpers.EnsureSufficientExecutionStack();1034 if (exprAst is VariableExpressionAst)1035 {1036 return null;1037 }1038 1039 var memberExpr = exprAst as MemberExpressionAst;1040 if (memberExpr != null1041 && memberExpr is not InvokeMemberExpressionAst1042 && memberExpr.Member is StringConstantExpressionAst)1043 {1044 return CheckUsingExpression(memberExpr.Expression);1045 }1046 1047 var indexExpr = exprAst as IndexExpressionAst;1048 if (indexExpr != null)1049 {1050 if (!IsValidAttributeArgument(indexExpr.Index, s_isConstantAttributeArgVisitor))1051 {1052 return indexExpr.Index;1053 }1054 1055 return CheckUsingExpression(indexExpr.Target);1056 }1057 1058 return exprAst;1059 }1060 1061 public override AstVisitAction VisitVariableExpression(VariableExpressionAst variableExpressionAst)1062 {1063 if (variableExpressionAst.Splatted1064 && variableExpressionAst.Parent is not CommandAst1065 && variableExpressionAst.Parent is not UsingExpressionAst)1066 {1067 if (variableExpressionAst.Parent is ArrayLiteralAst && variableExpressionAst.Parent.Parent is CommandAst)1068 {1069 _parser.ReportError(variableExpressionAst.Extent,1070 nameof(ParserStrings.SplattingNotPermittedInArgumentList),1071 ParserStrings.SplattingNotPermittedInArgumentList,1072 variableExpressionAst.VariablePath.UserPath);1073 }1074 else1075 {1076 _parser.ReportError(variableExpressionAst.Extent,1077 nameof(ParserStrings.SplattingNotPermitted),1078 ParserStrings.SplattingNotPermitted,1079 variableExpressionAst.VariablePath.UserPath);1080 }1081 }1082 1083 if (variableExpressionAst.VariablePath.IsVariable)1084 {1085 if (variableExpressionAst.TupleIndex == VariableAnalysis.ForceDynamic1086 && !variableExpressionAst.Assigned1087 && !variableExpressionAst.VariablePath.IsGlobal1088 && !variableExpressionAst.VariablePath.IsScript1089 && !variableExpressionAst.IsConstantVariable()1090 && !SpecialVariables.IsImplicitVariableAccessibleInClassMethod(variableExpressionAst.VariablePath))1091 {1092 _parser.ReportError(variableExpressionAst.Extent,1093 nameof(ParserStrings.VariableNotLocal),1094 ParserStrings.VariableNotLocal);1095 }1096 }1097 1098 if (variableExpressionAst.VariablePath.UserPath.Equals(SpecialVariables.This, StringComparison.OrdinalIgnoreCase))1099 {1100 if (AnalyzingStaticMember())1101 {1102 _parser.ReportError(variableExpressionAst.Extent,1103 nameof(ParserStrings.NonStaticMemberAccessInStaticMember),1104 ParserStrings.NonStaticMemberAccessInStaticMember,1105 variableExpressionAst.VariablePath.UserPath);1106 }1107 }1108 1109 return AstVisitAction.Continue;1110 }1111 1112 public override AstVisitAction VisitHashtable(HashtableAst hashtableAst)1113 {1114 HashSet<string> keys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);1115 foreach (var entry in hashtableAst.KeyValuePairs)1116 {1117 var keyStrAst = entry.Item1 as ConstantExpressionAst;1118 if (keyStrAst != null)1119 {1120 var keyStr = keyStrAst.Value.ToString();1121 if (!keys.Add(keyStr))1122 {1123 string errorId;1124 string errorMsg;1125 if (hashtableAst.IsSchemaElement)1126 {1127 errorId = nameof(ParserStrings.DuplicatePropertyInInstanceDefinition);1128 errorMsg = ParserStrings.DuplicatePropertyInInstanceDefinition;1129 }1130 else1131 {1132 errorId = nameof(ParserStrings.DuplicateKeyInHashLiteral);1133 errorMsg = ParserStrings.DuplicateKeyInHashLiteral;1134 }1135 1136 _parser.ReportError(entry.Item1.Extent, errorId, errorMsg, keyStr);1137 }1138 }1139 }1140 1141 return AstVisitAction.Continue;1142 }1143 1144 public override AstVisitAction VisitAttributedExpression(AttributedExpressionAst attributedExpressionAst)1145 {1146 // The attribute (and not the entire expression) is used for the error extent.1147 var errorAst = attributedExpressionAst.Attribute;1148 while (attributedExpressionAst != null)1149 {1150 if (attributedExpressionAst.Child is VariableExpressionAst)1151 {1152 return AstVisitAction.Continue;1153 }1154 1155 attributedExpressionAst = attributedExpressionAst.Child as AttributedExpressionAst;1156 }1157 1158 _parser.ReportError(errorAst.Extent,1159 nameof(ParserStrings.UnexpectedAttribute),1160 ParserStrings.UnexpectedAttribute,1161 errorAst.TypeName.FullName);1162 1163 return AstVisitAction.Continue;1164 }1165 1166 public override AstVisitAction VisitBlockStatement(BlockStatementAst blockStatementAst)1167 {1168 if (blockStatementAst.IsInWorkflow())1169 {1170 return AstVisitAction.Continue;1171 }1172 1173 _parser.ReportError(blockStatementAst.Kind.Extent,1174 nameof(ParserStrings.UnexpectedKeyword),1175 ParserStrings.UnexpectedKeyword,1176 blockStatementAst.Kind.Text);1177 1178 return AstVisitAction.Continue;1179 }1180 1181 public override AstVisitAction VisitMemberExpression(MemberExpressionAst memberExpressionAst)1182 {1183 CheckMemberAccess(memberExpressionAst);1184 return AstVisitAction.Continue;1185 }1186 1187 public override AstVisitAction VisitInvokeMemberExpression(InvokeMemberExpressionAst memberExpressionAst)1188 {1189 CheckMemberAccess(memberExpressionAst);1190 return AstVisitAction.Continue;1191 }1192 1193 private static void CheckMemberAccess(MemberExpressionAst ast)1194 {1195 // If the member access is not constant, it may be considered suspicious1196 if (ast.Member is not ConstantExpressionAst)1197 {1198 MarkAstParentsAsSuspicious(ast);1199 }1200 