/// <summary> /// Finds the parameter that either contains this node or _is_ this node. /// </summary> /// <param name="callExpression">The containing call expression for this node</param> /// <param name="nodeAtPosition">The current node in the editor</param> /// <returns>The index of the argument (or 0 if this didn't correspond to any argument)</returns> public static int GetActiveParameterIndex(ICallExpression callExpression, INode nodeAtPosition) { var arguments = callExpression?.Arguments?.Elements.ToArray(); if (arguments == null) { return(DefaultArgIndex); } // first check the top level arguments (much cheaper and also very likely) for (int i = 0; i < arguments.Length; ++i) { if (ReferenceEquals(arguments[i], nodeAtPosition)) { return(i); } } // let's try the slightly more expensive check within subfields for (int i = 0; i < arguments.Length; ++i) { var result = NodeWalker.ForEachChildRecursively(arguments[i], node => ReferenceEquals(node, nodeAtPosition) ? node : null); if (result != null) { return(i); } } return(DefaultArgIndex); }
private void AnalyzeVariableStatement(INode node, DiagnosticContext context) { var variableStatement = node.Cast <IVariableStatement>(); // Don't need to check whether a statement was declared at the namespace level, // because we'll skip non-exported variable declarations. // And export keyword is applicable only for top level variables! // We only care about exported statements if ((variableStatement.Flags & NodeFlags.Export) == 0) { return; } var declarations = variableStatement.DeclarationList.Declarations; foreach (var declaration in declarations) { // 'any' is not allowed for top level variables if (declaration.Type?.Kind == TypeScript.Net.Types.SyntaxKind.AnyKeyword || NodeWalker.ForEachChildRecursively <Bool>(declaration.Type, n => n?.Kind == TypeScript.Net.Types.SyntaxKind.AnyKeyword)) { context.Logger.ReportNotAllowedTypeAnyOnTopLevelDeclaration( context.LoggingContext, declaration.LocationForLogging(context.SourceFile), declaration.Name.GetFormattedText(), Name); } // If the variable has an initializer (there is another lint rule that enforces variable initialization, // but that can't be assumed here) that is an object literal, then there should be a non-any type annotation if (declaration.Initializer?.Kind == TypeScript.Net.Types.SyntaxKind.ObjectLiteralExpression) { if (declaration.Type == null) { context.Logger.ReportMissingTypeAnnotationOnTopLevelDeclaration( context.LoggingContext, declaration.LocationForLogging(context.SourceFile), declaration.Name.GetFormattedText(), Name); } else if (declaration.Type.Kind == TypeScript.Net.Types.SyntaxKind.AnyKeyword) { context.Logger.ReportNotAllowedTypeAnyOnTopLevelDeclaration( context.LoggingContext, declaration.LocationForLogging(context.SourceFile), declaration.Name.GetFormattedText(), Name); } } } }
private static INode GetReference(ISourceFile spec, string identifierName) { var firstIdentifier = NodeWalker.ForEachChildRecursively( spec, node => { var identifier = node.As <IIdentifier>(); if (identifier?.Text == identifierName) { return(identifier); } return(null); }); return(firstIdentifier); }
/// <summary> /// Assumes declaration names are unique (full names are not used) /// </summary> private static Dictionary <string, int> CollectAllDeclarationsWithPositions(ISourceFile sourceFile) { var identifersToPositions = new Dictionary <string, int>(); NodeWalker.ForEachChildRecursively <INode>(sourceFile, node => { var declarationStatement = node.As <IDeclarationStatement>(); if (declarationStatement?.Name != null) { identifersToPositions.Add(declarationStatement.Name.Text + declarationStatement.Kind, declarationStatement.Pos); } var variableStatement = node.As <IVariableStatement>(); if (variableStatement != null) { foreach (var declaration in variableStatement.DeclarationList.Declarations) { identifersToPositions.Add(declaration.Name.Cast <IIdentifier>().Text + declaration.Kind, declaration.Pos); } } var enumMember = node.As <IEnumMember>(); if (enumMember != null) { identifersToPositions.Add(enumMember.Name.Text + enumMember.Kind, enumMember.Pos); } var exportSpecifier = node.As <IExportSpecifier>(); if (exportSpecifier != null) { identifersToPositions.Add(exportSpecifier.Name.Text + exportSpecifier.Kind, exportSpecifier.Pos); } return(null); }); return(identifersToPositions); }
/// <summary> /// Create a list of Code Lens annotations a source file to represent the current qualifier /// </summary> private IEnumerable <CodeLens> GetQualifierTypeForModulesAndTopLevelDeclarations(ISourceFile file) { // We want to display CodeLens for at the top of the file, and for namespaces at any depth of the source file // Also, note that namespaces can be on the same line (e.g. namespace Microsoft.Sdk {...} is actually namespace Sdk inside of namespace Microsoft) // In this case we want to display the qualifier for namespace Sdk, since that's what the user cares about // So we will use a line-number key'ed dictionary to make sure that for any line in the file, we only add a single qualifier var result = new List <CodeLens>(); var perLineCodeLenses = new Dictionary <int, CodeLens>(); if (file.Statements.Count > 0) { var firstNode = file.Statements[0]; var firstLine = firstNode.GetStartPosition().ToLineAndColumn().Line; if (GetQualifierAsCodeLens(firstNode, out var codeLens)) { perLineCodeLenses[firstLine] = codeLens; } } NodeWalker.ForEachChildRecursively(file, (node) => { if (node.Kind == SyntaxKind.ModuleDeclaration) { var lineAndColumn = node.GetStartPosition().ToLineAndColumn(); if (GetQualifierAsCodeLens(node, out var codeLens)) { perLineCodeLenses[lineAndColumn.Line] = codeLens; } } return((object)null); }); result.AddRange(perLineCodeLenses.Values.Where(v => v != null)); return(result); }
/// <summary> /// Invokes <paramref name="func"/> callback for each recursive child of the given node. /// Travesal stops when <paramref name="func"/> returns a 'truthy' value which is then returned from the function. /// </summary> public static T ForEachChildRecursively <T>(this INode node, Func <INode, T> func, bool recurseThroughIdentifiers = false) where T : class { return(NodeWalker.ForEachChildRecursively(node, func, recurseThroughIdentifiers)); }