in src/Workspaces/CSharp/Portable/Extensions/ExpressionSyntaxExtensions.cs [1336:1666]
private static bool TryReduce(
this NameSyntax name,
SemanticModel semanticModel,
out TypeSyntax replacementNode,
out TextSpan issueSpan,
OptionSet optionSet,
CancellationToken cancellationToken)
{
replacementNode = null;
issueSpan = default;
if (name.IsVar)
{
return false;
}
// we should not simplify a name of a namespace declaration
if (IsPartOfNamespaceDeclarationName(name))
{
return false;
}
// We can simplify Qualified names and AliasQualifiedNames. Generally, if we have
// something like "A.B.C.D", we only consider the full thing something we can simplify.
// However, in the case of "A.B.C<>.D", then we'll only consider simplifying up to the
// first open name. This is because if we remove the open name, we'll often change
// meaning as "D" will bind to C<T>.D which is different than C<>.D!
if (name is QualifiedNameSyntax qualifiedName)
{
var left = qualifiedName.Left;
if (ContainsOpenName(left))
{
// Don't simplify A.B<>.C
return false;
}
}
// 1. see whether binding the name binds to a symbol/type. if not, it is ambiguous and
// nothing we can do here.
var symbol = SimplificationHelpers.GetOriginalSymbolInfo(semanticModel, name);
if (symbol == null)
{
return false;
}
// treat constructor names as types
var method = symbol as IMethodSymbol;
if (method.IsConstructor())
{
symbol = method.ContainingType;
}
if (symbol.Kind == SymbolKind.Method && name.Kind() == SyntaxKind.GenericName)
{
// The option wants the generic method invocation name to be explicit, then quit the reduction
if (!optionSet.GetOption(SimplificationOptions.PreferImplicitTypeInference))
{
return false;
}
var genericName = (GenericNameSyntax)name;
replacementNode = SyntaxFactory.IdentifierName(genericName.Identifier)
.WithLeadingTrivia(genericName.GetLeadingTrivia())
.WithTrailingTrivia(genericName.GetTrailingTrivia());
issueSpan = genericName.TypeArgumentList.Span;
return name.CanReplaceWithReducedName(replacementNode, semanticModel, cancellationToken);
}
if (!(symbol is INamespaceOrTypeSymbol))
{
return false;
}
if (name.HasAnnotations(SpecialTypeAnnotation.Kind))
{
replacementNode = SyntaxFactory.PredefinedType(
SyntaxFactory.Token(
name.GetLeadingTrivia(),
GetPredefinedKeywordKind(SpecialTypeAnnotation.GetSpecialType(name.GetAnnotations(SpecialTypeAnnotation.Kind).First())),
name.GetTrailingTrivia()));
issueSpan = name.Span;
return name.CanReplaceWithReducedNameInContext(replacementNode, semanticModel, cancellationToken);
}
else
{
if (!name.IsRightSideOfDotOrColonColon())
{
if (name.TryReplaceWithAlias(semanticModel, optionSet.GetOption(SimplificationOptions.PreferAliasToQualification), cancellationToken, out var aliasReplacement))
{
// get the token text as it appears in source code to preserve e.g. Unicode character escaping
var text = aliasReplacement.Name;
var syntaxRef = aliasReplacement.DeclaringSyntaxReferences.FirstOrDefault();
if (syntaxRef != null)
{
var declIdentifier = ((UsingDirectiveSyntax)syntaxRef.GetSyntax(cancellationToken)).Alias.Name.Identifier;
text = declIdentifier.IsVerbatimIdentifier() ? declIdentifier.ToString().Substring(1) : declIdentifier.ToString();
}
var identifierToken = SyntaxFactory.Identifier(
name.GetLeadingTrivia(),
SyntaxKind.IdentifierToken,
text,
aliasReplacement.Name,
name.GetTrailingTrivia());
identifierToken = CSharpSimplificationService.TryEscapeIdentifierToken(identifierToken, name, semanticModel);
replacementNode = SyntaxFactory.IdentifierName(identifierToken);
// Merge annotation to new syntax node
var annotatedNodesOrTokens = name.GetAnnotatedNodesAndTokens(RenameAnnotation.Kind);
foreach (var annotatedNodeOrToken in annotatedNodesOrTokens)
{
if (annotatedNodeOrToken.IsToken)
{
identifierToken = annotatedNodeOrToken.AsToken().CopyAnnotationsTo(identifierToken);
}
else
{
replacementNode = annotatedNodeOrToken.AsNode().CopyAnnotationsTo(replacementNode);
}
}
annotatedNodesOrTokens = name.GetAnnotatedNodesAndTokens(AliasAnnotation.Kind);
foreach (var annotatedNodeOrToken in annotatedNodesOrTokens)
{
if (annotatedNodeOrToken.IsToken)
{
identifierToken = annotatedNodeOrToken.AsToken().CopyAnnotationsTo(identifierToken);
}
else
{
replacementNode = annotatedNodeOrToken.AsNode().CopyAnnotationsTo(replacementNode);
}
}
replacementNode = ((SimpleNameSyntax)replacementNode).WithIdentifier(identifierToken);
issueSpan = name.Span;
// In case the alias name is the same as the last name of the alias target, we only include
// the left part of the name in the unnecessary span to Not confuse uses.
if (name.Kind() == SyntaxKind.QualifiedName)
{
var qualifiedName3 = (QualifiedNameSyntax)name;
if (qualifiedName3.Right.Identifier.ValueText == identifierToken.ValueText)
{
issueSpan = qualifiedName3.Left.Span;
}
}
// first check if this would be a valid reduction
if (name.CanReplaceWithReducedNameInContext(replacementNode, semanticModel, cancellationToken))
{
// in case this alias name ends with "Attribute", we're going to see if we can also
// remove that suffix.
if (TryReduceAttributeSuffix(
name,
identifierToken,
semanticModel,
out var replacementNodeWithoutAttributeSuffix,
out var issueSpanWithoutAttributeSuffix,
cancellationToken))
{
if (name.CanReplaceWithReducedName(replacementNodeWithoutAttributeSuffix, semanticModel, cancellationToken))
{
replacementNode = replacementNode.CopyAnnotationsTo(replacementNodeWithoutAttributeSuffix);
issueSpan = issueSpanWithoutAttributeSuffix;
}
}
return true;
}
return false;
}
var nameHasNoAlias = false;
if (name is SimpleNameSyntax simpleName)
{
if (!simpleName.Identifier.HasAnnotations(AliasAnnotation.Kind))
{
nameHasNoAlias = true;
}
}
if (name is QualifiedNameSyntax qualifiedName2)
{
if (!qualifiedName2.Right.HasAnnotation(Simplifier.SpecialTypeAnnotation))
{
nameHasNoAlias = true;
}
}
if (name is AliasQualifiedNameSyntax aliasQualifiedName)
{
if (aliasQualifiedName.Name is SimpleNameSyntax &&
!aliasQualifiedName.Name.Identifier.HasAnnotations(AliasAnnotation.Kind) &&
!aliasQualifiedName.Name.HasAnnotation(Simplifier.SpecialTypeAnnotation))
{
nameHasNoAlias = true;
}
}
var aliasInfo = semanticModel.GetAliasInfo(name, cancellationToken);
if (nameHasNoAlias && aliasInfo == null)
{
if (IsReplaceableByVar(name, semanticModel, out replacementNode, out issueSpan, optionSet, cancellationToken))
{
return true;
}
// Don't simplify to predefined type if name is part of a QualifiedName.
// QualifiedNames can't contain PredefinedTypeNames (although MemberAccessExpressions can).
// In other words, the left side of a QualifiedName can't be a PredefinedTypeName.
var inDeclarationContext = PreferPredefinedTypeKeywordInDeclarations(name, optionSet, semanticModel);
var inMemberAccessContext = PreferPredefinedTypeKeywordInMemberAccess(name, optionSet, semanticModel);
if (!name.Parent.IsKind(SyntaxKind.QualifiedName) && (inDeclarationContext || inMemberAccessContext))
{
var codeStyleOptionName = inDeclarationContext
? nameof(CodeStyleOptions.PreferIntrinsicPredefinedTypeKeywordInDeclaration)
: nameof(CodeStyleOptions.PreferIntrinsicPredefinedTypeKeywordInMemberAccess);
var type = semanticModel.GetTypeInfo(name, cancellationToken).Type;
if (type != null)
{
var keywordKind = GetPredefinedKeywordKind(type.SpecialType);
if (keywordKind != SyntaxKind.None)
{
return CanReplaceWithPredefinedTypeKeywordInContext(name, semanticModel, out replacementNode, ref issueSpan, keywordKind, codeStyleOptionName, cancellationToken);
}
}
else
{
var typeSymbol = semanticModel.GetSymbolInfo(name, cancellationToken).Symbol;
if (typeSymbol.IsKind(SymbolKind.NamedType))
{
var keywordKind = GetPredefinedKeywordKind(((INamedTypeSymbol)typeSymbol).SpecialType);
if (keywordKind != SyntaxKind.None)
{
return CanReplaceWithPredefinedTypeKeywordInContext(name, semanticModel, out replacementNode, ref issueSpan, keywordKind, codeStyleOptionName, cancellationToken);
}
}
}
}
}
// Nullable rewrite: Nullable<int> -> int?
// Don't rewrite in the case where Nullable<int> is part of some qualified name like Nullable<int>.Something
if (!name.IsVar && (symbol.Kind == SymbolKind.NamedType) && !name.IsLeftSideOfQualifiedName())
{
var type = (INamedTypeSymbol)symbol;
if (aliasInfo == null && CanSimplifyNullable(type, name, semanticModel))
{
GenericNameSyntax genericName;
if (name.Kind() == SyntaxKind.QualifiedName)
{
genericName = (GenericNameSyntax)((QualifiedNameSyntax)name).Right;
}
else
{
genericName = (GenericNameSyntax)name;
}
var oldType = genericName.TypeArgumentList.Arguments.First();
if (oldType.Kind() == SyntaxKind.OmittedTypeArgument)
{
return false;
}
replacementNode = SyntaxFactory.NullableType(oldType)
.WithLeadingTrivia(name.GetLeadingTrivia())
.WithTrailingTrivia(name.GetTrailingTrivia());
issueSpan = name.Span;
// we need to simplify the whole qualified name at once, because replacing the identifier on the left in
// System.Nullable<int> alone would be illegal.
// If this fails we want to continue to try at least to remove the System if possible.
if (name.CanReplaceWithReducedNameInContext(replacementNode, semanticModel, cancellationToken))
{
return true;
}
}
}
}
SyntaxToken identifier;
switch (name.Kind())
{
case SyntaxKind.AliasQualifiedName:
var simpleName = ((AliasQualifiedNameSyntax)name).Name
.WithLeadingTrivia(name.GetLeadingTrivia());
simpleName = simpleName.ReplaceToken(simpleName.Identifier,
((AliasQualifiedNameSyntax)name).Name.Identifier.CopyAnnotationsTo(
simpleName.Identifier.WithLeadingTrivia(
((AliasQualifiedNameSyntax)name).Alias.Identifier.LeadingTrivia)));
replacementNode = simpleName;
issueSpan = ((AliasQualifiedNameSyntax)name).Alias.Span;
break;
case SyntaxKind.QualifiedName:
replacementNode = ((QualifiedNameSyntax)name).Right.WithLeadingTrivia(name.GetLeadingTrivia());
issueSpan = ((QualifiedNameSyntax)name).Left.Span;
break;
case SyntaxKind.IdentifierName:
identifier = ((IdentifierNameSyntax)name).Identifier;
// we can try to remove the Attribute suffix if this is the attribute name
TryReduceAttributeSuffix(name, identifier, semanticModel, out replacementNode, out issueSpan, cancellationToken);
break;
}
}
if (replacementNode == null)
{
return false;
}
return name.CanReplaceWithReducedName(replacementNode, semanticModel, cancellationToken);
}