in src/Features/CSharp/Portable/GenerateType/CSharpGenerateTypeService.cs [131:457]
protected override bool TryInitializeState(
SemanticDocument document,
SimpleNameSyntax simpleName,
CancellationToken cancellationToken,
out GenerateTypeServiceStateOptions generateTypeServiceStateOptions)
{
generateTypeServiceStateOptions = new GenerateTypeServiceStateOptions();
if (simpleName.IsVar)
{
return false;
}
if (SyntaxFacts.IsAliasQualifier(simpleName))
{
return false;
}
// Never offer if we're in a using directive, unless its a static using. The feeling here is that it's highly
// unlikely that this would be a location where a user would be wanting to generate
// something. They're really just trying to reference something that exists but
// isn't available for some reason (i.e. a missing reference).
var usingDirectiveSyntax = simpleName.GetAncestorOrThis<UsingDirectiveSyntax>();
if (usingDirectiveSyntax != null && usingDirectiveSyntax.StaticKeyword.Kind() != SyntaxKind.StaticKeyword)
{
return false;
}
ExpressionSyntax nameOrMemberAccessExpression = null;
if (simpleName.IsRightSideOfDot())
{
// This simplename comes from the cref
if (simpleName.IsParentKind(SyntaxKind.NameMemberCref))
{
return false;
}
nameOrMemberAccessExpression = generateTypeServiceStateOptions.NameOrMemberAccessExpression = (ExpressionSyntax)simpleName.Parent;
// If we're on the right side of a dot, then the left side better be a name (and
// not an arbitrary expression).
var leftSideExpression = simpleName.GetLeftSideOfDot();
if (!leftSideExpression.IsKind(
SyntaxKind.QualifiedName,
SyntaxKind.IdentifierName,
SyntaxKind.AliasQualifiedName,
SyntaxKind.GenericName,
SyntaxKind.SimpleMemberAccessExpression))
{
return false;
}
}
else
{
nameOrMemberAccessExpression = generateTypeServiceStateOptions.NameOrMemberAccessExpression = simpleName;
}
// BUG(5712): Don't offer generate type in an enum's base list.
if (nameOrMemberAccessExpression.Parent is BaseTypeSyntax &&
nameOrMemberAccessExpression.Parent.IsParentKind(SyntaxKind.BaseList) &&
((BaseTypeSyntax)nameOrMemberAccessExpression.Parent).Type == nameOrMemberAccessExpression &&
nameOrMemberAccessExpression.Parent.Parent.IsParentKind(SyntaxKind.EnumDeclaration))
{
return false;
}
// If we can guarantee it's a type only context, great. Otherwise, we may not want to
// provide this here.
var semanticModel = document.SemanticModel;
if (!SyntaxFacts.IsInNamespaceOrTypeContext(nameOrMemberAccessExpression))
{
// Don't offer Generate Type in an expression context *unless* we're on the left
// side of a dot. In that case the user might be making a type that they're
// accessing a static off of.
var syntaxTree = semanticModel.SyntaxTree;
var start = nameOrMemberAccessExpression.SpanStart;
var tokenOnLeftOfStart = syntaxTree.FindTokenOnLeftOfPosition(start, cancellationToken);
var isExpressionContext = syntaxTree.IsExpressionContext(start, tokenOnLeftOfStart, attributes: true, cancellationToken: cancellationToken, semanticModelOpt: semanticModel);
var isStatementContext = syntaxTree.IsStatementContext(start, tokenOnLeftOfStart, cancellationToken);
var isExpressionOrStatementContext = isExpressionContext || isStatementContext;
// Delegate Type Creation is not allowed in Non Type Namespace Context
generateTypeServiceStateOptions.IsDelegateAllowed = false;
if (!isExpressionOrStatementContext)
{
return false;
}
if (!simpleName.IsLeftSideOfDot() && !simpleName.IsInsideNameOf())
{
if (nameOrMemberAccessExpression == null || !nameOrMemberAccessExpression.IsKind(SyntaxKind.SimpleMemberAccessExpression) || !simpleName.IsRightSideOfDot())
{
return false;
}
var leftSymbol = semanticModel.GetSymbolInfo(((MemberAccessExpressionSyntax)nameOrMemberAccessExpression).Expression, cancellationToken).Symbol;
var token = simpleName.GetLastToken().GetNextToken();
// We let only the Namespace to be left of the Dot
if (leftSymbol == null ||
!leftSymbol.IsKind(SymbolKind.Namespace) ||
!token.IsKind(SyntaxKind.DotToken))
{
return false;
}
else
{
generateTypeServiceStateOptions.IsMembersWithModule = true;
generateTypeServiceStateOptions.IsTypeGeneratedIntoNamespaceFromMemberAccess = true;
}
}
// Global Namespace
if (!generateTypeServiceStateOptions.IsTypeGeneratedIntoNamespaceFromMemberAccess &&
!SyntaxFacts.IsInNamespaceOrTypeContext(simpleName))
{
var token = simpleName.GetLastToken().GetNextToken();
if (token.IsKind(SyntaxKind.DotToken) &&
simpleName.Parent == token.Parent)
{
generateTypeServiceStateOptions.IsMembersWithModule = true;
generateTypeServiceStateOptions.IsTypeGeneratedIntoNamespaceFromMemberAccess = true;
}
}
}
var fieldDeclaration = simpleName.GetAncestor<FieldDeclarationSyntax>();
if (fieldDeclaration != null &&
fieldDeclaration.Parent is CompilationUnitSyntax &&
document.Document.SourceCodeKind == SourceCodeKind.Regular)
{
return false;
}
// Check to see if Module could be an option in the Type Generation in Cross Language Generation
var nextToken = simpleName.GetLastToken().GetNextToken();
if (simpleName.IsLeftSideOfDot() ||
nextToken.IsKind(SyntaxKind.DotToken))
{
if (simpleName.IsRightSideOfDot())
{
if (simpleName.Parent is QualifiedNameSyntax parent)
{
var leftSymbol = semanticModel.GetSymbolInfo(parent.Left, cancellationToken).Symbol;
if (leftSymbol != null && leftSymbol.IsKind(SymbolKind.Namespace))
{
generateTypeServiceStateOptions.IsMembersWithModule = true;
}
}
}
}
if (SyntaxFacts.IsInNamespaceOrTypeContext(nameOrMemberAccessExpression))
{
if (nextToken.IsKind(SyntaxKind.DotToken))
{
// In Namespace or Type Context we cannot have Interface, Enum, Delegate as part of the Left Expression of a QualifiedName
generateTypeServiceStateOptions.IsDelegateAllowed = false;
generateTypeServiceStateOptions.IsInterfaceOrEnumNotAllowedInTypeContext = true;
generateTypeServiceStateOptions.IsMembersWithModule = true;
}
// case: class Goo<T> where T: MyType
if (nameOrMemberAccessExpression.GetAncestors<TypeConstraintSyntax>().Any())
{
generateTypeServiceStateOptions.IsClassInterfaceTypes = true;
return true;
}
// Events
if (nameOrMemberAccessExpression.GetAncestors<EventFieldDeclarationSyntax>().Any() ||
nameOrMemberAccessExpression.GetAncestors<EventDeclarationSyntax>().Any())
{
// Case : event goo name11
// Only Delegate
if (simpleName.Parent != null && !(simpleName.Parent is QualifiedNameSyntax))
{
generateTypeServiceStateOptions.IsDelegateOnly = true;
return true;
}
// Case : event SomeSymbol.goo name11
if (nameOrMemberAccessExpression is QualifiedNameSyntax)
{
// Only Namespace, Class, Struct and Module are allowed to contain Delegate
// Case : event Something.Mytype.<Delegate> Identifier
if (nextToken.IsKind(SyntaxKind.DotToken))
{
if (nameOrMemberAccessExpression.Parent != null && nameOrMemberAccessExpression.Parent is QualifiedNameSyntax)
{
return true;
}
else
{
Contract.Fail("Cannot reach this point");
}
}
else
{
// Case : event Something.<Delegate> Identifier
generateTypeServiceStateOptions.IsDelegateOnly = true;
return true;
}
}
}
}
else
{
// MemberAccessExpression
if ((nameOrMemberAccessExpression.IsKind(SyntaxKind.SimpleMemberAccessExpression) || (nameOrMemberAccessExpression.Parent != null && nameOrMemberAccessExpression.IsParentKind(SyntaxKind.SimpleMemberAccessExpression)))
&& nameOrMemberAccessExpression.IsLeftSideOfDot())
{
// Check to see if the expression is part of Invocation Expression
ExpressionSyntax outerMostMemberAccessExpression = null;
if (nameOrMemberAccessExpression.IsKind(SyntaxKind.SimpleMemberAccessExpression))
{
outerMostMemberAccessExpression = nameOrMemberAccessExpression;
}
else
{
Debug.Assert(nameOrMemberAccessExpression.IsParentKind(SyntaxKind.SimpleMemberAccessExpression));
outerMostMemberAccessExpression = (ExpressionSyntax)nameOrMemberAccessExpression.Parent;
}
outerMostMemberAccessExpression = outerMostMemberAccessExpression.GetAncestorsOrThis<ExpressionSyntax>().SkipWhile(n => n != null && n.IsKind(SyntaxKind.SimpleMemberAccessExpression)).FirstOrDefault();
if (outerMostMemberAccessExpression != null && outerMostMemberAccessExpression is InvocationExpressionSyntax)
{
generateTypeServiceStateOptions.IsEnumNotAllowed = true;
}
}
}
// Cases:
// // 1 - Function Address
// var s2 = new MyD2(goo);
// // 2 - Delegate
// MyD1 d = null;
// var s1 = new MyD2(d);
// // 3 - Action
// Action action1 = null;
// var s3 = new MyD2(action1);
// // 4 - Func
// Func<int> lambda = () => { return 0; };
// var s4 = new MyD3(lambda);
if (nameOrMemberAccessExpression.Parent is ObjectCreationExpressionSyntax)
{
var objectCreationExpressionOpt = generateTypeServiceStateOptions.ObjectCreationExpressionOpt = (ObjectCreationExpressionSyntax)nameOrMemberAccessExpression.Parent;
// Enum and Interface not Allowed in Object Creation Expression
generateTypeServiceStateOptions.IsInterfaceOrEnumNotAllowedInTypeContext = true;
if (objectCreationExpressionOpt.ArgumentList != null)
{
if (objectCreationExpressionOpt.ArgumentList.CloseParenToken.IsMissing)
{
return false;
}
// Get the Method symbol for the Delegate to be created
if (generateTypeServiceStateOptions.IsDelegateAllowed &&
objectCreationExpressionOpt.ArgumentList.Arguments.Count == 1 &&
objectCreationExpressionOpt.ArgumentList.Arguments[0].Expression.Kind() != SyntaxKind.DeclarationExpression)
{
generateTypeServiceStateOptions.DelegateCreationMethodSymbol = GetMethodSymbolIfPresent(semanticModel, objectCreationExpressionOpt.ArgumentList.Arguments[0].Expression, cancellationToken);
}
else
{
generateTypeServiceStateOptions.IsDelegateAllowed = false;
}
}
if (objectCreationExpressionOpt.Initializer != null)
{
foreach (var expression in objectCreationExpressionOpt.Initializer.Expressions)
{
var simpleAssignmentExpression = expression as AssignmentExpressionSyntax;
if (simpleAssignmentExpression == null)
{
continue;
}
var name = simpleAssignmentExpression.Left as SimpleNameSyntax;
if (name == null)
{
continue;
}
generateTypeServiceStateOptions.PropertiesToGenerate.Add(name);
}
}
}
if (generateTypeServiceStateOptions.IsDelegateAllowed)
{
// MyD1 z1 = goo;
if (nameOrMemberAccessExpression.Parent.IsKind(SyntaxKind.VariableDeclaration))
{
var variableDeclaration = (VariableDeclarationSyntax)nameOrMemberAccessExpression.Parent;
if (variableDeclaration.Variables.Count != 0)
{
var firstVarDeclWithInitializer = variableDeclaration.Variables.FirstOrDefault(var => var.Initializer != null && var.Initializer.Value != null);
if (firstVarDeclWithInitializer != null && firstVarDeclWithInitializer.Initializer != null && firstVarDeclWithInitializer.Initializer.Value != null)
{
generateTypeServiceStateOptions.DelegateCreationMethodSymbol = GetMethodSymbolIfPresent(semanticModel, firstVarDeclWithInitializer.Initializer.Value, cancellationToken);
}
}
}
// var w1 = (MyD1)goo;
if (nameOrMemberAccessExpression.Parent.IsKind(SyntaxKind.CastExpression))
{
var castExpression = (CastExpressionSyntax)nameOrMemberAccessExpression.Parent;
if (castExpression.Expression != null)
{
generateTypeServiceStateOptions.DelegateCreationMethodSymbol = GetMethodSymbolIfPresent(semanticModel, castExpression.Expression, cancellationToken);
}
}
}
return true;
}