Beispiel #1
0
        /// <summary>
        /// Infer return type. If `nullableState` is non-null, nullability is also inferred and `NullableWalker.Analyze`
        /// uses that state to set the inferred nullability of variables in the enclosing scope. `conversions` is
        /// only needed when nullability is inferred.
        /// </summary>
        public TypeWithAnnotations GetInferredReturnType(ConversionsBase conversions, NullableWalker.VariableState nullableState, ref HashSet <DiagnosticInfo> useSiteDiagnostics)
        {
            if (!InferredReturnType.UseSiteDiagnostics.IsEmpty)
            {
                if (useSiteDiagnostics == null)
                {
                    useSiteDiagnostics = new HashSet <DiagnosticInfo>();
                }
                foreach (var info in InferredReturnType.UseSiteDiagnostics)
                {
                    useSiteDiagnostics.Add(info);
                }
            }
            if (nullableState == null)
            {
                return(InferredReturnType.TypeWithAnnotations);
            }
            else
            {
                Debug.Assert(conversions != null);
                // Diagnostics from NullableWalker.Analyze can be dropped here since Analyze
                // will be called again from NullableWalker.ApplyConversion when the
                // BoundLambda is converted to an anonymous function.
                // https://github.com/dotnet/roslyn/issues/31752: Can we avoid generating extra
                // diagnostics? And is this exponential when there are nested lambdas?
                var returnTypes = ArrayBuilder <(BoundReturnStatement, TypeWithAnnotations)> .GetInstance();

                var diagnostics  = DiagnosticBag.GetInstance();
                var delegateType = Type.GetDelegateType();
                var compilation  = Binder.Compilation;
                NullableWalker.Analyze(compilation,
                                       lambda: this,
                                       (Conversions)conversions,
                                       diagnostics,
                                       delegateInvokeMethod: delegateType?.DelegateInvokeMethod,
                                       initialState: nullableState,
                                       analyzedNullabilityMapOpt: null,
                                       updatedMethodSymbolMapOpt: null,
                                       snapshotBuilderOpt: null,
                                       returnTypes);
                diagnostics.Free();
                var inferredReturnType = InferReturnType(returnTypes, node: this, compilation, conversions, delegateType, Symbol.IsAsync);
                returnTypes.Free();
                return(inferredReturnType.TypeWithAnnotations);
            }
        }
Beispiel #2
0
        public TypeSymbolWithAnnotations GetInferredReturnType(ref HashSet <DiagnosticInfo> useSiteDiagnostics, NullableWalker.VariableState nullableState = null)
        {
            if (!InferredReturnType.UseSiteDiagnostics.IsEmpty)
            {
                if (useSiteDiagnostics == null)
                {
                    useSiteDiagnostics = new HashSet <DiagnosticInfo>();
                }
                foreach (var info in InferredReturnType.UseSiteDiagnostics)
                {
                    useSiteDiagnostics.Add(info);
                }
            }
            if (nullableState == null)
            {
                return(InferredReturnType.Type);
            }
            else
            {
                var returnTypes = ArrayBuilder <(RefKind, TypeSymbolWithAnnotations)> .GetInstance();

                // Diagnostics from NullableWalker.Analyze can be dropped here since Analyze
                // will be called again from NullableWalker.ApplyConversion when the
                // BoundLambda is converted to an anonymous function.
                // https://github.com/dotnet/roslyn/issues/29617 Can we avoid generating extra
                // diagnostics? And is this exponential when there are nested lambdas?
                var diagnostics  = DiagnosticBag.GetInstance();
                var delegateType = Type.GetDelegateType();
                var compilation  = Binder.Compilation;
                var conversions  = (Conversions)Binder.Conversions.WithNullability(includeNullability: true);
                NullableWalker.Analyze(compilation, lambda: this, diagnostics, delegateInvokeMethod: delegateType?.DelegateInvokeMethod, returnTypes: returnTypes, initialState: nullableState);
                diagnostics.Free();
                var inferredReturnType = InferReturnType(returnTypes, compilation, conversions, delegateType, Symbol.IsAsync);
                returnTypes.Free();
                return(inferredReturnType.Type);
            }
        }
Beispiel #3
0
        /// <summary>
        /// Verify the type and nullability inferred by NullabilityWalker of all expressions in the source
        /// that are followed by specific annotations. Annotations are of the form /*T:type*/.
        /// </summary>
        internal static void VerifyTypes(this CSharpCompilation compilation, SyntaxTree tree = null)
        {
            if (tree == null)
            {
                foreach (var syntaxTree in compilation.SyntaxTrees)
                {
                    VerifyTypes(compilation, syntaxTree);
                }
                return;
            }

            var root           = tree.GetRoot();
            var allAnnotations = getAnnotations();

            if (allAnnotations.IsEmpty)
            {
                return;
            }

            var model = compilation.GetSemanticModel(tree);
            var annotationsByMethod = allAnnotations.GroupBy(annotation => annotation.Expression.Ancestors().OfType <BaseMethodDeclarationSyntax>().First()).ToArray();

            foreach (var annotations in annotationsByMethod)
            {
                var method      = (MethodSymbol)model.GetDeclaredSymbol(annotations.Key);
                var diagnostics = DiagnosticBag.GetInstance();
                var block       = MethodCompiler.BindMethodBody(method, new TypeCompilationState(method.ContainingType, compilation, null), diagnostics);
                var dictionary  = new Dictionary <SyntaxNode, TypeSymbolWithAnnotations>();
                NullableWalker.Analyze(
                    compilation,
                    method,
                    block,
                    diagnostics,
                    callbackOpt: (BoundExpression expr, TypeSymbolWithAnnotations exprType) => dictionary[expr.Syntax] = exprType);
                diagnostics.Free();
                var expectedTypes = annotations.SelectAsArray(annotation => annotation.Text);
                var actualTypes   = annotations.SelectAsArray(annotation => toDisplayString(annotation.Expression));
                // Consider reporting the correct source with annotations on mismatch.
                AssertEx.Equal(expectedTypes, actualTypes, message: method.ToTestDisplayString());

                foreach (var entry in dictionary.Values.Where(v => !v.IsNull))
                {
                    // Result types cannot have nested types that are unspeakables
                    Assert.Null(entry.VisitType(typeOpt: null,
                                                typeWithAnnotationsPredicateOpt: (tswa, a, b) => !tswa.Equals(entry, TypeCompareKind.ConsiderEverything) && !tswa.NullableAnnotation.IsSpeakable(),
                                                typePredicateOpt: (ts, _, b) => false, arg: (object)null, canDigThroughNullable: true));
                }

                string toDisplayString(SyntaxNode syntaxOpt)
                {
                    return((syntaxOpt != null) && dictionary.TryGetValue(syntaxOpt, out var type) ?
                           (type.IsNull ? "<null>" : type.ToDisplayString(TypeSymbolWithAnnotations.TestDisplayFormat)) :
                           null);
                }
            }

            ImmutableArray <(ExpressionSyntax Expression, string Text)> getAnnotations()
            {
                var builder = ArrayBuilder <(ExpressionSyntax, string)> .GetInstance();

                foreach (var token in root.DescendantTokens())
                {
                    foreach (var trivia in token.TrailingTrivia)
                    {
                        if (trivia.Kind() == SyntaxKind.MultiLineCommentTrivia)
                        {
                            var          text   = trivia.ToFullString();
                            const string prefix = "/*T:";
                            const string suffix = "*/";
                            if (text.StartsWith(prefix) && text.EndsWith(suffix))
                            {
                                var expr = getEnclosingExpression(token);
                                Assert.True(expr != null, $"VerifyTypes could not find a matching expression for annotation '{text}'.");

                                var content = text.Substring(prefix.Length, text.Length - prefix.Length - suffix.Length);
                                builder.Add((expr, content));
                            }
                        }
                    }
                }
                return(builder.ToImmutableAndFree());
            }

            ExpressionSyntax getEnclosingExpression(SyntaxToken token)
            {
                var node = token.Parent;

                while (true)
                {
                    var expr = asExpression(node);
                    if (expr != null)
                    {
                        return(expr);
                    }
                    if (node == root)
                    {
                        break;
                    }
                    node = node.Parent;
                }
                return(null);
            }

            ExpressionSyntax asExpression(SyntaxNode node)
            {
                var expr = node as ExpressionSyntax;

                if (expr == null)
                {
                    return(null);
                }
                switch (expr.Kind())
                {
                case SyntaxKind.ParenthesizedExpression:
                    return(((ParenthesizedExpressionSyntax)expr).Expression);

                case SyntaxKind.IdentifierName:
                    if (expr.Parent is MemberAccessExpressionSyntax memberAccess && memberAccess.Name == expr)
                    {
                        return(memberAccess);
                    }
                    break;
                }
                return(expr);
            }
        }