Beispiel #1
0
        private static void VerifyDataFlow(SemanticModel model, DeclarationExpressionSyntax decl, bool isDelegateCreation, bool isExecutableCode, IdentifierNameSyntax[] references, ISymbol symbol)
        {
            var dataFlowParent = decl.Parent.Parent.Parent as ExpressionSyntax;

            if (dataFlowParent == null)
            {
                Assert.IsAssignableFrom<ConstructorInitializerSyntax>(decl.Parent.Parent.Parent);
                return;
            }

            if (model.IsSpeculativeSemanticModel)
            {
                Assert.Throws<NotSupportedException>(() => model.AnalyzeDataFlow(dataFlowParent));
                return;
            }

            var dataFlow = model.AnalyzeDataFlow(dataFlowParent);

            Assert.Equal(isExecutableCode, dataFlow.Succeeded);

            if (isExecutableCode)
            {
                Assert.True(dataFlow.VariablesDeclared.Contains(symbol, ReferenceEqualityComparer.Instance));

                if (!isDelegateCreation)
                {
                    Assert.True(dataFlow.AlwaysAssigned.Contains(symbol, ReferenceEqualityComparer.Instance));
                    Assert.True(dataFlow.WrittenInside.Contains(symbol, ReferenceEqualityComparer.Instance));

                    var flowsIn = FlowsIn(dataFlowParent, decl, references);
                    Assert.Equal(flowsIn,
                                 dataFlow.DataFlowsIn.Contains(symbol, ReferenceEqualityComparer.Instance));
                    Assert.Equal(flowsIn,
                                 dataFlow.ReadInside.Contains(symbol, ReferenceEqualityComparer.Instance));

                    Assert.Equal(FlowsOut(dataFlowParent, decl, references),
                                 dataFlow.DataFlowsOut.Contains(symbol, ReferenceEqualityComparer.Instance));
                    Assert.Equal(ReadOutside(dataFlowParent, references),
                                 dataFlow.ReadOutside.Contains(symbol, ReferenceEqualityComparer.Instance));

                    Assert.Equal(WrittenOutside(dataFlowParent, references),
                                 dataFlow.WrittenOutside.Contains(symbol, ReferenceEqualityComparer.Instance));
                }
            }
        }
Beispiel #2
0
        protected static void VerifyModelForDeclarationField(
            SemanticModel model,
            SingleVariableDesignationSyntax designation,
            bool duplicate,
            params IdentifierNameSyntax[] references)
        {
            var symbol = model.GetDeclaredSymbol(designation);

            Assert.Equal(designation.Identifier.ValueText, symbol.Name);
            Assert.Equal(SymbolKind.Field, symbol.Kind);
            Assert.Equal(designation, symbol.DeclaringSyntaxReferences.Single().GetSyntax());
            Assert.Same(symbol, model.GetDeclaredSymbol((SyntaxNode)designation));

            var symbols = model.LookupSymbols(designation.SpanStart, name: designation.Identifier.ValueText);
            var names   = model.LookupNames(designation.SpanStart);

            if (duplicate)
            {
                Assert.True(symbols.Count() > 1);
                Assert.Contains(symbol, symbols);
            }
            else
            {
                Assert.Same(symbol, symbols.Single());
            }

            Assert.Contains(designation.Identifier.ValueText, names);

            var local = (IFieldSymbol)symbol;

            switch (designation.Parent)
            {
            case DeclarationPatternSyntax decl:
                var typeSyntax = decl.Type;

                Assert.True(SyntaxFacts.IsInNamespaceOrTypeContext(typeSyntax));
                Assert.True(SyntaxFacts.IsInTypeOnlyContext(typeSyntax));

                var type = local.Type;
                if (typeSyntax.IsVar && type.IsErrorType())
                {
                    Assert.Null(model.GetSymbolInfo(typeSyntax).Symbol);
                }
                else
                {
                    Assert.Equal(type, model.GetSymbolInfo(typeSyntax).Symbol);
                }

                AssertTypeInfo(model, decl.Type, type);
                break;

            case var parent:
                Assert.True(parent is VarPatternSyntax);
                break;
            }

            var declarator = designation.Ancestors().OfType <VariableDeclaratorSyntax>().FirstOrDefault();
            var inFieldDeclaratorArgumentlist = declarator != null && declarator.Parent.Parent.Kind() != SyntaxKind.LocalDeclarationStatement &&
                                                (declarator.ArgumentList?.Contains(designation)).GetValueOrDefault();

            // this is a declaration site, not a use site.
            Assert.Null(model.GetSymbolInfo(designation).Symbol);
            Assert.Null(model.GetSymbolInfo(designation).Symbol);

            foreach (var reference in references)
            {
                var referenceInfo = model.GetSymbolInfo(reference);
                symbols = model.LookupSymbols(reference.SpanStart, name: designation.Identifier.ValueText);

                if (duplicate)
                {
                    Assert.Null(referenceInfo.Symbol);
                    Assert.Contains(symbol, referenceInfo.CandidateSymbols);
                    Assert.True(symbols.Count() > 1);
                    Assert.Contains(symbol, symbols);
                }
                else
                {
                    Assert.Same(symbol, referenceInfo.Symbol);
                    Assert.Same(symbol, symbols.Single());
                    Assert.Equal(local.Type, model.GetTypeInfo(reference).Type);
                }

                Assert.True(model.LookupNames(reference.SpanStart).Contains(designation.Identifier.ValueText));
            }

            if (!inFieldDeclaratorArgumentlist)
            {
                var dataFlowParent = designation.FirstAncestorOrSelf <ExpressionSyntax>();

                if (model.IsSpeculativeSemanticModel)
                {
                    Assert.Throws <NotSupportedException>(() => model.AnalyzeDataFlow(dataFlowParent));
                }
                else
                {
                    var dataFlow = model.AnalyzeDataFlow(dataFlowParent);

                    if (dataFlow.Succeeded)
                    {
                        Assert.False(dataFlow.VariablesDeclared.Contains(symbol, ReferenceEqualityComparer.Instance));
                        Assert.False(dataFlow.AlwaysAssigned.Contains(symbol, ReferenceEqualityComparer.Instance));
                        Assert.False(dataFlow.WrittenInside.Contains(symbol, ReferenceEqualityComparer.Instance));
                        Assert.False(dataFlow.DataFlowsIn.Contains(symbol, ReferenceEqualityComparer.Instance));
                        Assert.False(dataFlow.ReadInside.Contains(symbol, ReferenceEqualityComparer.Instance));
                        Assert.False(dataFlow.DataFlowsOut.Contains(symbol, ReferenceEqualityComparer.Instance));
                        Assert.False(dataFlow.ReadOutside.Contains(symbol, ReferenceEqualityComparer.Instance));
                        Assert.False(dataFlow.WrittenOutside.Contains(symbol, ReferenceEqualityComparer.Instance));
                    }
                }
            }
        }
Beispiel #3
0
        private static void VerifyModelForOutField(
            SemanticModel model,
            DeclarationExpressionSyntax decl,
            bool duplicate,
            params IdentifierNameSyntax[] references)
        {
            var variableDesignationSyntax = GetVariableDesignation(decl);
            var symbol = model.GetDeclaredSymbol(variableDesignationSyntax);
            Assert.Equal(decl.Identifier().ValueText, symbol.Name);
            Assert.Equal(SymbolKind.Field, symbol.Kind);
            Assert.Equal(variableDesignationSyntax, symbol.DeclaringSyntaxReferences.Single().GetSyntax());
            Assert.Same(symbol, model.GetDeclaredSymbol((SyntaxNode)variableDesignationSyntax));

            var symbols = model.LookupSymbols(decl.SpanStart, name: decl.Identifier().ValueText);
            var names = model.LookupNames(decl.SpanStart);

            if (duplicate)
            {
                Assert.True(symbols.Count() > 1);
                Assert.Contains(symbol, symbols);
            }
            else
            {
                Assert.Same(symbol, symbols.Single());
            }

            Assert.Contains(decl.Identifier().ValueText, names);

            var local = (FieldSymbol)symbol;
            var typeSyntax = decl.Type();

            Assert.True(SyntaxFacts.IsInNamespaceOrTypeContext(typeSyntax));
            Assert.True(SyntaxFacts.IsInTypeOnlyContext(typeSyntax));

            if (typeSyntax.IsVar && local.Type.IsErrorType())
            {
                Assert.Null(model.GetSymbolInfo(typeSyntax).Symbol);
            }
            else
            {
                Assert.Equal(local.Type, model.GetSymbolInfo(typeSyntax).Symbol);
            }

            var declarator = decl.Ancestors().OfType<VariableDeclaratorSyntax>().FirstOrDefault();
            var inFieldDeclaratorArgumentlist = declarator != null && declarator.Parent.Parent.Kind() != SyntaxKind.LocalDeclarationStatement &&
                                           (declarator.ArgumentList?.Contains(decl)).GetValueOrDefault();
            if (inFieldDeclaratorArgumentlist)
            {
                Assert.Null(model.GetSymbolInfo(decl).Symbol);
                Assert.Null(model.GetSymbolInfo(decl).Symbol);
            }
            else
            {
                Assert.Same(symbol, model.GetSymbolInfo(decl).Symbol);
                Assert.Same(symbol, model.GetSymbolInfo(decl).Symbol);
            }

            Assert.Null(model.GetDeclaredSymbol(decl));

            foreach (var reference in references)
            {
                var referenceInfo = model.GetSymbolInfo(reference);
                symbols = model.LookupSymbols(reference.SpanStart, name: decl.Identifier().ValueText);

                if (duplicate)
                {
                    Assert.Null(referenceInfo.Symbol);
                    Assert.Contains(symbol, referenceInfo.CandidateSymbols);
                    Assert.True(symbols.Count() > 1);
                    Assert.Contains(symbol, symbols);
                }
                else
                {
                    Assert.Same(symbol, referenceInfo.Symbol);
                    Assert.Same(symbol, symbols.Single());
                    Assert.Equal(local.Type, model.GetTypeInfo(reference).Type);
                }

                Assert.True(model.LookupNames(reference.SpanStart).Contains(decl.Identifier().ValueText));
            }

            if (!inFieldDeclaratorArgumentlist)
            {
                var dataFlowParent = (ExpressionSyntax)decl.Parent.Parent.Parent;

                if (model.IsSpeculativeSemanticModel)
                {
                    Assert.Throws<NotSupportedException>(() => model.AnalyzeDataFlow(dataFlowParent));
                }
                else
                {
                    var dataFlow = model.AnalyzeDataFlow(dataFlowParent);

                    if (dataFlow.Succeeded)
                    {
                        Assert.False(dataFlow.VariablesDeclared.Contains(symbol, ReferenceEqualityComparer.Instance));
                        Assert.False(dataFlow.AlwaysAssigned.Contains(symbol, ReferenceEqualityComparer.Instance));
                        Assert.False(dataFlow.WrittenInside.Contains(symbol, ReferenceEqualityComparer.Instance));
                        Assert.False(dataFlow.DataFlowsIn.Contains(symbol, ReferenceEqualityComparer.Instance));
                        Assert.False(dataFlow.ReadInside.Contains(symbol, ReferenceEqualityComparer.Instance));
                        Assert.False(dataFlow.DataFlowsOut.Contains(symbol, ReferenceEqualityComparer.Instance));
                        Assert.False(dataFlow.ReadOutside.Contains(symbol, ReferenceEqualityComparer.Instance));
                        Assert.False(dataFlow.WrittenOutside.Contains(symbol, ReferenceEqualityComparer.Instance));
                    }
                }
            }
        }
Beispiel #4
0
 public static DataFlowAnalysis AnalyzeDataFlow(this SemanticModel semanticModel, MethodDeclarationSyntax methodDeclarationSyntax)
 {
     return(semanticModel.AnalyzeDataFlow(methodDeclarationSyntax.Body));
 }
Beispiel #5
0
        private ExpressionSyntax TryVisitInvocationExpression(InvocationExpressionSyntax node, ForEachStatementSyntax containingForEach)
        {
            var memberAccess = node.Expression as MemberAccessExpressionSyntax;

            if (memberAccess != null)
            {
                var symbol = semantic.GetSymbolInfo(memberAccess).Symbol as IMethodSymbol;
                var owner  = node.AncestorsAndSelf().FirstOrDefault(x => x is MethodDeclarationSyntax);
                if (owner == null)
                {
                    return(null);
                }
                currentMethodIsStatic          = semantic.GetDeclaredSymbol((MethodDeclarationSyntax)owner).IsStatic;
                currentMethodName              = ((MethodDeclarationSyntax)owner).Identifier.ValueText;
                currentMethodTypeParameters    = ((MethodDeclarationSyntax)owner).TypeParameterList;
                currentMethodConstraintClauses = ((MethodDeclarationSyntax)owner).ConstraintClauses;


                if (IsSupportedMethod(GetMethodFullName(node)))
                {
                    var chain = new List <LinqStep>();
                    chain.Add(new LinqStep(GetMethodFullName(node), node.ArgumentList.Arguments.Select(x => x.Expression).ToList(), node));
                    var c        = node;
                    var lastNode = node;
                    while (c.Expression is MemberAccessExpressionSyntax)
                    {
                        c = ((MemberAccessExpressionSyntax)c.Expression).Expression as InvocationExpressionSyntax;
                        if (c != null && IsSupportedMethod(GetMethodFullName(c)))
                        {
                            chain.Add(new LinqStep(GetMethodFullName(c), c.ArgumentList.Arguments.Select(x => x.Expression).ToList(), c));
                            lastNode = c;
                        }
                        else
                        {
                            break;
                        }
                    }
                    if (containingForEach != null)
                    {
                        chain.Insert(0, new LinqStep(IEnumerableForEachMethod, new[] { SyntaxFactory.SimpleLambdaExpression(SyntaxFactory.Parameter(containingForEach.Identifier), containingForEach.Statement) })
                        {
                            Lambda = new Lambda(containingForEach.Statement, new[] { CreateParameter(containingForEach.Identifier, semantic.GetTypeInfo(containingForEach.Type).ConvertedType) })
                        });
                    }
                    if (!chain.Any(x => x.Arguments.Any(y => y is AnonymousFunctionExpressionSyntax)))
                    {
                        return(null);
                    }
                    if (chain.Count == 1 && RootMethodsThatRequireYieldReturn.Contains(chain[0].MethodName))
                    {
                        return(null);
                    }

                    if (currentMethodName == "SetBlogConfigAsync")
                    {
                        Debugger.Break();
                    }

                    var flowsIn  = new List <ISymbol>();
                    var flowsOut = new List <ISymbol>();
                    foreach (var item in chain)
                    {
                        foreach (var arg in item.Arguments)
                        {
                            if (item.Lambda != null)
                            {
                                var dataFlow = semantic.AnalyzeDataFlow(item.Lambda.Body);
                                var pname    = item.Lambda.Parameters.Single().Identifier.ValueText;
                                foreach (var k in dataFlow.DataFlowsIn)
                                {
                                    if (k.Name == pname)
                                    {
                                        continue;
                                    }
                                    if (!flowsIn.Contains(k))
                                    {
                                        flowsIn.Add(k);
                                    }
                                }
                                foreach (var k in dataFlow.DataFlowsOut)
                                {
                                    if (k.Name == pname)
                                    {
                                        continue;
                                    }
                                    if (!flowsOut.Contains(k))
                                    {
                                        flowsOut.Add(k);
                                    }
                                }
                            }
                            else
                            {
                                var dataFlow = semantic.AnalyzeDataFlow(arg);
                                foreach (var k in dataFlow.DataFlowsIn)
                                {
                                    if (!flowsIn.Contains(k))
                                    {
                                        flowsIn.Add(k);
                                    }
                                }
                                foreach (var k in dataFlow.DataFlowsOut)
                                {
                                    if (!flowsOut.Contains(k))
                                    {
                                        flowsOut.Add(k);
                                    }
                                }
                            }
                        }
                    }

                    currentFlow = flowsIn
                                  .Union(flowsOut)
                                  .Where(x => (x as IParameterSymbol)?.IsThis != true)
                                  .Select(x => new VariableCapture(x, flowsOut.Contains(x))) ?? Enumerable.Empty <VariableCapture>();



                    var collection = ((MemberAccessExpressionSyntax)lastNode.Expression).Expression;

                    if (IsAnonymousType(semantic.GetTypeInfo(collection).Type))
                    {
                        return(null);
                    }


                    var semanticReturnType = semantic.GetTypeInfo(node).Type;
                    if (IsAnonymousType(semanticReturnType) || currentFlow.Any(x => IsAnonymousType(GetSymbolType(x.Symbol))))
                    {
                        return(null);
                    }



                    return(TryRewrite(chain.First().MethodName, collection, semanticReturnType, chain, node)
                           .WithLeadingTrivia(((CSharpSyntaxNode)containingForEach ?? node).GetLeadingTrivia())
                           .WithTrailingTrivia(((CSharpSyntaxNode)containingForEach ?? node).GetTrailingTrivia()));
                }
            }
            return(null);
        }
Beispiel #6
0
        private static void Analyze(
            SyntaxNodeAnalysisContext context,
            SyntaxNode declaration,
            ParameterListSyntax parameterList,
            CSharpSyntaxNode bodyOrExpressionBody)
        {
            if (parameterList == null)
            {
                return;
            }

            if (bodyOrExpressionBody == null)
            {
                return;
            }

            if (!parameterList.Parameters.Any())
            {
                return;
            }

            SemanticModel     semanticModel     = context.SemanticModel;
            CancellationToken cancellationToken = context.CancellationToken;

            var methodSymbol = (IMethodSymbol)semanticModel.GetDeclaredSymbol(declaration, cancellationToken);

            SyntaxWalker walker = null;

            foreach (IParameterSymbol parameter in methodSymbol.Parameters)
            {
                cancellationToken.ThrowIfCancellationRequested();

                ITypeSymbol type = parameter.Type;

                if (type.Kind == SymbolKind.ErrorType)
                {
                    continue;
                }

                if (CSharpFacts.IsSimpleType(type.SpecialType))
                {
                    continue;
                }

                if (!type.IsReadOnlyStruct())
                {
                    if (parameter.RefKind == RefKind.In &&
                        type.TypeKind == TypeKind.Struct)
                    {
                        var parameterSyntax = (ParameterSyntax)parameter.GetSyntax(cancellationToken);

                        Debug.Assert(parameterSyntax.Modifiers.Contains(SyntaxKind.InKeyword), "");

                        DiagnosticHelpers.ReportDiagnostic(context, DiagnosticRules.DoNotPassNonReadOnlyStructByReadOnlyReference, parameterSyntax.Identifier);
                    }

                    continue;
                }

                if (parameter.RefKind != RefKind.None)
                {
                    continue;
                }

                if (walker == null)
                {
                    if (methodSymbol.ImplementsInterfaceMember(allInterfaces: true))
                    {
                        break;
                    }

                    walker = SyntaxWalker.GetInstance();
                }
                else if (walker.Parameters.ContainsKey(parameter.Name))
                {
                    walker.Parameters.Clear();
                    break;
                }

                walker.Parameters.Add(parameter.Name, parameter);
            }

            if (walker == null)
            {
                return;
            }

            try
            {
                if (walker.Parameters.Count > 0)
                {
                    walker.SemanticModel     = semanticModel;
                    walker.CancellationToken = cancellationToken;

                    if (bodyOrExpressionBody.IsKind(SyntaxKind.Block))
                    {
                        walker.VisitBlock((BlockSyntax)bodyOrExpressionBody);
                    }
                    else
                    {
                        walker.VisitArrowExpressionClause((ArrowExpressionClauseSyntax)bodyOrExpressionBody);
                    }

                    if (walker.Parameters.Count > 0)
                    {
                        DataFlowAnalysis analysis = (bodyOrExpressionBody.IsKind(SyntaxKind.Block))
                            ? semanticModel.AnalyzeDataFlow((BlockSyntax)bodyOrExpressionBody)
                            : semanticModel.AnalyzeDataFlow(((ArrowExpressionClauseSyntax)bodyOrExpressionBody).Expression);

                        bool?isReferencedAsMethodGroup = null;

                        foreach (KeyValuePair <string, IParameterSymbol> kvp in walker.Parameters)
                        {
                            var isAssigned = false;

                            foreach (ISymbol assignedSymbol in analysis.AlwaysAssigned)
                            {
                                if (SymbolEqualityComparer.Default.Equals(assignedSymbol, kvp.Value))
                                {
                                    isAssigned = true;
                                    break;
                                }
                            }

                            if (isAssigned)
                            {
                                continue;
                            }

                            if (isReferencedAsMethodGroup ??= IsReferencedAsMethodGroup())
                            {
                                break;
                            }

                            if (kvp.Value.GetSyntaxOrDefault(cancellationToken) is ParameterSyntax parameter)
                            {
                                DiagnosticHelpers.ReportDiagnostic(
                                    context,
                                    DiagnosticRules.MakeParameterRefReadOnly,
                                    parameter.Identifier);
                            }
                        }
                    }
                }
            }
            finally
            {
                SyntaxWalker.Free(walker);
            }

            bool IsReferencedAsMethodGroup()
            {
                switch (declaration.Kind())
                {
                case SyntaxKind.MethodDeclaration:
                    return(MethodReferencedAsMethodGroupWalker.IsReferencedAsMethodGroup(declaration.Parent, methodSymbol, semanticModel, cancellationToken));

                case SyntaxKind.LocalFunctionStatement:
                    return(MethodReferencedAsMethodGroupWalker.IsReferencedAsMethodGroup(declaration.FirstAncestor <MemberDeclarationSyntax>(), methodSymbol, semanticModel, cancellationToken));

                default:
                    return(false);
                }
            }
        }
            // To convert a null-check to pattern-matching, we should make sure of a few things:
            //
            //      (1) The pattern variable may not be used before the point of declaration.
            //
            //          {
            //              var use = t;
            //              if (x is T t) {}
            //          }
            //
            //      (2) The pattern variable may not be used outside of the new scope which
            //          is determined by the parent statement.
            //
            //          {
            //              if (x is T t) {}
            //          }
            //
            //          var use = t;
            //
            //      (3) The pattern variable may not be used before assignment in opposite
            //          branches, if any.
            //
            //          {
            //              if (x is T t) {}
            //              var use = t;
            //          }
            //
            // We walk up the tree from the point of null-check and see if any of the above is violated.
            private bool CanSafelyConvertToPatternMatching()
            {
                // Keep track of whether the pattern variable is definitely assigned when false/true.
                // We start by the null-check itself, if it's compared with '==', the pattern variable
                // will be definitely assigned when false, because we wrap the is-operator in a !-operator.
                var defAssignedWhenTrue = _comparison.Kind() == SyntaxKind.NotEqualsExpression;

                foreach (var current in _comparison.Ancestors())
                {
                    // Checking for any conditional statement or expression that could possibly
                    // affect or determine the state of definite-assignment of the pattern variable.
                    switch (current.Kind())
                    {
                    case SyntaxKind.LogicalAndExpression when !defAssignedWhenTrue:
                    case SyntaxKind.LogicalOrExpression when defAssignedWhenTrue:
                        // Since the pattern variable is only definitely assigned if the pattern
                        // succeeded, in the following cases it would not be safe to use pattern-matching.
                        // For example:
                        //
                        //      if ((x = o as string) == null && SomeExpression)
                        //      if ((x = o as string) != null || SomeExpression)
                        //
                        // Here, x would never be definitely assigned if pattern-matching were used.
                        return(false);

                    case SyntaxKind.LogicalAndExpression:
                    case SyntaxKind.LogicalOrExpression:

                    // Parentheses and cast expressions do not contribute to the flow analysis.
                    case SyntaxKind.ParenthesizedExpression:
                    case SyntaxKind.CastExpression:

                    // Skip over declaration parts to get to the parenting statement
                    // which might be a for-statement or a local declaration statement.
                    case SyntaxKind.EqualsValueClause:
                    case SyntaxKind.VariableDeclarator:
                    case SyntaxKind.VariableDeclaration:
                        continue;

                    case SyntaxKind.LogicalNotExpression:
                        // The !-operator negates the definitive assignment state.
                        defAssignedWhenTrue = !defAssignedWhenTrue;
                        continue;

                    case SyntaxKind.ConditionalExpression:
                        var conditionalExpression = (ConditionalExpressionSyntax)current;
                        if (LocalFlowsIn(defAssignedWhenTrue
                                    ? conditionalExpression.WhenFalse
                                    : conditionalExpression.WhenTrue))
                        {
                            // In a conditional expression, the pattern variable
                            // would not be definitely assigned in the opposite branch.
                            return(false);
                        }

                        return(CheckExpression(conditionalExpression));

                    case SyntaxKind.ForStatement:
                        var forStatement = (ForStatementSyntax)current;
                        if (!forStatement.Condition.Span.Contains(_comparison.Span))
                        {
                            // In a for-statement, only the condition expression
                            // can make this definitely assigned in the loop body.
                            return(false);
                        }

                        return(CheckLoop(forStatement, forStatement.Statement, defAssignedWhenTrue));

                    case SyntaxKind.WhileStatement:
                        var whileStatement = (WhileStatementSyntax)current;
                        return(CheckLoop(whileStatement, whileStatement.Statement, defAssignedWhenTrue));

                    case SyntaxKind.IfStatement:
                        var ifStatement       = (IfStatementSyntax)current;
                        var oppositeStatement = defAssignedWhenTrue
                                ? ifStatement.Else?.Statement
                                : ifStatement.Statement;

                        if (oppositeStatement != null)
                        {
                            var dataFlow = _semanticModel.AnalyzeDataFlow(oppositeStatement);
                            if (dataFlow.DataFlowsIn.Contains(_localSymbol))
                            {
                                // Access before assignment is not safe in the opposite branch
                                // as the variable is not definitely assgined at this point.
                                // For example:
                                //
                                //    if (o is string x) { }
                                //    else { Use(x); }
                                //
                                return(false);
                            }

                            if (dataFlow.AlwaysAssigned.Contains(_localSymbol))
                            {
                                // If the variable is always assigned here, we don't need to check
                                // subsequent statements as it's definitely assigned afterwards.
                                // For example:
                                //
                                //     if (o is string x) { }
                                //     else { x = null; }
                                //
                                return(true);
                            }
                        }

                        if (!defAssignedWhenTrue &&
                            !_semanticModel.AnalyzeControlFlow(ifStatement.Statement).EndPointIsReachable)
                        {
                            // Access before assignment here is only valid if we have a negative
                            // pattern-matching in an if-statement with an unreachable endpoint.
                            // For example:
                            //
                            //      if (!(o is string x)) {
                            //        return;
                            //      }
                            //
                            //      // The 'return' statement above ensures x is definitely assigned here
                            //      Console.WriteLine(x);
                            //
                            return(true);
                        }

                        return(CheckStatement(ifStatement));
                    }

                    switch (current)
                    {
                    case ExpressionSyntax expression:
                        // If we reached here, it means we have a sub-expression that
                        // does not garantee definite assignment. We should make sure that
                        // the pattern variable is not used outside of the expression boundaries.
                        return(CheckExpression(expression));

                    case StatementSyntax statement:
                        // If we reached here, it means that the null-check is appeared in
                        // a statement. In that case, the variable would be actually in the
                        // scope in subsequent statements, but not definitely assigned.
                        // Therefore, we should ensure that there is no use before assignment.
                        return(CheckStatement(statement));
                    }

                    // Bail out for error cases and unhandled cases.
                    break;
                }

                return(false);
            }
Beispiel #8
0
        private static void ExtractVariableDeclarationsBeforeUsing(SemanticModel semanticModel, List <StatementSyntax> beforeUsing, List <StatementSyntax> insideUsing, List <SyntaxNode> nodesToRemove, CancellationToken ct)
        {
            var inUsingDataFlow = semanticModel.AnalyzeDataFlow(insideUsing.First(), insideUsing.Last());

            var declaredVariablesUsedOutside = inUsingDataFlow.ReadOutside.Union(inUsingDataFlow.WrittenOutside)
                                               .Distinct()
                                               .Intersect(inUsingDataFlow.VariablesDeclared)
                                               .Select(x => new { Symbol = x, Declarator = (VariableDeclaratorSyntax)x.DeclaringSyntaxReferences[0].GetSyntax(ct) })
                                               .ToArray();


            for (var i = 0; i < insideUsing.Count; i++)
            {
                var stmt = insideUsing[i];

                var localDeclarationStmt = stmt as LocalDeclarationStatementSyntax;

                if (localDeclarationStmt == null)
                {
                    continue;
                }

                nodesToRemove.Add(localDeclarationStmt);

                var declaredVariables = localDeclarationStmt.Declaration
                                        .Variables.Select(x => new { Symbol = semanticModel.GetDeclaredSymbol(x), Declarator = x })
                                        .ToArray();

                var variablesToMove = declaredVariables
                                      .Intersect(declaredVariablesUsedOutside)
                                      .ToArray();

                if (!variablesToMove.Any())
                {
                    continue;
                }

                var reducedLocalDeclaration = localDeclarationStmt.RemoveNodes(variablesToMove.Select(x => x.Declarator), SyntaxRemoveOptions.AddElasticMarker);

                if (reducedLocalDeclaration.Declaration.Variables.Any())
                {
                    insideUsing[i] = reducedLocalDeclaration;
                }
                else
                {
                    insideUsing.RemoveAt(i);
                    i--;
                }

                foreach (var needAssignment in variablesToMove.Where(x => x.Declarator.Initializer != null))
                {
                    insideUsing.Insert(i + 1, needAssignment.Declarator.InitializerAsAssignment());
                }

                beforeUsing.Add(SyntaxFactory.LocalDeclarationStatement(
                                    SyntaxFactory.VariableDeclaration(
                                        localDeclarationStmt.Declaration.Type,
                                        SyntaxFactory.SeparatedList(variablesToMove.Select(x => x.Declarator.WithInitializer(null)))
                                        )
                                    )
                                .WithAdditionalAnnotations(Formatter.Annotation)
                                );
            }
        }
Beispiel #9
0
        private static bool CanBeMadeConst(LocalDeclarationStatementSyntax localDeclaration, SemanticModel semanticModel)
        {
            // already const?
            if (localDeclaration.Modifiers.Any(SyntaxKind.ConstKeyword))
            {
                return(false);
            }

            // Ensure that all variables in the local declaration have initializers that
            // are assigned with constant values.
            foreach (var variable in localDeclaration.Declaration.Variables)
            {
                var initializer = variable.Initializer;
                if (initializer == null)
                {
                    return(false);
                }

                var constantValue = semanticModel.GetConstantValue(initializer.Value);
                if (!constantValue.HasValue)
                {
                    return(false);
                }

                var variableTypeName = localDeclaration.Declaration.Type;
                var variableType     = semanticModel.GetTypeInfo(variableTypeName).ConvertedType;

                // Ensure that the initializer value can be converted to the type of the
                // local declaration without a user-defined conversion.
                var conversion = semanticModel.ClassifyConversion(initializer.Value, variableType);
                if (!conversion.Exists || conversion.IsUserDefined)
                {
                    return(false);
                }

                // Special cases:
                //   * If the constant value is a string, the type of the local declaration
                //     must be System.String.
                //   * If the constant value is null, the type of the local declaration must
                //     be a reference type.
                if (constantValue.Value is string)
                {
                    if (variableType.SpecialType != SpecialType.System_String)
                    {
                        return(false);
                    }
                }
                else if (variableType.IsReferenceType && constantValue.Value != null)
                {
                    return(false);
                }
            }

            // Perform data flow analysis on the local declaration.
            var dataFlowAnalysis = semanticModel.AnalyzeDataFlow(localDeclaration);

            // Retrieve the local symbol for each variable in the local declaration
            // and ensure that it is not written outside of the data flow analysis region.
            foreach (var variable in localDeclaration.Declaration.Variables)
            {
                var variableSymbol = semanticModel.GetDeclaredSymbol(variable);
                if (dataFlowAnalysis.WrittenOutside.Contains(variableSymbol))
                {
                    return(false);
                }
            }

            return(true);
        }
        static bool ContainsLocalReferences(SemanticModel context, SyntaxNode expr, SyntaxNode body)
        {
            var dataFlowAnalysis = context.AnalyzeDataFlow(expr);

            return(dataFlowAnalysis.Captured.Any());
        }
 private static bool TryGetDataFlowAnalysis(LocalFunctionStatementSyntax localFunction, SemanticModel semanticModel, [NotNullWhen(returnValue: true)] out DataFlowAnalysis?dataFlow)
 {
     dataFlow = semanticModel.AnalyzeDataFlow(localFunction);
     return(dataFlow is { Succeeded : true });
        private Diagnostic AnalyzeAwaitedOrReturnedExpression(ExpressionSyntax expressionSyntax, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            if (expressionSyntax == null)
            {
                return(null);
            }

            if (semanticModel == null)
            {
                throw new ArgumentNullException(nameof(semanticModel));
            }

            SymbolInfo symbolToConsider = semanticModel.GetSymbolInfo(expressionSyntax, cancellationToken);

            if (CommonInterest.TaskConfigureAwait.Any(configureAwait => configureAwait.IsMatch(symbolToConsider.Symbol)))
            {
                if (((InvocationExpressionSyntax)expressionSyntax).Expression is MemberAccessExpressionSyntax memberAccessExpression)
                {
                    symbolToConsider = semanticModel.GetSymbolInfo(memberAccessExpression.Expression, cancellationToken);
                }
            }

            ITypeSymbol symbolType;
            bool        dataflowAnalysisCompatibleVariable = false;

            switch (symbolToConsider.Symbol)
            {
            case ILocalSymbol localSymbol:
                symbolType = localSymbol.Type;
                dataflowAnalysisCompatibleVariable = true;
                break;

            case IParameterSymbol parameterSymbol:
                symbolType = parameterSymbol.Type;
                dataflowAnalysisCompatibleVariable = true;
                break;

            case IFieldSymbol fieldSymbol:
                symbolType = fieldSymbol.Type;
                break;

            case IMethodSymbol methodSymbol:
                if (Utils.IsTask(methodSymbol.ReturnType) && expressionSyntax is InvocationExpressionSyntax invocationExpressionSyntax)
                {
                    // Consider all arguments
                    var expressionsToConsider = invocationExpressionSyntax.ArgumentList.Arguments.Select(a => a.Expression);

                    // Consider the implicit first argument when this method is invoked as an extension method.
                    if (methodSymbol.IsExtensionMethod && invocationExpressionSyntax.Expression is MemberAccessExpressionSyntax invokedMember)
                    {
                        if (!methodSymbol.ContainingType.Equals(semanticModel.GetSymbolInfo(invokedMember.Expression, cancellationToken).Symbol))
                        {
                            expressionsToConsider = new ExpressionSyntax[] { invokedMember.Expression }.Concat(expressionsToConsider);
                        }
                    }

                    return(expressionsToConsider.Select(e => this.AnalyzeAwaitedOrReturnedExpression(e, semanticModel, cancellationToken)).FirstOrDefault(r => r != null));
                }

                return(null);

            default:
                return(null);
            }

            if (symbolType?.Name != nameof(Task) || !symbolType.BelongsToNamespace(Namespaces.SystemThreadingTasks))
            {
                return(null);
            }

            // Report warning if the task was not initialized within the current delegate or lambda expression
            var containingFunc = Utils.GetContainingFunction(expressionSyntax);

            if (containingFunc.BlockOrExpression is BlockSyntax delegateBlock)
            {
                if (dataflowAnalysisCompatibleVariable)
                {
                    // Run data flow analysis to understand where the task was defined
                    DataFlowAnalysis dataFlowAnalysis;

                    // When possible (await is direct child of the block and not a field), execute data flow analysis by passing first and last statement to capture only what happens before the await
                    // Check if the await is direct child of the code block (first parent is ExpressionStantement, second parent is the block itself)
                    if (delegateBlock.Equals(expressionSyntax.Parent.Parent?.Parent))
                    {
                        dataFlowAnalysis = semanticModel.AnalyzeDataFlow(delegateBlock.ChildNodes().First(), expressionSyntax.Parent.Parent);
                    }
                    else
                    {
                        // Otherwise analyze the data flow for the entire block. One caveat: it doesn't distinguish if the initalization happens after the await.
                        dataFlowAnalysis = semanticModel.AnalyzeDataFlow(delegateBlock);
                    }

                    if (!dataFlowAnalysis.WrittenInside.Contains(symbolToConsider.Symbol))
                    {
                        return(Diagnostic.Create(Descriptor, expressionSyntax.GetLocation()));
                    }
                }
                else
                {
                    // Do the best we can searching for assignment statements.
                    if (!Utils.IsAssignedWithin(containingFunc.BlockOrExpression, semanticModel, symbolToConsider.Symbol, cancellationToken))
                    {
                        return(Diagnostic.Create(Descriptor, expressionSyntax.GetLocation()));
                    }
                }
            }
            else
            {
                // It's not a block, it's just a lambda expression, so the variable must be external.
                return(Diagnostic.Create(Descriptor, expressionSyntax.GetLocation()));
            }

            return(null);
        }