public void AstParser() { ScriptBlockAst sb = System.Management.Automation.Language.Parser.ParseInput(sampleScript, out tokens, out ParseError[] errors); // AST type list https://docs.microsoft.com/en-us/dotnet/api/system.management.automation.language?view=powershellsdk-1.1.0 IEnumerable <Ast> astnodeList = sb.FindAll(delegate(Ast t) { return(true); } , true); foreach (var astnode in astnodeList) { asttypeList.Add(astnode.GetType().ToString()); } }
public static ConfigurationParseResult ExtractConfigurationNames(string path) { // Get the resolved script path. This will throw an exception if the file is not found. string fullPath = Path.GetFullPath(path); Token[] tokens; ParseError[] errors; // Parse the script into an AST, capturing parse errors. Note - even with errors, the // file may still successfully define one or more configurations. ScriptBlockAst ast = Parser.ParseFile(fullPath, out tokens, out errors); IEnumerable <CommandAst> configs = ast.FindAll(IsAstConfiguration, true).Select(x => (CommandAst)x); List <string> requiredModules = configs.Select(GetRequiredModulesFromAst).SelectMany(x => x).Distinct().ToList(); return(new ConfigurationParseResult() { Path = fullPath, Errors = errors, RequiredModules = requiredModules, }); }
/// <summary> /// Get the UsingExpressionAsts out of a script /// </summary> /// <param name="script"></param> /// <returns>a list of UsingExpressionAsts ordered by the StartOffset</returns> private static IEnumerable <Ast> GetUsingExpressionAsts(string script) { if (String.IsNullOrEmpty(script)) { return(null); } ParseError[] errors; Token[] tokens; ScriptBlockAst scriptAst = Parser.ParseInput(script, out tokens, out errors); if (errors.Length != 0) { return(null); } var list = scriptAst.FindAll(ast => ast is UsingExpressionAst, searchNestedScriptBlocks: true).ToList(); if (list.Count > 1) { return(list.OrderBy(a => a.Extent.StartOffset)); } return(list); }
protected static bool DiscoverPesterTests(string source, TestDescribeCollection tests, IMessageLogger logger) { if (!File.Exists(source)) { return(false); } //SendMessage(TestMessageLevel.Informational, string.Format(Resources.SearchingForTestsFormat, source), logger); Token[] tokens; ParseError[] errors; ScriptBlockAst ast = Parser.ParseFile(source, out tokens, out errors); //if (errors.Any()) //{ // foreach (var error in errors) // { // SendMessage(TestMessageLevel.Error, string.Format(Resources.ParserErrorFormat, error.Message), logger); // } // return; //} IEnumerable <Ast> testSuites = ast.FindAll( m => (m is CommandAst) && string.Equals("describe", ((CommandAst)m).GetCommandName(), StringComparison.OrdinalIgnoreCase), true); foreach (Ast describeAst in testSuites) { string describeName = GetFunctionName(logger, describeAst, "describe"); Dictionary <string, TestContext> contextByName = new Dictionary <string, TestContext>(); //IEnumerable<string> tags = GetDescribeTags(logger, describeAst); IEnumerable <Ast> its = describeAst.FindAll(m => (m as CommandAst)?.GetCommandName()?.Equals("it", StringComparison.OrdinalIgnoreCase) == true, true); foreach (Ast test in its) { CommandAst itAst = (CommandAst)test; string itName = GetFunctionName(logger, test, "it"); string contextName = GetParentContextName(logger, test); if (!contextByName.TryGetValue(contextName, out TestContext context)) { context = new TestContext { Name = contextName, Path = source }; contextByName.Add(contextName, context); } // Didn't find the name for the test. Skip it. if (string.IsNullOrEmpty(itName)) { SendMessage(TestMessageLevel.Informational, "Test name was empty. Skipping test.", logger); continue; } TestIt it = new TestIt { Name = itName, Ast = itAst, Path = source, LineNr = itAst.Extent.StartLineNumber }; context.Its.Add(it); } if (contextByName.Count > 0) { TestDescribe describe = new TestDescribe { Name = describeName, Ast = describeAst, Path = source, LineNr = describeAst.Extent.StartLineNumber }; foreach (TestContext context in contextByName.Values) { describe.Contexts.Add(context); } tests.Add(describe); } } return(true); }
/// <summary> /// Checks if a variable is initialized and referenced in either its assignment or children scopes /// </summary> /// <param name="scriptBlockAst">Ast of type ScriptBlock</param> /// <param name="fileName">Name of file containing the ast</param> /// <returns>An enumerable containing diagnostic records</returns> private IEnumerable <DiagnosticRecord> AnalyzeScriptBlockAst(ScriptBlockAst scriptBlockAst, string fileName) { IEnumerable <Ast> assignmentAsts = scriptBlockAst.FindAll(testAst => testAst is AssignmentStatementAst, false); IEnumerable <Ast> varAsts = scriptBlockAst.FindAll(testAst => testAst is VariableExpressionAst, true); IEnumerable <Ast> varsInAssignment; Dictionary <string, AssignmentStatementAst> assignments = new Dictionary <string, AssignmentStatementAst>(StringComparer.OrdinalIgnoreCase); string varKey; bool inAssignment; if (assignmentAsts == null) { yield break; } foreach (AssignmentStatementAst assignmentAst in assignmentAsts) { // Only checks for the case where lhs is a variable. Ignore things like $foo.property VariableExpressionAst assignmentVarAst = assignmentAst.Left as VariableExpressionAst; if (assignmentVarAst != null) { // Ignore if variable is global or environment variable or scope is function if (!Helper.Instance.IsVariableGlobalOrEnvironment(assignmentVarAst, scriptBlockAst) && !assignmentVarAst.VariablePath.IsScript && !string.Equals(assignmentVarAst.VariablePath.DriveName, "function", StringComparison.OrdinalIgnoreCase)) { string variableName = Helper.Instance.VariableNameWithoutScope(assignmentVarAst.VariablePath); if (!assignments.ContainsKey(variableName)) { assignments.Add(variableName, assignmentAst); } } } } if (varAsts != null) { foreach (VariableExpressionAst varAst in varAsts) { varKey = Helper.Instance.VariableNameWithoutScope(varAst.VariablePath); inAssignment = false; if (assignments.ContainsKey(varKey)) { varsInAssignment = assignments[varKey].Left.FindAll(testAst => testAst is VariableExpressionAst, true); // Checks if this variableAst is part of the logged assignment foreach (VariableExpressionAst varInAssignment in varsInAssignment) { inAssignment |= varInAssignment.Equals(varAst); } if (!inAssignment) { assignments.Remove(varKey); } // Check if variable belongs to PowerShell built-in variables if (Helper.Instance.HasSpecialVars(varKey)) { assignments.Remove(varKey); } } } } foreach (string key in assignments.Keys) { yield return(new DiagnosticRecord( string.Format(CultureInfo.CurrentCulture, Strings.UseDeclaredVarsMoreThanAssignmentsError, key), assignments[key].Left.Extent, GetName(), DiagnosticSeverity.Warning, fileName, key)); } }
/// <summary> /// Checks if a variable is initialized and referenced in either its assignment or children scopes /// </summary> /// <param name="scriptBlockAst">Ast of type ScriptBlock</param> /// <param name="fileName">Name of file containing the ast</param> /// <returns>An enumerable containing diagnostic records</returns> private IEnumerable <DiagnosticRecord> AnalyzeScriptBlockAst(ScriptBlockAst scriptBlockAst, string fileName) { IEnumerable <Ast> assignmentAsts = scriptBlockAst.FindAll(testAst => testAst is AssignmentStatementAst, false); IEnumerable <Ast> varAsts = scriptBlockAst.FindAll(testAst => testAst is VariableExpressionAst, true); IEnumerable <Ast> varsInAssignment; Dictionary <string, AssignmentStatementAst> assignmentsDictionary_OrdinalIgnoreCase = new Dictionary <string, AssignmentStatementAst>(StringComparer.OrdinalIgnoreCase); string varKey; bool inAssignment; if (assignmentAsts == null) { yield break; } foreach (AssignmentStatementAst assignmentAst in assignmentAsts) { // Only checks for the case where lhs is a variable. Ignore things like $foo.property VariableExpressionAst assignmentVarAst = assignmentAst.Left as VariableExpressionAst; if (assignmentVarAst == null) { // If the variable is declared in a strongly typed way, e.g. [string]$s = 'foo' then the type is ConvertExpressionAst. // Therefore we need to the VariableExpressionAst from its Child property. var assignmentVarAstAsConvertExpressionAst = assignmentAst.Left as ConvertExpressionAst; if (assignmentVarAstAsConvertExpressionAst != null && assignmentVarAstAsConvertExpressionAst.Child != null) { assignmentVarAst = assignmentVarAstAsConvertExpressionAst.Child as VariableExpressionAst; } } if (assignmentVarAst != null) { // Ignore if variable is global or environment variable or scope is drive qualified variable if (!Helper.Instance.IsVariableGlobalOrEnvironment(assignmentVarAst, scriptBlockAst) && !assignmentVarAst.VariablePath.IsScript && assignmentVarAst.VariablePath.DriveName == null) { string variableName = Helper.Instance.VariableNameWithoutScope(assignmentVarAst.VariablePath); if (!assignmentsDictionary_OrdinalIgnoreCase.ContainsKey(variableName)) { assignmentsDictionary_OrdinalIgnoreCase.Add(variableName, assignmentAst); } } } } if (varAsts != null) { foreach (VariableExpressionAst varAst in varAsts) { varKey = Helper.Instance.VariableNameWithoutScope(varAst.VariablePath); inAssignment = false; if (assignmentsDictionary_OrdinalIgnoreCase.ContainsKey(varKey)) { varsInAssignment = assignmentsDictionary_OrdinalIgnoreCase[varKey].Left.FindAll(testAst => testAst is VariableExpressionAst, true); // Checks if this variableAst is part of the logged assignment foreach (VariableExpressionAst varInAssignment in varsInAssignment) { // Try casting to AssignmentStatementAst to be able to catch case where a variable is assigned more than once (https://github.com/PowerShell/PSScriptAnalyzer/issues/833) var varInAssignmentAsStatementAst = varInAssignment.Parent as AssignmentStatementAst; var varAstAsAssignmentStatementAst = varAst.Parent as AssignmentStatementAst; if (varAstAsAssignmentStatementAst != null) { if (varAstAsAssignmentStatementAst.Operator == TokenKind.Equals) { if (varInAssignmentAsStatementAst != null) { inAssignment = varInAssignmentAsStatementAst.Left.Extent.Text.Equals(varAstAsAssignmentStatementAst.Left.Extent.Text, StringComparison.OrdinalIgnoreCase); } else { inAssignment = varInAssignment.Equals(varAst); } } } else { inAssignment = varInAssignment.Equals(varAst); } } if (!inAssignment) { assignmentsDictionary_OrdinalIgnoreCase.Remove(varKey); } // Check if variable belongs to PowerShell built-in variables if (Helper.Instance.HasSpecialVars(varKey)) { assignmentsDictionary_OrdinalIgnoreCase.Remove(varKey); } } } } // Detect usages of Get-Variable var getVariableCmdletNamesAndAliases = Helper.Instance.CmdletNameAndAliases("Get-Variable"); IEnumerable <Ast> getVariableCommandAsts = scriptBlockAst.FindAll(testAst => testAst is CommandAst commandAst && getVariableCmdletNamesAndAliases.Contains(commandAst.GetCommandName(), StringComparer.OrdinalIgnoreCase), true); foreach (CommandAst getVariableCommandAst in getVariableCommandAsts) { var commandElements = getVariableCommandAst.CommandElements.ToList(); // The following extracts the variable name only in the simplest possibe case 'Get-Variable variableName' if (commandElements.Count == 2 && commandElements[1] is StringConstantExpressionAst constantExpressionAst) { var variableName = constantExpressionAst.Value; if (assignmentsDictionary_OrdinalIgnoreCase.ContainsKey(variableName)) { assignmentsDictionary_OrdinalIgnoreCase.Remove(variableName); } } } foreach (string key in assignmentsDictionary_OrdinalIgnoreCase.Keys) { yield return(new DiagnosticRecord( string.Format(CultureInfo.CurrentCulture, Strings.UseDeclaredVarsMoreThanAssignmentsError, key), assignmentsDictionary_OrdinalIgnoreCase[key].Left.Extent, GetName(), DiagnosticSeverity.Warning, fileName, key)); } }