예제 #1
0
 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);
            }
예제 #3
0
 public override SyntaxNode?Visit(SyntaxNode?node)
 => base.Visit(node);
예제 #4
0
 public static TNode?GetAncestorOrThis <TNode>(this SyntaxNode?node)
     where TNode : SyntaxNode
 {
     return(node?.GetAncestorsOrThis <TNode>().FirstOrDefault());
 }
예제 #5
0
        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));
        }
예제 #6
0
 public static (SyntaxToken openBrace, SyntaxToken closeBrace) GetBracePair(this SyntaxNode?node)
 => node.GetBraces();
예제 #7
0
 /// <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);
 }
예제 #8
0
 public abstract SyntaxNode?GetCallTargetNode(SyntaxNode?node);
예제 #9
0
 public abstract SyntaxNode?GetInvocationExpressionNode(SyntaxNode?node);
예제 #10
0
 public abstract SyntaxNode?GetMemberAccessExpressionNode(SyntaxNode?node);
예제 #11
0
 public abstract SyntaxNode?GetMemberAccessNameNode(SyntaxNode?node);
예제 #12
0
 public abstract SyntaxNode?GetAssignmentRightNode(SyntaxNode?node);
예제 #13
0
 public abstract ITypeSymbol?GetClassDeclarationTypeSymbol(SyntaxNode?node, SemanticModel semanticModel);
예제 #14
0
 public abstract ITypeSymbol?GetEnclosingTypeSymbol(SyntaxNode?node, SemanticModel semanticModel);
예제 #15
0
 public abstract IMethodSymbol?GetCalleeMethodSymbol(SyntaxNode?node, SemanticModel semanticModel);
예제 #16
0
 public abstract SyntaxNode?GetDefaultValueForAnOptionalParameter(SyntaxNode?declNode, int paramIndex);
예제 #17
0
 internal static SyntaxNode?AsRootOfNewTreeWithOptionsFrom(this SyntaxNode?node, SyntaxTree oldTree)
 {
     return(node != null?oldTree.WithRootAndOptions(node, oldTree.Options).GetRoot() : null);
 }
예제 #18
0
 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))));
            }
        }
예제 #20
0
 protected abstract IEnumerable <SyntaxNode> GetCallArgumentExpressionNodes(SyntaxNode?node, CallKinds callKind);
 internal WithTwoChildren(InternalSyntax.SyntaxList green, SyntaxNode?parent, int position)
     : base(green, parent, position)
 {
 }
예제 #22
0
 public abstract IEnumerable <SyntaxNode> GetDescendantAssignmentExpressionNodes(SyntaxNode?node);
예제 #23
0
        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));
        }
예제 #24
0
 public abstract IEnumerable <SyntaxNode> GetDescendantMemberAccessExpressionNodes(SyntaxNode?node);
예제 #25
0
        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));
        }
예제 #26
0
 // returns true if node is an ObjectCreationExpression and is under a FieldDeclaration node
 public abstract bool IsObjectCreationExpressionUnderFieldDeclaration(SyntaxNode?node);
예제 #27
0
        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));
        }
예제 #28
0
 // returns the ancestor VariableDeclarator node for an ObjectCreationExpression if
 // IsObjectCreationExpressionUnderFieldDeclaration(node) returns true, return null otherwise.
 public abstract SyntaxNode?GetVariableDeclaratorOfAFieldDeclarationNode(SyntaxNode?objectCreationExpression);
예제 #29
0
        /// <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));
        }
예제 #30
0
            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);
                }
            }