public static bool IsMemberDeclarationContext(
			this SyntaxTree syntaxTree,
			int position,
			CSharpSyntaxContext contextOpt,
			ISet<SyntaxKind> validModifiers,
			ISet<SyntaxKind> validTypeDeclarations,
			bool canBePartial,
			CancellationToken cancellationToken)
		{
			var typeDecl = contextOpt != null
				? contextOpt.ContainingTypeOrEnumDeclaration
				: syntaxTree.GetContainingTypeOrEnumDeclaration(position, cancellationToken);

			if (typeDecl == null)
			{
				return false;
			}

			if (!validTypeDeclarations.Contains(typeDecl.Kind()))
			{
				return false;
			}

			validTypeDeclarations = validTypeDeclarations ?? SpecializedCollections.EmptySet<SyntaxKind>();

			// Check many of the simple cases first.
			var leftToken = contextOpt != null
				? contextOpt.LeftToken
				: syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken);

			if (syntaxTree.IsMemberDeclarationContext(position, leftToken, cancellationToken))
			{
				return true;
			}

			var token = contextOpt != null
				? contextOpt.TargetToken
				: leftToken.GetPreviousTokenIfTouchingWord(position);

			// A member can also show up after certain types of modifiers
			if (canBePartial &&
				token.IsKindOrHasMatchingText(SyntaxKind.PartialKeyword))
			{
				return true;
			}

			var modifierTokens = contextOpt != null
				? contextOpt.PrecedingModifiers
				: syntaxTree.GetPrecedingModifiers(position, leftToken, cancellationToken);

			if (!modifierTokens.Any())
			{
				return false;
			}

			validModifiers = validModifiers ?? SpecializedCollections.EmptySet<SyntaxKind>();

			if (modifierTokens.IsSubsetOf(validModifiers))
			{
				var member = token.Parent;
				if (token.HasMatchingText(SyntaxKind.AsyncKeyword))
				{
					// second appearance of "async", not followed by modifier: treat it as type
					if (syntaxTree.GetPrecedingModifiers(token.SpanStart, token, cancellationToken).Any(x => x == SyntaxKind.AsyncKeyword))
					{
						return false;
					}

					// rule out async lambdas inside a method
					if (token.GetAncestor<StatementSyntax>() == null)
					{
						member = token.GetAncestor<MemberDeclarationSyntax>();
					}
				}

				// cases:
				// public |
				// async |
				// public async |
				return member != null &&
					member.Parent is BaseTypeDeclarationSyntax;
			}

			return false;
		}
		public static bool IsTypeDeclarationContext(
			this SyntaxTree syntaxTree,
			int position,
			CSharpSyntaxContext contextOpt,
			ISet<SyntaxKind> validModifiers,
			ISet<SyntaxKind> validTypeDeclarations,
			bool canBePartial,
			CancellationToken cancellationToken)
		{
			// We only allow nested types inside a class or struct, not inside a
			// an interface or enum.
			var typeDecl = contextOpt != null
				? contextOpt.ContainingTypeDeclaration
				: syntaxTree.GetContainingTypeDeclaration(position, cancellationToken);

			validTypeDeclarations = validTypeDeclarations ?? SpecializedCollections.EmptySet<SyntaxKind>();

			if (typeDecl != null)
			{
				if (!validTypeDeclarations.Contains(typeDecl.Kind()))
				{
					return false;
				}
			}

			// Check many of the simple cases first.
			var leftToken = contextOpt != null
				? contextOpt.LeftToken
				: syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken);

			if (syntaxTree.IsTypeDeclarationContext(position, leftToken, cancellationToken))
			{
				return true;
			}

			// If we're touching the right of an identifier, move back to
			// previous token.
			var token = contextOpt != null
				? contextOpt.TargetToken
				: leftToken.GetPreviousTokenIfTouchingWord(position);

			// A type can also show up after certain types of modifiers
			if (canBePartial &&
				token.IsKindOrHasMatchingText(SyntaxKind.PartialKeyword))
			{
				return true;
			}

			// using static | is never a type declaration context
			if (token.IsStaticKeywordInUsingDirective())
			{
				return false;
			}

			var modifierTokens = contextOpt != null
				? contextOpt.PrecedingModifiers
				: syntaxTree.GetPrecedingModifiers(position, leftToken, cancellationToken);

			if (!modifierTokens.Any())
			{
				return false;
			}

			validModifiers = validModifiers ?? SpecializedCollections.EmptySet<SyntaxKind>();

			if (modifierTokens.IsProperSubsetOf(validModifiers))
			{
				// the parent is the member
				// the grandparent is the container of the member
				var container = token.Parent.GetParent();
				if (container.IsKind(SyntaxKind.CompilationUnit) ||
					container.IsKind(SyntaxKind.NamespaceDeclaration) ||
					container.IsKind(SyntaxKind.ClassDeclaration) ||
					container.IsKind(SyntaxKind.StructDeclaration))
				{
					return true;
				}
			}

			return false;
		}
		SyntaxContext(CSharpSyntaxContext ctx, List<ITypeSymbol> inferredTypes)
		{
			this.inferredTypes = inferredTypes;
			this.ctx = ctx;
		}