// If this is Y in X.Y, walk up to X.Y
            static TypeSyntax WalkUpCurrentQualifiedName(TypeSyntax node)
            {
                while (node.IsParentKind(SyntaxKind.QualifiedName, out QualifiedNameSyntax? qualifiedName) &&
                       qualifiedName.Right == node)
                {
                    node = qualifiedName;
                }

                return(node);
            }
        internal override bool TryAnalyzeVariableDeclaration(
            TypeSyntax typeName, SemanticModel semanticModel,
            OptionSet optionSet, CancellationToken cancellationToken)
        {
            // var (x, y) = e;
            // foreach (var (x, y) in e) ...
            if (typeName.IsParentKind(SyntaxKind.DeclarationExpression))
            {
                var parent = (DeclarationExpressionSyntax)typeName.Parent;
                if (parent.Designation.IsKind(SyntaxKind.ParenthesizedVariableDesignation))
                {
                    return(true);
                }
            }

            // If it is currently not var, explicit typing exists, return.
            // this also takes care of cases where var is mapped to a named type via an alias or a class declaration.
            if (!typeName.StripRefIfNeeded().IsTypeInferred(semanticModel))
            {
                return(false);
            }

            if (typeName.Parent.IsKind(SyntaxKind.VariableDeclaration) &&
                typeName.Parent.Parent.IsKind(SyntaxKind.LocalDeclarationStatement, SyntaxKind.ForStatement, SyntaxKind.UsingStatement))
            {
                // check assignment for variable declarations.
                var variableDeclaration = (VariableDeclarationSyntax)typeName.Parent;
                var variable            = variableDeclaration.Variables.First();
                if (!AssignmentSupportsStylePreference(
                        variable.Identifier, typeName, variable.Initializer.Value,
                        semanticModel, optionSet, cancellationToken))
                {
                    return(false);
                }

                // This error case is handled by a separate code fix (UseExplicitTypeForConst).
                if ((variableDeclaration.Parent as LocalDeclarationStatementSyntax)?.IsConst == true)
                {
                    return(false);
                }
            }
            else if (typeName.Parent is ForEachStatementSyntax foreachStatement &&
                     foreachStatement.Type == typeName)
            {
                if (!AssignmentSupportsStylePreference(
                        foreachStatement.Identifier, typeName, foreachStatement.Expression,
                        semanticModel, optionSet, cancellationToken))
                {
                    return(false);
                }
            }

            return(true);
        }
        private static bool IsLanguageRestrictedToNonNullForm(TypeSyntax node)
        {
            // Simplify syntax checks by walking up qualified names to an equivalent parent node.
            node = WalkUpCurrentQualifiedName(node);

            if (node.IsParentKind(SyntaxKind.QualifiedName, out QualifiedNameSyntax? qualifiedName) &&
                qualifiedName.Left == node)
            {
                // Cannot dot off a nullable reference type
                return(true);
            }

            if (node.IsParentKind(SyntaxKind.UsingDirective))
            {
                // Using directives cannot directly reference a nullable reference type
                return(true);
            }

            if (node.IsParentKind(SyntaxKind.SimpleBaseType))
            {
                // Cannot derive directly from a nullable reference type
                return(true);
            }

            if (node.IsParentKind(SyntaxKind.NamespaceDeclaration, SyntaxKind.FileScopedNamespaceDeclaration))
            {
                // Namespace names cannot be nullable reference types
                return(true);
            }

            if (node.IsParentKind(SyntaxKind.NameEquals) && node.Parent.IsParentKind(SyntaxKind.UsingDirective))
            {
                // This is the alias or the target type of a using alias directive, neither of which can be nullable
                //
                //   using CustomException = System.Exception;
                //         ^^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^
                return(true);
            }

            return(false);
Esempio n. 4
0
        protected override bool TryAnalyzeVariableDeclaration(TypeSyntax typeName, SemanticModel semanticModel, OptionSet optionSet, CancellationToken cancellationToken, out TextSpan issueSpan)
        {
            // If it is already var, return.
            if (typeName.IsTypeInferred(semanticModel))
            {
                issueSpan = default(TextSpan);
                return(false);
            }

            var candidateReplacementNode = SyntaxFactory.IdentifierName("var");
            var candidateIssueSpan       = typeName.Span;

            // If there exists a type named var, return.
            var conflict = semanticModel.GetSpeculativeSymbolInfo(typeName.SpanStart, candidateReplacementNode, SpeculativeBindingOption.BindAsTypeOrNamespace).Symbol;

            if (conflict?.IsKind(SymbolKind.NamedType) == true)
            {
                issueSpan = default(TextSpan);
                return(false);
            }

            if (typeName.Parent.IsKind(SyntaxKind.VariableDeclaration) &&
                typeName.Parent.IsParentKind(SyntaxKind.LocalDeclarationStatement, SyntaxKind.ForStatement, SyntaxKind.UsingStatement))
            {
                var variableDeclaration = (VariableDeclarationSyntax)typeName.Parent;

                // implicitly typed variables cannot be constants.
                if ((variableDeclaration.Parent as LocalDeclarationStatementSyntax)?.IsConst == true)
                {
                    issueSpan = default(TextSpan);
                    return(false);
                }

                var variable = variableDeclaration.Variables.Single();
                if (AssignmentSupportsStylePreference(
                        variable.Identifier, typeName, variable.Initializer.Value,
                        semanticModel, optionSet, cancellationToken))
                {
                    issueSpan = candidateIssueSpan;
                    return(true);
                }
            }
            else if (typeName.IsParentKind(SyntaxKind.ForEachStatement))
            {
                issueSpan = candidateIssueSpan;
                return(true);
            }

            issueSpan = default(TextSpan);
            return(false);
        }
        protected override bool TryAnalyzeVariableDeclaration(TypeSyntax typeName, SemanticModel semanticModel, OptionSet optionSet, CancellationToken cancellationToken, out TextSpan issueSpan)
        {
            issueSpan = default;

            // var (x, y) = e;
            // foreach (var (x, y) in e) ...
            if (typeName.IsParentKind(SyntaxKind.DeclarationExpression))
            {
                var parent = (DeclarationExpressionSyntax)typeName.Parent;
                if (parent.Designation.IsKind(SyntaxKind.ParenthesizedVariableDesignation))
                {
                    issueSpan = typeName.Span;
                    return(true);
                }
            }

            // If it is currently not var, explicit typing exists, return.
            // this also takes care of cases where var is mapped to a named type via an alias or a class declaration.
            if (!typeName.IsTypeInferred(semanticModel))
            {
                return(false);
            }

            if (typeName.Parent.IsKind(SyntaxKind.VariableDeclaration) &&
                typeName.Parent.Parent.IsKind(SyntaxKind.LocalDeclarationStatement, SyntaxKind.ForStatement, SyntaxKind.UsingStatement))
            {
                // check assignment for variable declarations.
                var variable = ((VariableDeclarationSyntax)typeName.Parent).Variables.First();
                if (!AssignmentSupportsStylePreference(
                        variable.Identifier, typeName, variable.Initializer.Value,
                        semanticModel, optionSet, cancellationToken))
                {
                    return(false);
                }
            }
            else if (typeName.Parent.IsKind(SyntaxKind.ForEachStatement))
            {
                var foreachStatement = (ForEachStatementSyntax)typeName.Parent;
                if (!AssignmentSupportsStylePreference(
                        foreachStatement.Identifier, typeName, foreachStatement.Expression,
                        semanticModel, optionSet, cancellationToken))
                {
                    return(false);
                }
            }

            issueSpan = typeName.Span;
            return(true);
        }
Esempio n. 6
0
        public static Accessibility DetermineAccessibilityConstraint(
            this SemanticModel semanticModel,
            TypeSyntax type,
            CancellationToken cancellationToken)
        {
            if (type == null)
            {
                return(Accessibility.Private);
            }

            type = GetOutermostType(type);

            // Interesting cases based on 3.5.4 Accessibility constraints in the language spec.
            // If any of the below hold, then we will override the default accessibility if the
            // constraint wants the type to be more accessible. i.e. if by default we generate
            // 'internal', but a constraint makes us 'public', then be public.

            // 1) The direct base class of a class type must be at least as accessible as the
            //    class type itself.
            //
            // 2) The explicit base interfaces of an interface type must be at least as accessible
            //    as the interface type itself.
            if (type != null)
            {
                if (type.Parent is BaseTypeSyntax && type.Parent.IsParentKind(SyntaxKind.BaseList) && ((BaseTypeSyntax)type.Parent).Type == type)
                {
                    var containingType = semanticModel.GetDeclaredSymbol(type.GetAncestor <BaseTypeDeclarationSyntax>(), cancellationToken) as INamedTypeSymbol;
                    if (containingType != null && containingType.TypeKind == TypeKind.Interface)
                    {
                        return(containingType.DeclaredAccessibility);
                    }
                    else if (((BaseListSyntax)type.Parent.Parent).Types[0] == type.Parent)
                    {
                        return(containingType.DeclaredAccessibility);
                    }
                }
            }

            // 4) The type of a constant must be at least as accessible as the constant itself.
            // 5) The type of a field must be at least as accessible as the field itself.
            if (type.IsParentKind(SyntaxKind.VariableDeclaration) &&
                type.Parent.IsParentKind(SyntaxKind.FieldDeclaration))
            {
                var variableDeclaration = (VariableDeclarationSyntax)type.Parent;
                return(semanticModel.GetDeclaredSymbol(
                           variableDeclaration.Variables[0], cancellationToken).DeclaredAccessibility);
            }

            // Also do the same check if we are in an object creation expression
            if (type.IsParentKind(SyntaxKind.ObjectCreationExpression) &&
                type.Parent.IsParentKind(SyntaxKind.EqualsValueClause) &&
                type.Parent.Parent.IsParentKind(SyntaxKind.VariableDeclarator) &&
                type.Parent.Parent.Parent.IsParentKind(SyntaxKind.VariableDeclaration) &&
                type.Parent.Parent.Parent.Parent.IsParentKind(SyntaxKind.FieldDeclaration))
            {
                var variableDeclaration = (VariableDeclarationSyntax)type.Parent.Parent.Parent.Parent;
                return(semanticModel.GetDeclaredSymbol(
                           variableDeclaration.Variables[0], cancellationToken).DeclaredAccessibility);
            }

            // 3) The return type of a delegate type must be at least as accessible as the
            //    delegate type itself.
            // 6) The return type of a method must be at least as accessible as the method
            //    itself.
            // 7) The type of a property must be at least as accessible as the property itself.
            // 8) The type of an event must be at least as accessible as the event itself.
            // 9) The type of an indexer must be at least as accessible as the indexer itself.
            // 10) The return type of an operator must be at least as accessible as the operator
            //     itself.
            if (type.IsParentKind(SyntaxKind.DelegateDeclaration) ||
                type.IsParentKind(SyntaxKind.MethodDeclaration) ||
                type.IsParentKind(SyntaxKind.PropertyDeclaration) ||
                type.IsParentKind(SyntaxKind.EventDeclaration) ||
                type.IsParentKind(SyntaxKind.IndexerDeclaration) ||
                type.IsParentKind(SyntaxKind.OperatorDeclaration))
            {
                return(semanticModel.GetDeclaredSymbol(
                           type.Parent, cancellationToken).DeclaredAccessibility);
            }

            // 3) The parameter types of a delegate type must be at least as accessible as the
            //    delegate type itself.
            // 6) The parameter types of a method must be at least as accessible as the method
            //    itself.
            // 9) The parameter types of an indexer must be at least as accessible as the
            //    indexer itself.
            // 10) The parameter types of an operator must be at least as accessible as the
            //     operator itself.
            // 11) The parameter types of an instance constructor must be at least as accessible
            //     as the instance constructor itself.
            if (type.IsParentKind(SyntaxKind.Parameter) && type.Parent.IsParentKind(SyntaxKind.ParameterList))
            {
                if (type.Parent.Parent.IsParentKind(SyntaxKind.DelegateDeclaration) ||
                    type.Parent.Parent.IsParentKind(SyntaxKind.MethodDeclaration) ||
                    type.Parent.Parent.IsParentKind(SyntaxKind.IndexerDeclaration) ||
                    type.Parent.Parent.IsParentKind(SyntaxKind.OperatorDeclaration))
                {
                    return(semanticModel.GetDeclaredSymbol(
                               type.Parent.Parent.Parent, cancellationToken).DeclaredAccessibility);
                }

                if (type.Parent.Parent.IsParentKind(SyntaxKind.ConstructorDeclaration))
                {
                    var symbol = semanticModel.GetDeclaredSymbol(type.Parent.Parent.Parent, cancellationToken);
                    if (!symbol.IsStatic)
                    {
                        return(symbol.DeclaredAccessibility);
                    }
                }
            }

            // 8) The type of an event must be at least as accessible as the event itself.
            if (type.IsParentKind(SyntaxKind.VariableDeclaration) &&
                type.Parent.IsParentKind(SyntaxKind.EventFieldDeclaration))
            {
                var variableDeclaration = (VariableDeclarationSyntax)type.Parent;
                var symbol = semanticModel.GetDeclaredSymbol(variableDeclaration.Variables[0], cancellationToken);
                if (symbol != null)
                {
                    return(symbol.DeclaredAccessibility);
                }
            }

            return(Accessibility.Private);
        }