public static ISymbol?GetSymbol(SyntaxNode?node, SemanticModel semanticModel) { return(GetDeclaredSymbol(node, semanticModel) ?? GetReferencedSymbol(node, semanticModel)); }
/// <summary> /// Gets the set of logging classes containing methods to output. /// </summary> public IReadOnlyList <LoggerClass> GetLogClasses(IEnumerable <ClassDeclarationSyntax> classes) { INamedTypeSymbol loggerMessageAttribute = _compilation.GetTypeByMetadataName(LoggerMessageAttribute); if (loggerMessageAttribute == null) { // nothing to do if this type isn't available return(Array.Empty <LoggerClass>()); } INamedTypeSymbol loggerSymbol = _compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.ILogger"); if (loggerSymbol == null) { // nothing to do if this type isn't available return(Array.Empty <LoggerClass>()); } INamedTypeSymbol logLevelSymbol = _compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.LogLevel"); if (logLevelSymbol == null) { // nothing to do if this type isn't available return(Array.Empty <LoggerClass>()); } INamedTypeSymbol exceptionSymbol = _compilation.GetTypeByMetadataName("System.Exception"); if (exceptionSymbol == null) { Diag(DiagnosticDescriptors.MissingRequiredType, null, "System.Exception"); return(Array.Empty <LoggerClass>()); } INamedTypeSymbol enumerableSymbol = _compilation.GetSpecialType(SpecialType.System_Collections_IEnumerable); INamedTypeSymbol stringSymbol = _compilation.GetSpecialType(SpecialType.System_String); var results = new List <LoggerClass>(); var ids = new HashSet <int>(); // we enumerate by syntax tree, to minimize the need to instantiate semantic models (since they're expensive) foreach (var group in classes.GroupBy(x => x.SyntaxTree)) { SemanticModel?sm = null; foreach (ClassDeclarationSyntax classDec in group) { // stop if we're asked to _cancellationToken.ThrowIfCancellationRequested(); LoggerClass?lc = null; string nspace = string.Empty; string? loggerField = null; bool multipleLoggerFields = false; ids.Clear(); foreach (MemberDeclarationSyntax member in classDec.Members) { var method = member as MethodDeclarationSyntax; if (method == null) { // we only care about methods continue; } sm ??= _compilation.GetSemanticModel(classDec.SyntaxTree); IMethodSymbol logMethodSymbol = sm.GetDeclaredSymbol(method, _cancellationToken) as IMethodSymbol; Debug.Assert(logMethodSymbol != null, "log method is present."); (int eventId, int?level, string message, string?eventName, bool skipEnabledCheck) = (-1, null, string.Empty, null, false); foreach (AttributeListSyntax mal in method.AttributeLists) { foreach (AttributeSyntax ma in mal.Attributes) { IMethodSymbol attrCtorSymbol = sm.GetSymbolInfo(ma, _cancellationToken).Symbol as IMethodSymbol; if (attrCtorSymbol == null || !loggerMessageAttribute.Equals(attrCtorSymbol.ContainingType, SymbolEqualityComparer.Default)) { // badly formed attribute definition, or not the right attribute continue; } bool hasMisconfiguredInput = false; ImmutableArray <AttributeData>?boundAttrbutes = logMethodSymbol?.GetAttributes(); if (boundAttrbutes == null) { continue; } foreach (AttributeData attributeData in boundAttrbutes) { // supports: [LoggerMessage(0, LogLevel.Warning, "custom message")] // supports: [LoggerMessage(eventId: 0, level: LogLevel.Warning, message: "custom message")] if (attributeData.ConstructorArguments.Any()) { foreach (TypedConstant typedConstant in attributeData.ConstructorArguments) { if (typedConstant.Kind == TypedConstantKind.Error) { hasMisconfiguredInput = true; } } ImmutableArray <TypedConstant> items = attributeData.ConstructorArguments; Debug.Assert(items.Length == 3); eventId = items[0].IsNull ? -1 : (int)GetItem(items[0]); level = items[1].IsNull ? null : (int?)GetItem(items[1]); message = items[2].IsNull ? "" : (string)GetItem(items[2]); } // argument syntax takes parameters. e.g. EventId = 0 // supports: e.g. [LoggerMessage(EventId = 0, Level = LogLevel.Warning, Message = "custom message")] if (attributeData.NamedArguments.Any()) { foreach (KeyValuePair <string, TypedConstant> namedArgument in attributeData.NamedArguments) { TypedConstant typedConstant = namedArgument.Value; if (typedConstant.Kind == TypedConstantKind.Error) { hasMisconfiguredInput = true; } else { TypedConstant value = namedArgument.Value; switch (namedArgument.Key) { case "EventId": eventId = (int)GetItem(value); break; case "Level": level = value.IsNull ? null : (int?)GetItem(value); break; case "SkipEnabledCheck": skipEnabledCheck = (bool)GetItem(value); break; case "EventName": eventName = (string?)GetItem(value); break; case "Message": message = value.IsNull ? "" : (string)GetItem(value); break; } } } } } if (hasMisconfiguredInput) { // skip further generator execution and let compiler generate the errors break; } IMethodSymbol?methodSymbol = sm.GetDeclaredSymbol(method, _cancellationToken); if (methodSymbol != null) { var lm = new LoggerMethod { Name = methodSymbol.Name, Level = level, Message = message, EventId = eventId, EventName = eventName, IsExtensionMethod = methodSymbol.IsExtensionMethod, Modifiers = method.Modifiers.ToString(), SkipEnabledCheck = skipEnabledCheck }; ExtractTemplates(message, lm.TemplateMap, lm.TemplateList); bool keepMethod = true; // whether or not we want to keep the method definition or if it's got errors making it so we should discard it instead if (lm.Name[0] == '_') { // can't have logging method names that start with _ since that can lead to conflicting symbol names // because the generated symbols start with _ Diag(DiagnosticDescriptors.InvalidLoggingMethodName, method.Identifier.GetLocation()); keepMethod = false; } if (!methodSymbol.ReturnsVoid) { // logging methods must return void Diag(DiagnosticDescriptors.LoggingMethodMustReturnVoid, method.ReturnType.GetLocation()); keepMethod = false; } if (method.Arity > 0) { // we don't currently support generic methods Diag(DiagnosticDescriptors.LoggingMethodIsGeneric, method.Identifier.GetLocation()); keepMethod = false; } bool isStatic = false; bool isPartial = false; foreach (SyntaxToken mod in method.Modifiers) { if (mod.IsKind(SyntaxKind.PartialKeyword)) { isPartial = true; } else if (mod.IsKind(SyntaxKind.StaticKeyword)) { isStatic = true; } } if (!isPartial) { Diag(DiagnosticDescriptors.LoggingMethodMustBePartial, method.GetLocation()); keepMethod = false; } if (method.Body != null) { Diag(DiagnosticDescriptors.LoggingMethodHasBody, method.Body.GetLocation()); keepMethod = false; } // ensure there are no duplicate ids. if (ids.Contains(lm.EventId)) { Diag(DiagnosticDescriptors.ShouldntReuseEventIds, ma.GetLocation(), lm.EventId, classDec.Identifier.Text); } else { _ = ids.Add(lm.EventId); } string msg = lm.Message; if (msg.StartsWith("INFORMATION:", StringComparison.OrdinalIgnoreCase) || msg.StartsWith("INFO:", StringComparison.OrdinalIgnoreCase) || msg.StartsWith("WARNING:", StringComparison.OrdinalIgnoreCase) || msg.StartsWith("WARN:", StringComparison.OrdinalIgnoreCase) || msg.StartsWith("ERROR:", StringComparison.OrdinalIgnoreCase) || msg.StartsWith("ERR:", StringComparison.OrdinalIgnoreCase)) { Diag(DiagnosticDescriptors.RedundantQualifierInMessage, ma.GetLocation(), method.Identifier.ToString()); } bool foundLogger = false; bool foundException = false; bool foundLogLevel = level != null; foreach (IParameterSymbol paramSymbol in methodSymbol.Parameters) { string paramName = paramSymbol.Name; if (string.IsNullOrWhiteSpace(paramName)) { // semantic problem, just bail quietly keepMethod = false; break; } ITypeSymbol paramTypeSymbol = paramSymbol !.Type; if (paramTypeSymbol is IErrorTypeSymbol) { // semantic problem, just bail quietly keepMethod = false; break; } string typeName = paramTypeSymbol.ToDisplayString( SymbolDisplayFormat.FullyQualifiedFormat.WithMiscellaneousOptions( SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier)); var lp = new LoggerParameter { Name = paramName, Type = typeName, IsLogger = !foundLogger && IsBaseOrIdentity(paramTypeSymbol !, loggerSymbol), IsException = !foundException && IsBaseOrIdentity(paramTypeSymbol !, exceptionSymbol), IsLogLevel = !foundLogLevel && IsBaseOrIdentity(paramTypeSymbol !, logLevelSymbol), IsEnumerable = IsBaseOrIdentity(paramTypeSymbol !, enumerableSymbol) && !IsBaseOrIdentity(paramTypeSymbol !, stringSymbol), }; foundLogger |= lp.IsLogger; foundException |= lp.IsException; foundLogLevel |= lp.IsLogLevel; if (lp.IsLogger && lm.TemplateMap.ContainsKey(paramName)) { Diag(DiagnosticDescriptors.ShouldntMentionLoggerInMessage, paramSymbol.Locations[0], paramName); } else if (lp.IsException && lm.TemplateMap.ContainsKey(paramName)) { Diag(DiagnosticDescriptors.ShouldntMentionExceptionInMessage, paramSymbol.Locations[0], paramName); } else if (lp.IsLogLevel && lm.TemplateMap.ContainsKey(paramName)) { Diag(DiagnosticDescriptors.ShouldntMentionLogLevelInMessage, paramSymbol.Locations[0], paramName); } else if (lp.IsLogLevel && level != null && !lm.TemplateMap.ContainsKey(paramName)) { Diag(DiagnosticDescriptors.ArgumentHasNoCorrespondingTemplate, paramSymbol.Locations[0], paramName); } else if (lp.IsTemplateParameter && !lm.TemplateMap.ContainsKey(paramName)) { Diag(DiagnosticDescriptors.ArgumentHasNoCorrespondingTemplate, paramSymbol.Locations[0], paramName); } if (paramName[0] == '_') { // can't have logging method parameter names that start with _ since that can lead to conflicting symbol names // because all generated symbols start with _ Diag(DiagnosticDescriptors.InvalidLoggingMethodParameterName, paramSymbol.Locations[0]); } lm.AllParameters.Add(lp); if (lp.IsTemplateParameter) { lm.TemplateParameters.Add(lp); } } if (keepMethod) { if (isStatic && !foundLogger) { Diag(DiagnosticDescriptors.MissingLoggerArgument, method.GetLocation(), lm.Name); keepMethod = false; } else if (!isStatic && foundLogger) { Diag(DiagnosticDescriptors.LoggingMethodShouldBeStatic, method.GetLocation()); } else if (!isStatic && !foundLogger) { if (loggerField == null) { (loggerField, multipleLoggerFields) = FindLoggerField(sm, classDec, loggerSymbol); } if (multipleLoggerFields) { Diag(DiagnosticDescriptors.MultipleLoggerFields, method.GetLocation(), classDec.Identifier.Text); keepMethod = false; } else if (loggerField == null) { Diag(DiagnosticDescriptors.MissingLoggerField, method.GetLocation(), classDec.Identifier.Text); keepMethod = false; } else { lm.LoggerField = loggerField; } } if (level == null && !foundLogLevel) { Diag(DiagnosticDescriptors.MissingLogLevel, method.GetLocation()); keepMethod = false; } foreach (KeyValuePair <string, string> t in lm.TemplateMap) { bool found = false; foreach (LoggerParameter p in lm.AllParameters) { if (t.Key.Equals(p.Name, StringComparison.OrdinalIgnoreCase)) { found = true; break; } } if (!found) { Diag(DiagnosticDescriptors.TemplateHasNoCorrespondingArgument, ma.GetLocation(), t.Key); } } } if (lc == null) { // determine the namespace the class is declared in, if any SyntaxNode?potentialNamespaceParent = classDec.Parent; while (potentialNamespaceParent != null && potentialNamespaceParent is not NamespaceDeclarationSyntax #if ROSLYN4_0_OR_GREATER && potentialNamespaceParent is not FileScopedNamespaceDeclarationSyntax #endif ) { potentialNamespaceParent = potentialNamespaceParent.Parent; } #if ROSLYN4_0_OR_GREATER if (potentialNamespaceParent is BaseNamespaceDeclarationSyntax namespaceParent) #else if (potentialNamespaceParent is NamespaceDeclarationSyntax namespaceParent) #endif { nspace = namespaceParent.Name.ToString(); while (true) { namespaceParent = namespaceParent.Parent as NamespaceDeclarationSyntax; if (namespaceParent == null) { break; } nspace = $"{namespaceParent.Name}.{nspace}"; } } } if (keepMethod) { lc ??= new LoggerClass { Keyword = classDec.Keyword.ValueText, Namespace = nspace, Name = classDec.Identifier.ToString() + classDec.TypeParameterList, Constraints = classDec.ConstraintClauses.ToString(), ParentClass = null, }; LoggerClass currentLoggerClass = lc; var parentLoggerClass = (classDec.Parent as TypeDeclarationSyntax); bool IsAllowedKind(SyntaxKind kind) => kind == SyntaxKind.ClassDeclaration || kind == SyntaxKind.StructDeclaration || kind == SyntaxKind.RecordDeclaration; while (parentLoggerClass != null && IsAllowedKind(parentLoggerClass.Kind())) { currentLoggerClass.ParentClass = new LoggerClass { Keyword = parentLoggerClass.Keyword.ValueText, Namespace = nspace, Name = parentLoggerClass.Identifier.ToString() + parentLoggerClass.TypeParameterList, Constraints = parentLoggerClass.ConstraintClauses.ToString(), ParentClass = null, }; currentLoggerClass = currentLoggerClass.ParentClass; parentLoggerClass = (parentLoggerClass.Parent as TypeDeclarationSyntax); } lc.Methods.Add(lm); } } } } } if (lc != null) { results.Add(lc); } } } return(results); }
public override SyntaxNode?Visit(SyntaxNode?node) => base.Visit(node);
public static TNode?GetAncestorOrThis <TNode>(this SyntaxNode?node) where TNode : SyntaxNode { return(node?.GetAncestorsOrThis <TNode>().FirstOrDefault()); }
public override bool IsOnPropertyDeclarationHeader(SyntaxNode root, int position, [NotNullWhen(true)] out SyntaxNode?propertyDeclaration) { var node = TryGetAncestorForLocation <PropertyDeclarationSyntax>(root, position); propertyDeclaration = node; if (propertyDeclaration == null) { return(false); } RoslynDebug.AssertNotNull(node); return(IsOnHeader(root, position, node, node.Identifier)); }
public static (SyntaxToken openBrace, SyntaxToken closeBrace) GetBracePair(this SyntaxNode?node) => node.GetBraces();
/// <summary> /// Gets the root node of the current syntax tree if the syntax tree has already been parsed and the tree is still cached. /// In almost all cases, you should call <see cref="GetSyntaxRootAsync"/> to fetch the root node, which will parse /// the document if necessary. /// </summary> public bool TryGetSyntaxRoot([NotNullWhen(returnValue: true)] out SyntaxNode?root) { root = null; return(this.TryGetSyntaxTree(out var tree) && tree.TryGetRoot(out root) && root != null); }
public abstract SyntaxNode?GetCallTargetNode(SyntaxNode?node);
public abstract SyntaxNode?GetInvocationExpressionNode(SyntaxNode?node);
public abstract SyntaxNode?GetMemberAccessExpressionNode(SyntaxNode?node);
public abstract SyntaxNode?GetMemberAccessNameNode(SyntaxNode?node);
public abstract SyntaxNode?GetAssignmentRightNode(SyntaxNode?node);
public abstract ITypeSymbol?GetClassDeclarationTypeSymbol(SyntaxNode?node, SemanticModel semanticModel);
public abstract ITypeSymbol?GetEnclosingTypeSymbol(SyntaxNode?node, SemanticModel semanticModel);
public abstract IMethodSymbol?GetCalleeMethodSymbol(SyntaxNode?node, SemanticModel semanticModel);
public abstract SyntaxNode?GetDefaultValueForAnOptionalParameter(SyntaxNode?declNode, int paramIndex);
internal static SyntaxNode?AsRootOfNewTreeWithOptionsFrom(this SyntaxNode?node, SyntaxTree oldTree) { return(node != null?oldTree.WithRootAndOptions(node, oldTree.Options).GetRoot() : null); }
public abstract IEnumerable <SyntaxNode> GetObjectInitializerExpressionNodes(SyntaxNode?node);
public override async Task RegisterCodeFixesAsync(CodeFixContext context) { Diagnostic?diagnostic = context.Diagnostics.First(); SyntaxNode?root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); var syntaxNode = (ExpressionSyntax)root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); CSharpUtils.ContainingFunctionData container = CSharpUtils.GetContainingFunction(syntaxNode); if (container.BlockOrExpression is null) { return; } SemanticModel?semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); ISymbol?enclosingSymbol = semanticModel.GetEnclosingSymbol(diagnostic.Location.SourceSpan.Start, context.CancellationToken); if (enclosingSymbol is null) { return; } bool convertToAsync = !container.IsAsync && Utils.HasAsyncCompatibleReturnType(enclosingSymbol as IMethodSymbol); if (convertToAsync) { // We don't support this yet, and we don't want to take the sync method path in this case. // The user will have to fix this themselves. return; } Regex lookupKey = (container.IsAsync || convertToAsync) ? CommonInterest.FileNamePatternForMethodsThatSwitchToMainThread : CommonInterest.FileNamePatternForMethodsThatAssertMainThread; string[] options = diagnostic.Properties[lookupKey.ToString()].Split('\n'); if (options.Length > 0) { // For any symbol lookups, we want to consider the position of the very first statement in the block. int positionForLookup = container.BlockOrExpression.GetLocation().SourceSpan.Start + 1; Lazy <ISymbol> cancellationTokenSymbol = new Lazy <ISymbol>(() => Utils.FindCancellationToken(semanticModel, positionForLookup, context.CancellationToken).FirstOrDefault()); foreach (var option in options) { // We're looking for methods that either require no parameters, // or (if we have one to give) that have just one parameter that is a CancellationToken. IMethodSymbol?proposedMethod = Utils.FindMethodGroup(semanticModel, option) .FirstOrDefault(m => !m.Parameters.Any(p => !p.HasExplicitDefaultValue) || (cancellationTokenSymbol.Value is object && m.Parameters.Length == 1 && Utils.IsCancellationTokenParameter(m.Parameters[0]))); if (proposedMethod is null) { // We can't find it, so don't offer to use it. continue; } if (proposedMethod.IsStatic) { OfferFix(option); } else { foreach (Tuple <bool, ISymbol>?candidate in Utils.FindInstanceOf(proposedMethod.ContainingType, semanticModel, positionForLookup, context.CancellationToken)) { if (candidate.Item1) { OfferFix($"{candidate.Item2.Name}.{proposedMethod.Name}"); } else { OfferFix($"{candidate.Item2.ContainingNamespace}.{candidate.Item2.ContainingType.Name}.{candidate.Item2.Name}.{proposedMethod.Name}"); } } } void OfferFix(string fullyQualifiedMethod) { context.RegisterCodeFix(CodeAction.Create($"Add call to {fullyQualifiedMethod}", ct => Fix(fullyQualifiedMethod, proposedMethod, cancellationTokenSymbol), fullyQualifiedMethod), context.Diagnostics); } } } Task <Document> Fix(string fullyQualifiedMethod, IMethodSymbol methodSymbol, Lazy <ISymbol> cancellationTokenSymbol) { int typeAndMethodDelimiterIndex = fullyQualifiedMethod.LastIndexOf('.'); IdentifierNameSyntax methodName = SyntaxFactory.IdentifierName(fullyQualifiedMethod.Substring(typeAndMethodDelimiterIndex + 1)); ExpressionSyntax invokedMethod = CSharpUtils.MemberAccess(fullyQualifiedMethod.Substring(0, typeAndMethodDelimiterIndex).Split('.'), methodName); InvocationExpressionSyntax?invocationExpression = SyntaxFactory.InvocationExpression(invokedMethod); IParameterSymbol? cancellationTokenParameter = methodSymbol.Parameters.FirstOrDefault(Utils.IsCancellationTokenParameter); if (cancellationTokenParameter is object && cancellationTokenSymbol.Value is object) { ArgumentSyntax?arg = SyntaxFactory.Argument(SyntaxFactory.IdentifierName(cancellationTokenSymbol.Value.Name)); if (methodSymbol.Parameters.IndexOf(cancellationTokenParameter) > 0) { arg = arg.WithNameColon(SyntaxFactory.NameColon(SyntaxFactory.IdentifierName(cancellationTokenParameter.Name))); } invocationExpression = invocationExpression.AddArgumentListArguments(arg); } ExpressionSyntax? awaitExpression = container.IsAsync ? SyntaxFactory.AwaitExpression(invocationExpression) : null; ExpressionStatementSyntax?addedStatement = SyntaxFactory.ExpressionStatement(awaitExpression ?? invocationExpression) .WithAdditionalAnnotations(Simplifier.Annotation, Formatter.Annotation); var initialBlockSyntax = container.BlockOrExpression as BlockSyntax; if (initialBlockSyntax is null) { initialBlockSyntax = SyntaxFactory.Block(SyntaxFactory.ReturnStatement((ExpressionSyntax)container.BlockOrExpression)) .WithAdditionalAnnotations(Formatter.Annotation); } BlockSyntax?newBlock = initialBlockSyntax.WithStatements(initialBlockSyntax.Statements.Insert(0, addedStatement)); return(Task.FromResult(context.Document.WithSyntaxRoot(root.ReplaceNode(container.BlockOrExpression, newBlock)))); } }
protected abstract IEnumerable <SyntaxNode> GetCallArgumentExpressionNodes(SyntaxNode?node, CallKinds callKind);
internal WithTwoChildren(InternalSyntax.SyntaxList green, SyntaxNode?parent, int position) : base(green, parent, position) { }
public abstract IEnumerable <SyntaxNode> GetDescendantAssignmentExpressionNodes(SyntaxNode?node);
public override bool IsOnTypeHeader(SyntaxNode root, int position, bool fullHeader, [NotNullWhen(true)] out SyntaxNode?typeDeclaration) { var node = TryGetAncestorForLocation <BaseTypeDeclarationSyntax>(root, position); typeDeclaration = node; if (node == null) { return(false); } var lastToken = (node as TypeDeclarationSyntax)?.TypeParameterList?.GetLastToken() ?? node.Identifier; if (fullHeader) { lastToken = node.BaseList?.GetLastToken() ?? lastToken; } return(IsOnHeader(root, position, node, lastToken)); }
public abstract IEnumerable <SyntaxNode> GetDescendantMemberAccessExpressionNodes(SyntaxNode?node);
public override bool IsOnLocalDeclarationHeader(SyntaxNode root, int position, [NotNullWhen(true)] out SyntaxNode?localDeclaration) { var node = TryGetAncestorForLocation <LocalDeclarationStatementSyntax>(root, position); localDeclaration = node; if (localDeclaration == null) { return(false); } var initializersExpressions = node !.Declaration.Variables .Where(v => v.Initializer != null) .SelectAsArray(initializedV => initializedV.Initializer !.Value); return(IsOnHeader(root, position, node, node, holes: initializersExpressions)); }
// returns true if node is an ObjectCreationExpression and is under a FieldDeclaration node public abstract bool IsObjectCreationExpressionUnderFieldDeclaration(SyntaxNode?node);
private async Task <Document> AddImportDirectivesFromSymbolAnnotationsAsync( Document document, CodeGenerationPreferences preferences, IEnumerable <SyntaxNode> syntaxNodes, IAddImportsService addImportsService, SyntaxGenerator generator, bool allowInHiddenRegions, CancellationToken cancellationToken) { using var _ = PooledDictionary <INamespaceSymbol, SyntaxNode> .GetInstance(out var importToSyntax); var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var model = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); SyntaxNode?first = null, last = null; var annotatedNodes = syntaxNodes.Where(x => x.HasAnnotations(SymbolAnnotation.Kind)); foreach (var annotatedNode in annotatedNodes) { cancellationToken.ThrowIfCancellationRequested(); if (annotatedNode.GetAnnotations(DoNotAddImportsAnnotation.Kind).Any()) { continue; } var annotations = annotatedNode.GetAnnotations(SymbolAnnotation.Kind); foreach (var annotation in annotations) { cancellationToken.ThrowIfCancellationRequested(); foreach (var namedType in SymbolAnnotation.GetSymbols(annotation, model.Compilation).OfType <INamedTypeSymbol>()) { cancellationToken.ThrowIfCancellationRequested(); if (namedType.OriginalDefinition.IsSpecialType() || namedType.IsNullable() || namedType.IsTupleType) { continue; } var namespaceSymbol = namedType.ContainingNamespace; if (namespaceSymbol is null || namespaceSymbol.IsGlobalNamespace) { continue; } first ??= annotatedNode; last = annotatedNode; if (importToSyntax.ContainsKey(namespaceSymbol)) { continue; } var namespaceSyntax = GenerateNamespaceImportDeclaration(namespaceSymbol, generator); if (addImportsService.HasExistingImport(model.Compilation, root, annotatedNode, namespaceSyntax, generator)) { continue; } if (IsInsideNamespace(annotatedNode, namespaceSymbol, model, cancellationToken)) { continue; } importToSyntax[namespaceSymbol] = namespaceSyntax; } } } if (first == null || last == null || importToSyntax.Count == 0) { return(document); } var context = first.GetCommonRoot(last); // Find the namespace/compilation-unit we'll be adding all these imports to. var importContainer = addImportsService.GetImportContainer(root, context, importToSyntax.First().Value, preferences); // Now remove any imports we think can cause conflicts in that container. var safeImportsToAdd = GetSafeToAddImports(importToSyntax.Keys.ToImmutableArray(), importContainer, model, cancellationToken); var importsToAdd = importToSyntax.Where(kvp => safeImportsToAdd.Contains(kvp.Key)).Select(kvp => kvp.Value).ToImmutableArray(); if (importsToAdd.Length == 0) { return(document); } root = addImportsService.AddImports( model.Compilation, root, context, importsToAdd, generator, preferences, allowInHiddenRegions, cancellationToken); return(document.WithSyntaxRoot(root)); }
// returns the ancestor VariableDeclarator node for an ObjectCreationExpression if // IsObjectCreationExpressionUnderFieldDeclaration(node) returns true, return null otherwise. public abstract SyntaxNode?GetVariableDeclaratorOfAFieldDeclarationNode(SyntaxNode?objectCreationExpression);
/// <summary> /// Attempts to return an speculative semantic model for <paramref name="document"/> if possible if <paramref /// name="node"/> is contained within a method body in the tree. Specifically, this will attempt to get an /// existing cached semantic model <paramref name="document"/>. If it can find one, and the top-level semantic /// version for this project matches the cached version, and the position is within a method body, then it will /// be returned, just with the previous corresponding method body swapped out with the current method body. /// <para/> /// If this is not possible, the regular semantic model for <paramref name="document"/> will be returned. /// <para/> /// When using this API, semantic model should only be used to ask questions about nodes inside of the /// member that contains the given <paramref name="node"/>. /// <para/> /// As a speculative semantic model may be returned, location based information provided by it may be innacurate. /// </summary> public static ValueTask <SemanticModel> ReuseExistingSpeculativeModelAsync(this Document document, SyntaxNode?node, CancellationToken cancellationToken) { if (node == null) { return(document.GetRequiredSemanticModelAsync(cancellationToken)); } var workspace = document.Project.Solution.Workspace; var semanticModelService = workspace.Services.GetRequiredService <ISemanticModelReuseWorkspaceService>(); return(semanticModelService.ReuseExistingSpeculativeModelAsync(document, node, cancellationToken)); }
private async Task EnqueueWorkItemAsync(Document document, InvocationReasons invocationReasons, SyntaxNode?changedMember = null) { // we are shutting down _shutdownToken.ThrowIfCancellationRequested(); var priorityService = document.GetLanguageService <IWorkCoordinatorPriorityService>(); var isLowPriority = priorityService != null && await priorityService.IsLowPriorityAsync(document, _shutdownToken).ConfigureAwait(false); var currentMember = GetSyntaxPath(changedMember); // call to this method is serialized. and only this method does the writing. _documentAndProjectWorkerProcessor.Enqueue( new WorkItem(document.Id, document.Project.Language, invocationReasons, isLowPriority, currentMember, _listener.BeginAsyncOperation("WorkItem"))); // enqueue semantic work planner if (invocationReasons.Contains(PredefinedInvocationReasons.SemanticChanged)) { // must use "Document" here so that the snapshot doesn't go away. we need the snapshot to calculate p2p dependency graph later. // due to this, we might hold onto solution (and things kept alive by it) little bit longer than usual. _semanticChangeProcessor.Enqueue(document, currentMember); } }