// 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);
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); }
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); }