/// <summary> /// Visit function and checks that it has write verbose /// </summary> /// <param name="funcAst"></param> /// <returns></returns> public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst funcAst) { if (funcAst == null) { return(AstVisitAction.SkipChildren); } var commandAsts = funcAst.Body.FindAll(testAst => testAst is CommandAst, false); bool hasVerbose = false; if (commandAsts != null) { foreach (CommandAst commandAst in commandAsts) { hasVerbose |= String.Equals(commandAst.GetCommandName(), "Write-Verbose", StringComparison.OrdinalIgnoreCase); } } if (!hasVerbose) { DiagnosticRecords.Add(new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.ProvideVerboseMessageErrorFunction, funcAst.Name), funcAst.Extent, GetName(), DiagnosticSeverity.Information, fileName)); } return(AstVisitAction.Continue); }
/// <summary> /// Visit function and checks that it has write verbose /// </summary> /// <param name="funcAst"></param> /// <returns></returns> public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst funcAst) { if (funcAst == null) { return(AstVisitAction.SkipChildren); } //Write-Verbose is not required for non-advanced functions if (funcAst.Body == null || funcAst.Body.ParamBlock == null || funcAst.Body.ParamBlock.Attributes == null || funcAst.Body.ParamBlock.Parameters == null || !funcAst.Body.ParamBlock.Attributes.Any(attr => attr.TypeName.GetReflectionType() == typeof(CmdletBindingAttribute))) { return(AstVisitAction.Continue); } var commandAsts = funcAst.Body.FindAll(testAst => testAst is CommandAst, false); bool hasVerbose = false; if (commandAsts != null) { foreach (CommandAst commandAst in commandAsts) { hasVerbose |= String.Equals(commandAst.GetCommandName(), "Write-Verbose", StringComparison.OrdinalIgnoreCase); } } if (!hasVerbose) { DiagnosticRecords.Add(new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.ProvideVerboseMessageErrorFunction, funcAst.Name), funcAst.Extent, GetName(), DiagnosticSeverity.Information, fileName)); } return(AstVisitAction.Continue); }
/// <summary> /// AnalyzeScript: Analyzes the ast to check that cmdlets have help. /// </summary> /// <param name="ast">The script's ast</param> /// <param name="fileName">The name of the script</param> /// <returns>A List of diagnostic results of this rule</returns> public IEnumerable <DiagnosticRecord> AnalyzeScript(Ast ast, string fileName) { if (ast == null) { throw new ArgumentNullException(Strings.NullAstErrorMessage); } DiagnosticRecords.Clear(); this.fileName = fileName; exportedFunctions = Helper.Instance.GetExportedFunction(ast); ast.Visit(this); return(DiagnosticRecords); }
/// <summary> /// AnalyzeScript: Checks that objects return in a cmdlet have their types declared in OutputType Attribute /// </summary> /// <param name="ast">The script's ast</param> /// <param name="fileName">The name of the script</param> /// <returns>A List of diagnostic results of this rule</returns> public IEnumerable <DiagnosticRecord> AnalyzeScript(Ast ast, string fileName) { if (ast == null) { throw new ArgumentNullException(Strings.NullAstErrorMessage); } DiagnosticRecords.Clear(); this.fileName = fileName; _classes = ast.FindAll(item => item is TypeDefinitionAst && ((item as TypeDefinitionAst).IsClass), true).Cast <TypeDefinitionAst>(); ast.Visit(this); return(DiagnosticRecords); }
/// <summary> /// Visit function and checks that it has comment help /// </summary> /// <param name="funcAst"></param> /// <returns></returns> public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst funcAst) { if (funcAst == null) { return(AstVisitAction.SkipChildren); } if (exportedFunctions.Contains(funcAst.Name)) { if (funcAst.GetHelpContent() == null) { DiagnosticRecords.Add( new DiagnosticRecord( string.Format(CultureInfo.CurrentCulture, Strings.ProvideCommentHelpError, funcAst.Name), funcAst.Extent, GetName(), DiagnosticSeverity.Information, fileName)); } } return(AstVisitAction.Continue); }
/// <summary> /// AnalyzeScript: Analyzes the ast to check that Write-Verbose is called at least once in every cmdlet or script. /// <param name="ast">The script's ast</param> /// <param name="fileName">The script's file name</param> /// </summary> public IEnumerable <DiagnosticRecord> AnalyzeScript(Ast ast, string fileName) { if (ast == null) { throw new ArgumentNullException(Strings.NullAstErrorMessage); } ClearList(); this.AddNames(new List <string>() { "Configuration", "Workflow" }); DiagnosticRecords.Clear(); this.fileName = fileName; //We only check that advanced functions should have Write-Verbose ast.Visit(this); return(DiagnosticRecords); }
/// <summary> /// AnalyzeScript: Analyzes the ast to check that cmdlets have help. /// </summary> /// <param name="ast">The script's ast</param> /// <param name="fileName">The name of the script</param> /// <returns>A List of diagnostic results of this rule</returns> public IEnumerable <DiagnosticRecord> AnalyzeScript(Ast ast, string fileName) { if (ast == null) { throw new ArgumentNullException(Strings.NullAstErrorMessage); } DiagnosticRecords.Clear(); this.fileName = fileName; exportedFunctions = new HashSet <string>(StringComparer.OrdinalIgnoreCase); List <string> exportFunctionsCmdlet = Helper.Instance.CmdletNameAndAliases("export-modulemember"); // find functions exported IEnumerable <Ast> cmdAsts = ast.FindAll(item => item is CommandAst && exportFunctionsCmdlet.Contains((item as CommandAst).GetCommandName(), StringComparer.OrdinalIgnoreCase), true); CommandInfo exportMM = Helper.Instance.GetCommandInfo("export-modulemember", CommandTypes.Cmdlet); // switch parameters IEnumerable <ParameterMetadata> switchParams = (exportMM != null) ? exportMM.Parameters.Values.Where <ParameterMetadata>(pm => pm.SwitchParameter) : Enumerable.Empty <ParameterMetadata>(); if (exportMM == null) { return(DiagnosticRecords); } foreach (CommandAst cmdAst in cmdAsts) { if (cmdAst.CommandElements == null || cmdAst.CommandElements.Count < 2) { continue; } int i = 1; while (i < cmdAst.CommandElements.Count) { CommandElementAst ceAst = cmdAst.CommandElements[i]; ExpressionAst exprAst = null; if (ceAst is CommandParameterAst) { var paramAst = ceAst as CommandParameterAst; var param = exportMM.ResolveParameter(paramAst.ParameterName); if (param == null) { i += 1; continue; } if (string.Equals(param.Name, "function", StringComparison.OrdinalIgnoreCase)) { // checks for the case of -Function:"verb-nouns" if (paramAst.Argument != null) { exprAst = paramAst.Argument; } // checks for the case of -Function "verb-nouns" else if (i < cmdAst.CommandElements.Count - 1) { i += 1; exprAst = cmdAst.CommandElements[i] as ExpressionAst; } } // some other parameter. we just checks whether the one after this is positional else if (i < cmdAst.CommandElements.Count - 1) { // the next element is a parameter like -module so just move to that one if (cmdAst.CommandElements[i + 1] is CommandParameterAst) { i += 1; continue; } // not a switch parameter so the next element is definitely the argument to this parameter if (paramAst.Argument == null && !switchParams.Contains(param)) { // skips the next element i += 1; } i += 1; continue; } } else if (ceAst is ExpressionAst) { exprAst = ceAst as ExpressionAst; } if (exprAst != null) { // One string so just add this to the list if (exprAst is StringConstantExpressionAst) { exportedFunctions.Add((exprAst as StringConstantExpressionAst).Value); } // Array of the form "v-n", "v-n1" else if (exprAst is ArrayLiteralAst) { exportedFunctions.UnionWith(Helper.Instance.GetStringsFromArrayLiteral(exprAst as ArrayLiteralAst)); } // Array of the form @("v-n", "v-n1") else if (exprAst is ArrayExpressionAst) { ArrayExpressionAst arrExAst = exprAst as ArrayExpressionAst; if (arrExAst.SubExpression != null && arrExAst.SubExpression.Statements != null) { foreach (StatementAst stAst in arrExAst.SubExpression.Statements) { if (stAst is PipelineAst) { PipelineAst pipeAst = stAst as PipelineAst; if (pipeAst.PipelineElements != null) { foreach (CommandBaseAst cmdBaseAst in pipeAst.PipelineElements) { if (cmdBaseAst is CommandExpressionAst) { exportedFunctions.UnionWith(Helper.Instance.GetStringsFromArrayLiteral((cmdBaseAst as CommandExpressionAst).Expression as ArrayLiteralAst)); } } } } } } } } i += 1; } } ast.Visit(this); return(DiagnosticRecords); }
/// <summary> /// Visit function and checks that it is a cmdlet. If yes, then checks that any object returns must have a type declared /// in the output type (the only exception is if the type is object) /// </summary> /// <param name="funcAst"></param> /// <returns></returns> public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst funcAst) { if (funcAst == null || funcAst.Body == null || funcAst.Body.ParamBlock == null || funcAst.Body.ParamBlock.Attributes == null || funcAst.Body.ParamBlock.Attributes.Count == 0 || !funcAst.Body.ParamBlock.Attributes.Any(attr => attr.TypeName.GetReflectionType() == typeof(CmdletBindingAttribute))) { return(AstVisitAction.Continue); } HashSet <string> outputTypes = new HashSet <string>(); foreach (AttributeAst attrAst in funcAst.Body.ParamBlock.Attributes) { if (attrAst.TypeName != null && attrAst.TypeName.GetReflectionType() == typeof(OutputTypeAttribute) && attrAst.PositionalArguments != null) { foreach (ExpressionAst expAst in attrAst.PositionalArguments) { if (expAst is StringConstantExpressionAst) { Type type = Type.GetType((expAst as StringConstantExpressionAst).Value); if (type != null) { outputTypes.Add(type.FullName); } } else { TypeExpressionAst typeAst = expAst as TypeExpressionAst; if (typeAst != null && typeAst.TypeName != null) { if (typeAst.TypeName.GetReflectionType() != null) { outputTypes.Add(typeAst.TypeName.GetReflectionType().FullName); } else { outputTypes.Add(typeAst.TypeName.FullName); } } } } } } #if PSV3 List <Tuple <string, StatementAst> > returnTypes = FindPipelineOutput.OutputTypes(funcAst); #else List <Tuple <string, StatementAst> > returnTypes = FindPipelineOutput.OutputTypes(funcAst, _classes); #endif HashSet <string> specialTypes = new HashSet <string>(StringComparer.OrdinalIgnoreCase); specialTypes.Add(typeof(Unreached).FullName); specialTypes.Add(typeof(Undetermined).FullName); specialTypes.Add(typeof(object).FullName); specialTypes.Add(typeof(void).FullName); specialTypes.Add(typeof(PSCustomObject).FullName); specialTypes.Add(typeof(PSObject).FullName); foreach (Tuple <string, StatementAst> returnType in returnTypes) { string typeName = returnType.Item1; if (String.IsNullOrEmpty(typeName) || specialTypes.Contains(typeName) || outputTypes.Contains(typeName, StringComparer.OrdinalIgnoreCase)) { continue; } else { DiagnosticRecords.Add(new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.UseOutputTypeCorrectlyError, funcAst.Name, typeName), returnType.Item2.Extent, GetName(), DiagnosticSeverity.Information, fileName)); } } return(AstVisitAction.Continue); }