/// <summary> /// AnalyzeDSCResource: Analyzes given DSC Resource /// </summary> /// <param name="ast">The script's ast</param> /// <param name="fileName">The name of the script file being analyzed</param> /// <returns>The results of the analysis</returns> public IEnumerable <DiagnosticRecord> AnalyzeDSCResource(Ast ast, string fileName) { if (ast == null) { throw new ArgumentNullException(Strings.NullAstErrorMessage); } // TODO: Add logic for DSC Resources IEnumerable <Ast> functionDefinitionAsts = Helper.Instance.DscResourceFunctions(ast); IEnumerable <TypeDefinitionAst> classes = ast.FindAll(item => item is TypeDefinitionAst && ((item as TypeDefinitionAst).IsClass), true).Cast <TypeDefinitionAst>(); foreach (FunctionDefinitionAst func in functionDefinitionAsts) { List <Tuple <string, StatementAst> > outputTypes = FindPipelineOutput.OutputTypes(func, classes); if (String.Equals(func.Name, "Set-TargetResource", StringComparison.OrdinalIgnoreCase)) { foreach (Tuple <string, StatementAst> outputType in outputTypes) { yield return(new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.ReturnCorrectTypesForSetTargetResourceFunctionsDSCError), outputType.Item2.Extent, GetName(), DiagnosticSeverity.Information, fileName)); } } else { Dictionary <string, string> returnTypes = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); returnTypes["Get-TargetResource"] = typeof(System.Collections.Hashtable).FullName; returnTypes["Test-TargetResource"] = typeof(bool).FullName; foreach (Tuple <string, StatementAst> outputType in outputTypes) { string type = outputType.Item1; if (String.IsNullOrEmpty(type) || String.Equals(typeof(Unreached).FullName, type, StringComparison.OrdinalIgnoreCase) || String.Equals(typeof(Undetermined).FullName, type, StringComparison.OrdinalIgnoreCase) || String.Equals(typeof(object).FullName, type, StringComparison.OrdinalIgnoreCase) || String.Equals(type, returnTypes[func.Name], StringComparison.OrdinalIgnoreCase)) { continue; } else { yield return(new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.ReturnCorrectTypesForGetTestTargetResourceFunctionsDSCResourceError, func.Name, returnTypes[func.Name], type), outputType.Item2.Extent, GetName(), DiagnosticSeverity.Information, fileName)); } } } } }
/// <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); }