package checker

import (
	"iter"
	"math"
	"slices"

	"github.com/microsoft/typescript-go/internal/ast"
	"github.com/microsoft/typescript-go/internal/core"
	"github.com/microsoft/typescript-go/internal/debug"
	"github.com/microsoft/typescript-go/internal/diagnostics"
	"github.com/microsoft/typescript-go/internal/jsnum"
	"github.com/microsoft/typescript-go/internal/parser"
	"github.com/microsoft/typescript-go/internal/scanner"
)

type JsxFlags uint32

const (
	JsxFlagsNone                    JsxFlags = 0
	JsxFlagsIntrinsicNamedElement   JsxFlags = 1 << 0 // An element from a named property of the JSX.IntrinsicElements interface
	JsxFlagsIntrinsicIndexedElement JsxFlags = 1 << 1 // An element inferred from the string index signature of the JSX.IntrinsicElements interface
	JsxFlagsIntrinsicElement        JsxFlags = JsxFlagsIntrinsicNamedElement | JsxFlagsIntrinsicIndexedElement
)

type JsxReferenceKind int32

const (
	JsxReferenceKindComponent JsxReferenceKind = iota
	JsxReferenceKindFunction
	JsxReferenceKindMixed
)

type JsxElementLinks struct {
	jsxFlags                         JsxFlags    // Flags for the JSX element
	resolvedJsxElementAttributesType *Type       // Resolved element attributes type of a JSX opening-like element
	jsxNamespace                     *ast.Symbol // Resolved JSX namespace symbol for this node
	jsxImplicitImportContainer       *ast.Symbol // Resolved module symbol the implicit JSX import of this file should refer to
}

var JsxNames = struct {
	JSX                                    string
	IntrinsicElements                      string
	ElementClass                           string
	ElementAttributesPropertyNameContainer string
	ElementChildrenAttributeNameContainer  string
	Element                                string
	ElementType                            string
	IntrinsicAttributes                    string
	IntrinsicClassAttributes               string
	LibraryManagedAttributes               string
}{
	JSX:                                    "JSX",
	IntrinsicElements:                      "IntrinsicElements",
	ElementClass:                           "ElementClass",
	ElementAttributesPropertyNameContainer: "ElementAttributesProperty",
	ElementChildrenAttributeNameContainer:  "ElementChildrenAttribute",
	Element:                                "Element",
	ElementType:                            "ElementType",
	IntrinsicAttributes:                    "IntrinsicAttributes",
	IntrinsicClassAttributes:               "IntrinsicClassAttributes",
	LibraryManagedAttributes:               "LibraryManagedAttributes",
}

var ReactNames = struct {
	Fragment string
}{
	Fragment: "Fragment",
}

func (c *Checker) checkJsxElement(node *ast.Node, checkMode CheckMode) *Type {
	c.checkNodeDeferred(node)
	return c.getJsxElementTypeAt(node)
}

func (c *Checker) checkJsxElementDeferred(node *ast.Node) {
	jsxElement := node.AsJsxElement()
	c.checkJsxOpeningLikeElementOrOpeningFragment(jsxElement.OpeningElement)
	// Perform resolution on the closing tag so that rename/go to definition/etc work
	if isJsxIntrinsicTagName(jsxElement.ClosingElement.TagName()) {
		c.getIntrinsicTagSymbol(jsxElement.ClosingElement)
	} else {
		c.checkExpression(jsxElement.ClosingElement.TagName())
	}
	c.checkJsxChildren(node, CheckModeNormal)
}

func (c *Checker) checkJsxExpression(node *ast.Node, checkMode CheckMode) *Type {
	c.checkGrammarJsxExpression(node.AsJsxExpression())
	if node.Expression() == nil {
		return c.errorType
	}
	t := c.checkExpressionEx(node.Expression(), checkMode)
	if node.AsJsxExpression().DotDotDotToken != nil && t != c.anyType && !c.isArrayType(t) {
		c.error(node, diagnostics.JSX_spread_child_must_be_an_array_type)
	}
	return t
}

func (c *Checker) checkJsxSelfClosingElement(node *ast.Node, checkMode CheckMode) *Type {
	c.checkNodeDeferred(node)
	return c.getJsxElementTypeAt(node)
}

func (c *Checker) checkJsxSelfClosingElementDeferred(node *ast.Node) {
	c.checkJsxOpeningLikeElementOrOpeningFragment(node)
}

func (c *Checker) checkJsxFragment(node *ast.Node) *Type {
	c.checkJsxOpeningLikeElementOrOpeningFragment(node.AsJsxFragment().OpeningFragment)
	// by default, jsx:'react' will use jsxFactory = React.createElement and jsxFragmentFactory = React.Fragment
	// if jsxFactory compiler option is provided, ensure jsxFragmentFactory compiler option or @jsxFrag pragma is provided too
	nodeSourceFile := ast.GetSourceFileOfNode(node)
	if c.compilerOptions.GetJSXTransformEnabled() && (c.compilerOptions.JsxFactory != "" || ast.GetPragmaFromSourceFile(nodeSourceFile, "jsx") != nil) && c.compilerOptions.JsxFragmentFactory == "" && ast.GetPragmaFromSourceFile(nodeSourceFile, "jsxfrag") == nil {
		message := core.IfElse(c.compilerOptions.JsxFactory != "",
			diagnostics.The_jsxFragmentFactory_compiler_option_must_be_provided_to_use_JSX_fragments_with_the_jsxFactory_compiler_option,
			diagnostics.An_jsxFrag_pragma_is_required_when_using_an_jsx_pragma_with_JSX_fragments)
		c.error(node, message)
	}
	c.checkJsxChildren(node, CheckModeNormal)
	t := c.getJsxElementTypeAt(node)
	return core.IfElse(c.isErrorType(t), c.anyType, t)
}

func (c *Checker) checkJsxAttributes(node *ast.Node, checkMode CheckMode) *Type {
	return c.createJsxAttributesTypeFromAttributesProperty(node.Parent, checkMode)
}

func (c *Checker) checkJsxOpeningLikeElementOrOpeningFragment(node *ast.Node) {
	isNodeOpeningLikeElement := ast.IsJsxOpeningLikeElement(node)
	if isNodeOpeningLikeElement {
		c.checkGrammarJsxElement(node)
	}
	c.checkJsxPreconditions(node)
	c.markJsxAliasReferenced(node)
	sig := c.getResolvedSignature(node, nil, CheckModeNormal)
	c.checkDeprecatedSignature(sig, node)
	if isNodeOpeningLikeElement {
		elementTypeConstraint := c.getJsxElementTypeTypeAt(node)
		if elementTypeConstraint != nil {
			tagName := node.TagName()
			var tagType *Type
			if isJsxIntrinsicTagName(tagName) {
				tagType = c.getStringLiteralType(tagName.Text())
			} else {
				tagType = c.checkExpression(tagName)
			}
			var diags []*ast.Diagnostic
			if !c.checkTypeRelatedToEx(tagType, elementTypeConstraint, c.assignableRelation, tagName, diagnostics.Its_type_0_is_not_a_valid_JSX_element_type, &diags) {
				c.addDiagnostic(ast.NewDiagnosticChain(diags[0], diagnostics.X_0_cannot_be_used_as_a_JSX_component, scanner.GetTextOfNode(tagName)))
			}
		} else {
			c.checkJsxReturnAssignableToAppropriateBound(c.getJsxReferenceKind(node), c.getReturnTypeOfSignature(sig), node)
		}
	}
}

func (c *Checker) checkJsxPreconditions(errorNode *ast.Node) {
	// Preconditions for using JSX
	if c.compilerOptions.Jsx == core.JsxEmitNone {
		c.error(errorNode, diagnostics.Cannot_use_JSX_unless_the_jsx_flag_is_provided)
	}
	if c.noImplicitAny && c.getJsxElementTypeAt(errorNode) == nil {
		c.error(errorNode, diagnostics.JSX_element_implicitly_has_type_any_because_the_global_type_JSX_Element_does_not_exist)
	}
}

func (c *Checker) checkJsxReturnAssignableToAppropriateBound(refKind JsxReferenceKind, elemInstanceType *Type, openingLikeElement *ast.Node) {
	var diags []*ast.Diagnostic
	switch refKind {
	case JsxReferenceKindFunction:
		sfcReturnConstraint := c.getJsxStatelessElementTypeAt(openingLikeElement)
		if sfcReturnConstraint != nil {
			c.checkTypeRelatedToEx(elemInstanceType, sfcReturnConstraint, c.assignableRelation, openingLikeElement.TagName(), diagnostics.Its_return_type_0_is_not_a_valid_JSX_element, &diags)
		}
	case JsxReferenceKindComponent:
		classConstraint := c.getJsxElementClassTypeAt(openingLikeElement)
		if classConstraint != nil {
			// Issue an error if this return type isn't assignable to JSX.ElementClass, failing that
			c.checkTypeRelatedToEx(elemInstanceType, classConstraint, c.assignableRelation, openingLikeElement.TagName(), diagnostics.Its_instance_type_0_is_not_a_valid_JSX_element, &diags)
		}
	default:
		sfcReturnConstraint := c.getJsxStatelessElementTypeAt(openingLikeElement)
		classConstraint := c.getJsxElementClassTypeAt(openingLikeElement)
		if sfcReturnConstraint == nil || classConstraint == nil {
			return
		}
		combined := c.getUnionType([]*Type{sfcReturnConstraint, classConstraint})
		c.checkTypeRelatedToEx(elemInstanceType, combined, c.assignableRelation, openingLikeElement.TagName(), diagnostics.Its_element_type_0_is_not_a_valid_JSX_element, &diags)
	}
	if len(diags) != 0 {
		c.addDiagnostic(ast.NewDiagnosticChain(diags[0], diagnostics.X_0_cannot_be_used_as_a_JSX_component, scanner.GetTextOfNode(openingLikeElement.TagName())))
	}
}

func (c *Checker) inferJsxTypeArguments(node *ast.Node, signature *Signature, checkMode CheckMode, context *InferenceContext) []*Type {
	paramType := c.getEffectiveFirstArgumentForJsxSignature(signature, node)
	checkAttrType := c.checkExpressionWithContextualType(node.Attributes(), paramType, context, checkMode)
	c.inferTypes(context.inferences, checkAttrType, paramType, InferencePriorityNone, false)
	return c.getInferredTypes(context)
}

func (c *Checker) getContextualTypeForJsxExpression(node *ast.Node, contextFlags ContextFlags) *Type {
	switch {
	case ast.IsJsxAttributeLike(node.Parent):
		return c.getContextualType(node, contextFlags)
	case ast.IsJsxElement(node.Parent):
		return c.getContextualTypeForChildJsxExpression(node.Parent, node, contextFlags)
	}
	return nil
}

func (c *Checker) getContextualTypeForJsxAttribute(attribute *ast.Node, contextFlags ContextFlags) *Type {
	// When we trying to resolve JsxOpeningLikeElement as a stateless function element, we will already give its attributes a contextual type
	// which is a type of the parameter of the signature we are trying out.
	// If there is no contextual type (e.g. we are trying to resolve stateful component), get attributes type from resolving element's tagName
	if ast.IsJsxAttribute(attribute) {
		attributesType := c.getApparentTypeOfContextualType(attribute.Parent, contextFlags)
		if attributesType == nil || IsTypeAny(attributesType) {
			return nil
		}
		return c.getTypeOfPropertyOfContextualType(attributesType, attribute.Name().Text())
	}
	return c.getContextualType(attribute.Parent, contextFlags)
}

func (c *Checker) getContextualJsxElementAttributesType(node *ast.Node, contextFlags ContextFlags) *Type {
	if ast.IsJsxOpeningElement(node) && contextFlags != ContextFlagsIgnoreNodeInferences {
		index := c.findContextualNode(node.Parent, contextFlags == ContextFlagsNone)
		if index >= 0 {
			// Contextually applied type is moved from attributes up to the outer jsx attributes so when walking up from the children they get hit
			// _However_ to hit them from the _attributes_ we must look for them here; otherwise we'll used the declared type
			// (as below) instead!
			return c.contextualInfos[index].t
		}
	}
	return c.getContextualTypeForArgumentAtIndex(node, 0)
}

func (c *Checker) getContextualTypeForChildJsxExpression(node *ast.Node, child *ast.JsxChild, contextFlags ContextFlags) *Type {
	attributesType := c.getApparentTypeOfContextualType(node.AsJsxElement().OpeningElement.Attributes(), contextFlags)
	// JSX expression is in children of JSX Element, we will look for an "children" attribute (we get the name from JSX.ElementAttributesProperty)
	jsxChildrenPropertyName := c.getJsxElementChildrenPropertyName(c.getJsxNamespaceAt(node))
	if !(attributesType != nil && !IsTypeAny(attributesType) && jsxChildrenPropertyName != ast.InternalSymbolNameMissing && jsxChildrenPropertyName != "") {
		return nil
	}
	realChildren := ast.GetSemanticJsxChildren(node.Children().Nodes)
	childIndex := slices.Index(realChildren, child)
	childFieldType := c.getTypeOfPropertyOfContextualType(attributesType, jsxChildrenPropertyName)
	if childFieldType == nil {
		return nil
	}
	if len(realChildren) == 1 {
		return childFieldType
	}
	return c.mapTypeEx(childFieldType, func(t *Type) *Type {
		if c.isArrayLikeType(t) {
			return c.getIndexedAccessType(t, c.getNumberLiteralType(jsnum.Number(childIndex)))
		}
		return t
	}, true /*noReductions*/)
}

func (c *Checker) discriminateContextualTypeByJSXAttributes(node *ast.Node, contextualType *Type) *Type {
	key := DiscriminatedContextualTypeKey{nodeId: ast.GetNodeId(node), typeId: contextualType.id}
	if discriminated := c.discriminatedContextualTypes[key]; discriminated != nil {
		return discriminated
	}
	jsxChildrenPropertyName := c.getJsxElementChildrenPropertyName(c.getJsxNamespaceAt(node))
	discriminantProperties := core.Filter(node.Properties(), func(p *ast.Node) bool {
		symbol := p.Symbol()
		if symbol == nil || !ast.IsJsxAttribute(p) {
			return false
		}
		initializer := p.Initializer()
		return (initializer == nil || c.isPossiblyDiscriminantValue(initializer)) && c.isDiscriminantProperty(contextualType, symbol.Name)
	})
	discriminantMembers := core.Filter(c.getPropertiesOfType(contextualType), func(s *ast.Symbol) bool {
		if s.Flags&ast.SymbolFlagsOptional == 0 || node.Symbol() == nil {
			return false
		}
		element := node.Parent.Parent
		if s.Name == jsxChildrenPropertyName && ast.IsJsxElement(element) && len(ast.GetSemanticJsxChildren(element.Children().Nodes)) != 0 {
			return false
		}
		return node.Symbol().Members[s.Name] == nil && c.isDiscriminantProperty(contextualType, s.Name)
	})
	discriminator := &ObjectLiteralDiscriminator{c: c, props: discriminantProperties, members: discriminantMembers}
	discriminated := c.discriminateTypeByDiscriminableItems(contextualType, discriminator)
	c.discriminatedContextualTypes[key] = discriminated
	return discriminated
}

func (c *Checker) elaborateJsxComponents(node *ast.Node, source *Type, target *Type, relation *Relation, diagnosticOutput *[]*ast.Diagnostic) bool {
	reportedError := false
	for _, prop := range node.Properties() {
		if !ast.IsJsxSpreadAttribute(prop) && !isHyphenatedJsxName(prop.Name().Text()) {
			nameType := c.getStringLiteralType(prop.Name().Text())
			if nameType != nil && nameType.flags&TypeFlagsNever == 0 {
				reportedError = c.elaborateElement(source, target, relation, prop.Name(), prop.Initializer(), nameType, nil, nil, diagnosticOutput) || reportedError
			}
		}
	}
	if ast.IsJsxOpeningElement(node.Parent) && ast.IsJsxElement(node.Parent.Parent) {
		containingElement := node.Parent.Parent // Containing JSXElement
		childrenPropName := c.getJsxElementChildrenPropertyName(c.getJsxNamespaceAt(node))
		if childrenPropName == ast.InternalSymbolNameMissing {
			childrenPropName = "children"
		}
		childrenNameType := c.getStringLiteralType(childrenPropName)
		childrenTargetType := c.getIndexedAccessType(target, childrenNameType)
		validChildren := ast.GetSemanticJsxChildren(containingElement.Children().Nodes)
		if len(validChildren) == 0 {
			return reportedError
		}
		moreThanOneRealChildren := len(validChildren) > 1
		var arrayLikeTargetParts *Type
		var nonArrayLikeTargetParts *Type
		iterableType := c.getGlobalIterableType()
		if iterableType != c.emptyGenericType {
			anyIterable := c.createIterableType(c.anyType)
			arrayLikeTargetParts = c.filterType(childrenTargetType, func(t *Type) bool { return c.isTypeAssignableTo(t, anyIterable) })
			nonArrayLikeTargetParts = c.filterType(childrenTargetType, func(t *Type) bool { return !c.isTypeAssignableTo(t, anyIterable) })
		} else {
			arrayLikeTargetParts = c.filterType(childrenTargetType, c.isArrayOrTupleLikeType)
			nonArrayLikeTargetParts = c.filterType(childrenTargetType, func(t *Type) bool { return !c.isArrayOrTupleLikeType(t) })
		}
		var invalidTextDiagnostic *diagnostics.Message
		var invalidTextDiagnosticArgs []any
		getInvalidTextualChildDiagnostic := func() (*diagnostics.Message, []any) {
			if invalidTextDiagnostic == nil {
				tagNameText := scanner.GetTextOfNode(node.Parent.TagName())
				invalidTextDiagnostic = diagnostics.X_0_components_don_t_accept_text_as_child_elements_Text_in_JSX_has_the_type_string_but_the_expected_type_of_1_is_2
				invalidTextDiagnosticArgs = []any{tagNameText, childrenPropName, c.TypeToString(childrenTargetType)}
			}
			return invalidTextDiagnostic, invalidTextDiagnosticArgs
		}
		if moreThanOneRealChildren {
			if arrayLikeTargetParts != c.neverType {
				realSource := c.createTupleType(c.checkJsxChildren(containingElement, CheckModeNormal))
				children := c.generateJsxChildren(containingElement, getInvalidTextualChildDiagnostic)
				reportedError = c.elaborateIterableOrArrayLikeTargetElementwise(children, realSource, arrayLikeTargetParts, relation, diagnosticOutput) || reportedError
			} else if !c.isTypeRelatedTo(c.getIndexedAccessType(source, childrenNameType), childrenTargetType, relation) {
				// arity mismatch
				diag := c.error(containingElement.AsJsxElement().OpeningElement.TagName(), diagnostics.This_JSX_tag_s_0_prop_expects_a_single_child_of_type_1_but_multiple_children_were_provided, childrenPropName, c.TypeToString(childrenTargetType))
				c.reportDiagnostic(diag, diagnosticOutput)
				reportedError = true
			}
		} else {
			if nonArrayLikeTargetParts != c.neverType {
				child := validChildren[0]
				e := c.getElaborationElementForJsxChild(child, childrenNameType, getInvalidTextualChildDiagnostic)
				if e.errorNode != nil {
					reportedError = c.elaborateElement(source, target, relation, e.errorNode, e.innerExpression, e.nameType, nil, e.createDiagnostic, diagnosticOutput) || reportedError
				}
			} else if !c.isTypeRelatedTo(c.getIndexedAccessType(source, childrenNameType), childrenTargetType, relation) {
				// arity mismatch
				diag := c.error(containingElement.AsJsxElement().OpeningElement.TagName(), diagnostics.This_JSX_tag_s_0_prop_expects_type_1_which_requires_multiple_children_but_only_a_single_child_was_provided, childrenPropName, c.TypeToString(childrenTargetType))
				c.reportDiagnostic(diag, diagnosticOutput)
				reportedError = true
			}
		}
	}
	return reportedError
}

type JsxElaborationElement struct {
	errorNode        *ast.Node
	innerExpression  *ast.Node
	nameType         *Type
	createDiagnostic func(prop *ast.Node) *ast.Diagnostic // Optional: creates a custom diagnostic for this element
}

func (c *Checker) generateJsxChildren(node *ast.Node, getInvalidTextDiagnostic func() (*diagnostics.Message, []any)) iter.Seq[JsxElaborationElement] {
	return func(yield func(JsxElaborationElement) bool) {
		memberOffset := 0
		for i, child := range node.Children().Nodes {
			nameType := c.getNumberLiteralType(jsnum.Number(i - memberOffset))
			e := c.getElaborationElementForJsxChild(child, nameType, getInvalidTextDiagnostic)
			if e.errorNode != nil {
				if !yield(e) {
					return
				}
			} else {
				memberOffset++
			}
		}
	}
}

func (c *Checker) getElaborationElementForJsxChild(child *ast.Node, nameType *Type, getInvalidTextDiagnostic func() (*diagnostics.Message, []any)) JsxElaborationElement {
	switch child.Kind {
	case ast.KindJsxExpression:
		// child is of the type of the expression
		return JsxElaborationElement{errorNode: child, innerExpression: child.Expression(), nameType: nameType}
	case ast.KindJsxText:
		if child.AsJsxText().ContainsOnlyTriviaWhiteSpaces {
			// Whitespace only jsx text isn't real jsx text
			return JsxElaborationElement{}
		}
		// child is a string
		return JsxElaborationElement{
			errorNode:       child,
			innerExpression: nil,
			nameType:        nameType,
			createDiagnostic: func(prop *ast.Node) *ast.Diagnostic {
				errorMessage, errorArgs := getInvalidTextDiagnostic()
				return NewDiagnosticForNode(prop, errorMessage, errorArgs...)
			},
		}
	case ast.KindJsxElement, ast.KindJsxSelfClosingElement, ast.KindJsxFragment:
		// child is of type JSX.Element
		return JsxElaborationElement{errorNode: child, innerExpression: child, nameType: nameType}
	}
	panic("Unhandled case in getElaborationElementForJsxChild")
}

func (c *Checker) elaborateIterableOrArrayLikeTargetElementwise(iterator iter.Seq[JsxElaborationElement], source *Type, target *Type, relation *Relation, diagnosticOutput *[]*ast.Diagnostic) bool {
	tupleOrArrayLikeTargetParts := c.filterType(target, c.isArrayOrTupleLikeType)
	nonTupleOrArrayLikeTargetParts := c.filterType(target, func(t *Type) bool { return !c.isArrayOrTupleLikeType(t) })
	// If `nonTupleOrArrayLikeTargetParts` is not `never`, then that should mean `Iterable` is defined.
	var iterationType *Type
	if nonTupleOrArrayLikeTargetParts != c.neverType {
		iterationType = c.getIterationTypeOfIterable(IterationUseForOf, IterationTypeKindYield, nonTupleOrArrayLikeTargetParts, nil /*errorNode*/)
	}
	reportedError := false
	for e := range iterator {
		prop := e.errorNode
		next := e.innerExpression
		nameType := e.nameType
		targetPropType := iterationType
		var targetIndexedPropType *Type
		if tupleOrArrayLikeTargetParts != c.neverType {
			targetIndexedPropType = c.getBestMatchIndexedAccessTypeOrUndefined(source, tupleOrArrayLikeTargetParts, nameType)
		}
		if targetIndexedPropType != nil && targetIndexedPropType.flags&TypeFlagsIndexedAccess == 0 {
			if iterationType != nil {
				targetPropType = c.getUnionType([]*Type{iterationType, targetIndexedPropType})
			} else {
				targetPropType = targetIndexedPropType
			}
		}
		if targetPropType == nil {
			continue
		}
		sourcePropType := c.getIndexedAccessTypeOrUndefined(source, nameType, AccessFlagsNone, nil, nil)
		if sourcePropType == nil {
			continue
		}
		propName := c.getPropertyNameFromIndex(nameType, nil /*accessNode*/)
		if !c.checkTypeRelatedTo(sourcePropType, targetPropType, relation, nil /*errorNode*/) {
			elaborated := next != nil && c.elaborateError(next, sourcePropType, targetPropType, relation, nil /*headMessage*/, diagnosticOutput)
			reportedError = true
			if !elaborated {
				// Issue error on the prop itself, since the prop couldn't elaborate the error. Use the expression type, if available.
				specificSource := sourcePropType
				if next != nil {
					specificSource = c.checkExpressionForMutableLocationWithContextualType(next, sourcePropType)
				}
				if e.createDiagnostic != nil {
					// Use the custom diagnostic factory if provided (e.g., for JSX text children with dynamic error messages)
					c.reportDiagnostic(e.createDiagnostic(prop), diagnosticOutput)
				} else if c.exactOptionalPropertyTypes && c.isExactOptionalPropertyMismatch(specificSource, targetPropType) {
					diag := createDiagnosticForNode(prop, diagnostics.Type_0_is_not_assignable_to_type_1_with_exactOptionalPropertyTypes_Colon_true_Consider_adding_undefined_to_the_type_of_the_target, c.TypeToString(specificSource), c.TypeToString(targetPropType))
					c.reportDiagnostic(diag, diagnosticOutput)
				} else {
					targetIsOptional := propName != ast.InternalSymbolNameMissing && core.OrElse(c.getPropertyOfType(tupleOrArrayLikeTargetParts, propName), c.unknownSymbol).Flags&ast.SymbolFlagsOptional != 0
					sourceIsOptional := propName != ast.InternalSymbolNameMissing && core.OrElse(c.getPropertyOfType(source, propName), c.unknownSymbol).Flags&ast.SymbolFlagsOptional != 0
					targetPropType = c.removeMissingType(targetPropType, targetIsOptional)
					sourcePropType = c.removeMissingType(sourcePropType, targetIsOptional && sourceIsOptional)
					result := c.checkTypeRelatedToEx(specificSource, targetPropType, relation, prop, nil, diagnosticOutput)
					if result && specificSource != sourcePropType {
						// If for whatever reason the expression type doesn't yield an error, make sure we still issue an error on the sourcePropType
						c.checkTypeRelatedToEx(sourcePropType, targetPropType, relation, prop, nil, diagnosticOutput)
					}
				}
			}
		}
	}
	return reportedError
}

func (c *Checker) getSuggestedSymbolForNonexistentJSXAttribute(name string, containingType *Type) *ast.Symbol {
	properties := c.getPropertiesOfType(containingType)
	var jsxSpecific *ast.Symbol
	switch name {
	case "for":
		jsxSpecific = core.Find(properties, func(x *ast.Symbol) bool { return ast.SymbolName(x) == "htmlFor" })
	case "class":
		jsxSpecific = core.Find(properties, func(x *ast.Symbol) bool { return ast.SymbolName(x) == "className" })
	}
	if jsxSpecific != nil {
		return jsxSpecific
	}
	return c.getSpellingSuggestionForName(name, slices.Values(properties), ast.SymbolFlagsValue)
}

func (c *Checker) getJSXFragmentType(node *ast.Node) *Type {
	// An opening fragment is required in order for `getJsxNamespace` to give the fragment factory
	links := c.sourceFileLinks.Get(ast.GetSourceFileOfNode(node))
	if links.jsxFragmentType != nil {
		return links.jsxFragmentType
	}
	jsxFragmentFactoryName := c.getJsxNamespace(node)
	// #38720/60122, allow null as jsxFragmentFactory
	shouldResolveFactoryReference := (c.compilerOptions.Jsx == core.JsxEmitReact || c.compilerOptions.JsxFragmentFactory != "") && jsxFragmentFactoryName != "null"
	if !shouldResolveFactoryReference {
		links.jsxFragmentType = c.anyType
		return links.jsxFragmentType
	}
	jsxFactorySymbol := c.getJsxNamespaceContainerForImplicitImport(node)
	if jsxFactorySymbol == nil {
		shouldModuleRefErr := c.compilerOptions.Jsx != core.JsxEmitPreserve && c.compilerOptions.Jsx != core.JsxEmitReactNative
		flags := ast.SymbolFlagsValue
		if !shouldModuleRefErr {
			flags &^= ast.SymbolFlagsEnum
		}
		jsxFactorySymbol = c.resolveName(node, jsxFragmentFactoryName, flags, diagnostics.Using_JSX_fragments_requires_fragment_factory_0_to_be_in_scope_but_it_could_not_be_found, true /*isUse*/, false /*excludeGlobals*/)
	}
	if jsxFactorySymbol == nil {
		links.jsxFragmentType = c.errorType
		return links.jsxFragmentType
	}
	if jsxFactorySymbol.Name == ReactNames.Fragment {
		links.jsxFragmentType = c.getTypeOfSymbol(jsxFactorySymbol)
		return links.jsxFragmentType
	}
	resolvedAlias := jsxFactorySymbol
	if jsxFactorySymbol.Flags&ast.SymbolFlagsAlias != 0 {
		resolvedAlias = c.resolveAlias(jsxFactorySymbol)
	}

	reactExports := c.getExportsOfSymbol(resolvedAlias)
	typeSymbol := c.getSymbol(reactExports, ReactNames.Fragment, ast.SymbolFlagsBlockScopedVariable)
	if typeSymbol != nil {
		links.jsxFragmentType = c.getTypeOfSymbol(typeSymbol)
	} else {
		links.jsxFragmentType = c.errorType
	}
	return links.jsxFragmentType
}

func (c *Checker) resolveJsxOpeningLikeElement(node *ast.Node, candidatesOutArray *[]*Signature, checkMode CheckMode) *Signature {
	isJsxOpenFragment := ast.IsJsxOpeningFragment(node)
	var exprTypes *Type
	if !isJsxOpenFragment {
		if isJsxIntrinsicTagName(node.TagName()) {
			result := c.getIntrinsicAttributesTypeFromJsxOpeningLikeElement(node)
			fakeSignature := c.createSignatureForJSXIntrinsic(node, result)
			c.checkTypeAssignableToAndOptionallyElaborate(c.checkExpressionWithContextualType(node.Attributes(), c.getEffectiveFirstArgumentForJsxSignature(fakeSignature, node), nil /*inferenceContext*/, CheckModeNormal), result, node.TagName(), node.Attributes(), nil, nil)
			typeArguments := node.TypeArguments()
			if len(typeArguments) != 0 {
				c.checkSourceElements(typeArguments)
				sourceFile := ast.GetSourceFileOfNode(node)
				typeArgumentList := node.TypeArgumentList()
				loc := core.NewTextRange(scanner.SkipTrivia(sourceFile.Text(), typeArgumentList.Loc.Pos()), typeArgumentList.Loc.End())
				c.addDiagnostic(ast.NewDiagnostic(sourceFile, loc, diagnostics.Expected_0_type_arguments_but_got_1, 0, len(typeArguments)))
			}
			return fakeSignature
		}
		exprTypes = c.checkExpression(node.TagName())
	} else {
		exprTypes = c.getJSXFragmentType(node)
	}
	apparentType := c.getApparentType(exprTypes)
	if c.isErrorType(apparentType) {
		return c.resolveErrorCall(node)
	}
	signatures := c.getUninstantiatedJsxSignaturesOfType(exprTypes, node)
	if c.isUntypedFunctionCall(exprTypes, apparentType, len(signatures), 0 /*constructSignatures*/) {
		return c.resolveUntypedCall(node)
	}
	if len(signatures) == 0 {
		// We found no signatures at all, which is an error
		if isJsxOpenFragment {
			c.error(node, diagnostics.JSX_element_type_0_does_not_have_any_construct_or_call_signatures, scanner.GetTextOfNode(node))
		} else {
			c.error(node.TagName(), diagnostics.JSX_element_type_0_does_not_have_any_construct_or_call_signatures, scanner.GetTextOfNode(node.TagName()))
		}
		return c.resolveErrorCall(node)
	}
	return c.resolveCall(node, signatures, candidatesOutArray, checkMode, SignatureFlagsNone, nil)
}

// Check if the given signature can possibly be a signature called by the JSX opening-like element.
// @param node a JSX opening-like element we are trying to figure its call signature
// @param signature a candidate signature we are trying whether it is a call signature
// @param relation a relationship to check parameter and argument type
func (c *Checker) checkApplicableSignatureForJsxCallLikeElement(node *ast.Node, signature *Signature, relation *Relation, checkMode CheckMode, reportErrors bool, diagnosticOutput *[]*ast.Diagnostic) bool {
	// Stateless function components can have maximum of three arguments: "props", "context", and "updater".
	// However "context" and "updater" are implicit and can't be specify by users. Only the first parameter, props,
	// can be specified by users through attributes property.
	paramType := c.getEffectiveFirstArgumentForJsxSignature(signature, node)
	var attributesType *Type
	if ast.IsJsxOpeningFragment(node) {
		attributesType = c.createJsxAttributesTypeFromAttributesProperty(node, CheckModeNormal)
	} else {
		attributesType = c.checkExpressionWithContextualType(node.Attributes(), paramType, nil /*inferenceContext*/, checkMode)
	}
	var checkAttributesType *Type
	checkTagNameDoesNotExpectTooManyArguments := func() bool {
		if c.getJsxNamespaceContainerForImplicitImport(node) != nil {
			return true // factory is implicitly jsx/jsxdev - assume it fits the bill, since we don't strongly look for the jsx/jsxs/jsxDEV factory APIs anywhere else (at least not yet)
		}
		// We assume fragments have the correct arity since the node does not have attributes
		var tagType *Type
		if (ast.IsJsxOpeningElement(node) || ast.IsJsxSelfClosingElement(node)) && !(isJsxIntrinsicTagName(node.TagName()) || ast.IsJsxNamespacedName(node.TagName())) {
			tagType = c.checkExpression(node.TagName())
		}
		if tagType == nil {
			return true
		}
		tagCallSignatures := c.getSignaturesOfType(tagType, SignatureKindCall)
		if len(tagCallSignatures) == 0 {
			return true
		}
		factory := c.getJsxFactoryEntity(node)
		if factory == nil {
			return true
		}
		factorySymbol := c.resolveEntityName(factory, ast.SymbolFlagsValue, true /*ignoreErrors*/, false /*dontResolveAlias*/, node)
		if factorySymbol == nil {
			return true
		}

		factoryType := c.getTypeOfSymbol(factorySymbol)
		callSignatures := c.getSignaturesOfType(factoryType, SignatureKindCall)
		if len(callSignatures) == 0 {
			return true
		}
		hasFirstParamSignatures := false
		maxParamCount := 0
		// Check that _some_ first parameter expects a FC-like thing, and that some overload of the SFC expects an acceptable number of arguments
		for _, sig := range callSignatures {
			firstparam := c.getTypeAtPosition(sig, 0)
			signaturesOfParam := c.getSignaturesOfType(firstparam, SignatureKindCall)
			if len(signaturesOfParam) == 0 {
				continue
			}
			for _, paramSig := range signaturesOfParam {
				hasFirstParamSignatures = true
				if c.hasEffectiveRestParameter(paramSig) {
					return true // some signature has a rest param, so function components can have an arbitrary number of arguments
				}
				paramCount := c.getParameterCount(paramSig)
				if paramCount > maxParamCount {
					maxParamCount = paramCount
				}
			}
		}
		if !hasFirstParamSignatures {
			// Not a single signature had a first parameter which expected a signature - for back compat, and
			// to guard against generic factories which won't have signatures directly, do not error
			return true
		}
		absoluteMinArgCount := math.MaxInt
		for _, tagSig := range tagCallSignatures {
			tagRequiredArgCount := c.getMinArgumentCount(tagSig)
			if tagRequiredArgCount < absoluteMinArgCount {
				absoluteMinArgCount = tagRequiredArgCount
			}
		}
		if absoluteMinArgCount <= maxParamCount {
			return true // some signature accepts the number of arguments the function component provides
		}
		if reportErrors {
			tagName := node.TagName()
			// We will not report errors in this function for fragments, since we do not check them in this function
			diag := NewDiagnosticForNode(tagName, diagnostics.Tag_0_expects_at_least_1_arguments_but_the_JSX_factory_2_provides_at_most_3, entityNameToString(tagName), absoluteMinArgCount, entityNameToString(factory), maxParamCount)
			tagNameSymbol := c.getSymbolAtLocation(tagName, false)
			if tagNameSymbol != nil && tagNameSymbol.ValueDeclaration != nil {
				diag.AddRelatedInfo(NewDiagnosticForNode(tagNameSymbol.ValueDeclaration, diagnostics.X_0_is_declared_here, entityNameToString(tagName)))
			}
			c.reportDiagnostic(diag, diagnosticOutput)
		}
		return false
	}
	if checkMode&CheckModeSkipContextSensitive != 0 {
		checkAttributesType = c.getRegularTypeOfObjectLiteral(attributesType)
	} else {
		checkAttributesType = attributesType
	}
	if !checkTagNameDoesNotExpectTooManyArguments() {
		return false
	}
	var errorNode *ast.Node
	if reportErrors {
		if ast.IsJsxOpeningFragment(node) {
			errorNode = node
		} else {
			errorNode = node.TagName()
		}
	}
	var attributes *ast.Node
	if !ast.IsJsxOpeningFragment(node) {
		attributes = node.Attributes()
	}
	return c.checkTypeRelatedToAndOptionallyElaborate(checkAttributesType, paramType, relation, errorNode, attributes, nil, diagnosticOutput)
}

// Get attributes type of the JSX opening-like element. The result is from resolving "attributes" property of the opening-like element.
//
// @param openingLikeElement a JSX opening-like element
// @param filter a function to remove attributes that will not participate in checking whether attributes are assignable
// @return an anonymous type (similar to the one returned by checkObjectLiteral) in which its properties are attributes property.
// @remarks Because this function calls getSpreadType, it needs to use the same checks as checkObjectLiteral,
// which also calls getSpreadType.
func (c *Checker) createJsxAttributesTypeFromAttributesProperty(openingLikeElement *ast.Node, checkMode CheckMode) *Type {
	var allAttributesTable ast.SymbolTable
	if c.strictNullChecks {
		allAttributesTable = make(ast.SymbolTable)
	}
	attributesTable := make(ast.SymbolTable)
	var attributesSymbol *ast.Symbol
	attributeParent := openingLikeElement
	spread := c.emptyJsxObjectType
	var hasSpreadAnyType bool
	var typeToIntersect *Type
	var explicitlySpecifyChildrenAttribute bool
	objectFlags := ObjectFlagsJsxAttributes
	createJsxAttributesType := func() *Type {
		objectFlags |= ObjectFlagsFreshLiteral
		result := c.newAnonymousType(attributesSymbol, attributesTable, nil, nil, nil)
		result.objectFlags |= objectFlags | ObjectFlagsObjectLiteral | ObjectFlagsContainsObjectOrArrayLiteral
		return result
	}
	jsxChildrenPropertyName := c.getJsxElementChildrenPropertyName(c.getJsxNamespaceAt(openingLikeElement))
	isJsxOpenFragment := ast.IsJsxOpeningFragment(openingLikeElement)
	if !isJsxOpenFragment {
		attributes := openingLikeElement.Attributes()
		attributesSymbol = attributes.Symbol()
		attributeParent = attributes
		contextualType := c.getContextualType(attributes, ContextFlagsNone)
		// Create anonymous type from given attributes symbol table.
		// @param symbol a symbol of JsxAttributes containing attributes corresponding to attributesTable
		// @param attributesTable a symbol table of attributes property
		for _, attributeDecl := range attributes.Properties() {
			member := attributeDecl.Symbol()
			if ast.IsJsxAttribute(attributeDecl) {
				exprType := c.checkJsxAttribute(attributeDecl, checkMode)
				objectFlags |= exprType.objectFlags & ObjectFlagsPropagatingFlags
				attributeSymbol := c.newSymbol(ast.SymbolFlagsProperty|member.Flags, member.Name)
				attributeSymbol.Declarations = member.Declarations
				attributeSymbol.Parent = member.Parent
				if member.ValueDeclaration != nil {
					attributeSymbol.ValueDeclaration = member.ValueDeclaration
				}
				links := c.valueSymbolLinks.Get(attributeSymbol)
				links.resolvedType = exprType
				links.target = member
				attributesTable[attributeSymbol.Name] = attributeSymbol
				if allAttributesTable != nil {
					allAttributesTable[attributeSymbol.Name] = attributeSymbol
				}
				if attributeDecl.Name().Text() == jsxChildrenPropertyName {
					explicitlySpecifyChildrenAttribute = true
				}
				if contextualType != nil {
					prop := c.getPropertyOfType(contextualType, member.Name)
					if prop != nil && prop.Declarations != nil && c.isDeprecatedSymbol(prop) && ast.IsIdentifier(attributeDecl.Name()) {
						c.addDeprecatedSuggestion(attributeDecl.Name(), prop.Declarations, attributeDecl.Name().Text())
					}
				}
				if contextualType != nil && checkMode&CheckModeInferential != 0 && checkMode&CheckModeSkipContextSensitive == 0 && c.isContextSensitive(attributeDecl) {
					inferenceContext := c.getInferenceContext(attributes)
					debug.Assert(inferenceContext != nil)
					// In CheckMode.Inferential we should always have an inference context
					inferenceNode := attributeDecl.Initializer().Expression()
					c.addIntraExpressionInferenceSite(inferenceContext, inferenceNode, exprType)
				}
			} else {
				debug.Assert(attributeDecl.Kind == ast.KindJsxSpreadAttribute)
				if len(attributesTable) != 0 {
					spread = c.getSpreadType(spread, createJsxAttributesType(), attributesSymbol, objectFlags, false /*readonly*/)
					attributesTable = make(ast.SymbolTable)
				}
				exprType := c.getReducedType(c.checkExpressionEx(attributeDecl.Expression(), checkMode&CheckModeInferential))
				if IsTypeAny(exprType) {
					hasSpreadAnyType = true
				}
				if c.isValidSpreadType(exprType) {
					spread = c.getSpreadType(spread, exprType, attributesSymbol, objectFlags, false /*readonly*/)
					if allAttributesTable != nil {
						c.checkSpreadPropOverrides(exprType, allAttributesTable, attributeDecl)
					}
				} else {
					c.error(attributeDecl.Expression(), diagnostics.Spread_types_may_only_be_created_from_object_types)
					if typeToIntersect != nil {
						typeToIntersect = c.getIntersectionType([]*Type{typeToIntersect, exprType})
					} else {
						typeToIntersect = exprType
					}
				}
			}
		}
		if !hasSpreadAnyType {
			if len(attributesTable) != 0 {
				spread = c.getSpreadType(spread, createJsxAttributesType(), attributesSymbol, objectFlags, false /*readonly*/)
			}
		}
	}
	parentHasSemanticJsxChildren := func(openingLikeElement *ast.Node) bool {
		// Handle children attribute
		parent := openingLikeElement.Parent
		if parent == nil {
			return false
		}
		var children []*ast.Node

		switch {
		case ast.IsJsxElement(parent):
			// We have to check that openingElement of the parent is the one we are visiting as this may not be true for selfClosingElement
			if parent.AsJsxElement().OpeningElement == openingLikeElement {
				children = parent.Children().Nodes
			}
		case ast.IsJsxFragment(parent):
			if parent.AsJsxFragment().OpeningFragment == openingLikeElement {
				children = parent.Children().Nodes
			}
		}
		return len(ast.GetSemanticJsxChildren(children)) != 0
	}
	if parentHasSemanticJsxChildren(openingLikeElement) {
		var childTypes []*Type = c.checkJsxChildren(openingLikeElement.Parent, checkMode)
		if !hasSpreadAnyType && jsxChildrenPropertyName != ast.InternalSymbolNameMissing && jsxChildrenPropertyName != "" {
			// Error if there is a attribute named "children" explicitly specified and children element.
			// This is because children element will overwrite the value from attributes.
			// Note: we will not warn "children" attribute overwritten if "children" attribute is specified in object spread.
			if explicitlySpecifyChildrenAttribute {
				c.error(attributeParent, diagnostics.X_0_are_specified_twice_The_attribute_named_0_will_be_overwritten, jsxChildrenPropertyName)
			}
			var childrenContextualType *Type
			if ast.IsJsxOpeningElement(openingLikeElement) {
				if contextualType := c.getApparentTypeOfContextualType(openingLikeElement.Attributes(), ContextFlagsNone); contextualType != nil {
					childrenContextualType = c.getTypeOfPropertyOfContextualType(contextualType, jsxChildrenPropertyName)
				}
			}
			// If there are children in the body of JSX element, create dummy attribute "children" with the union of children types so that it will pass the attribute checking process
			childrenPropSymbol := c.newSymbol(ast.SymbolFlagsProperty, jsxChildrenPropertyName)
			links := c.valueSymbolLinks.Get(childrenPropSymbol)
			switch {
			case len(childTypes) == 1:
				links.resolvedType = childTypes[0]
			case childrenContextualType != nil && someType(childrenContextualType, c.isTupleLikeType):
				links.resolvedType = c.createTupleType(childTypes)
			default:
				links.resolvedType = c.createArrayType(c.getUnionType(childTypes))
			}
			// Fake up a property declaration for the children
			childrenPropSymbol.ValueDeclaration = c.factory.NewPropertySignatureDeclaration(nil, c.factory.NewIdentifier(jsxChildrenPropertyName), nil /*postfixToken*/, nil /*type*/, nil /*initializer*/)
			childrenPropSymbol.ValueDeclaration.Parent = attributeParent
			childrenPropSymbol.ValueDeclaration.AsPropertySignatureDeclaration().Symbol = childrenPropSymbol
			childPropMap := make(ast.SymbolTable)
			childPropMap[jsxChildrenPropertyName] = childrenPropSymbol
			spread = c.getSpreadType(spread, c.newAnonymousType(attributesSymbol, childPropMap, nil, nil, nil), attributesSymbol, objectFlags|c.getPropagatingFlagsOfTypes(childTypes, TypeFlagsNone), false /*readonly*/)
		}
	}
	if hasSpreadAnyType {
		return c.anyType
	}
	if typeToIntersect != nil {
		if spread != c.emptyJsxObjectType {
			return c.getIntersectionType([]*Type{typeToIntersect, spread})
		}
		return typeToIntersect
	}
	if spread == c.emptyJsxObjectType {
		return createJsxAttributesType()
	}
	return spread
}

func (c *Checker) checkJsxAttribute(node *ast.Node, checkMode CheckMode) *Type {
	if node.Initializer() != nil {
		return c.checkExpressionForMutableLocation(node.Initializer(), checkMode)
	}
	// <Elem attr /> is sugar for <Elem attr={true} />
	return c.trueType
}

func (c *Checker) checkJsxChildren(node *ast.Node, checkMode CheckMode) []*Type {
	var childTypes []*Type
	for _, child := range node.Children().Nodes {
		// In React, JSX text that contains only whitespaces will be ignored so we don't want to type-check that
		// because then type of children property will have constituent of string type.
		if ast.IsJsxText(child) {
			if !child.AsJsxText().ContainsOnlyTriviaWhiteSpaces {
				childTypes = append(childTypes, c.stringType)
			}
		} else if ast.IsJsxExpression(child) && child.Expression() == nil {
			// empty jsx expressions don't *really* count as present children
			continue
		} else {
			childTypes = append(childTypes, c.checkExpressionForMutableLocation(child, checkMode))
		}
	}
	return childTypes
}

func (c *Checker) getUninstantiatedJsxSignaturesOfType(elementType *Type, caller *ast.Node) []*Signature {
	if elementType.flags&TypeFlagsString != 0 {
		return []*Signature{c.anySignature}
	}
	if elementType.flags&TypeFlagsStringLiteral != 0 {
		intrinsicType := c.getIntrinsicAttributesTypeFromStringLiteralType(elementType, caller)
		if intrinsicType == nil {
			c.error(caller, diagnostics.Property_0_does_not_exist_on_type_1, getStringLiteralValue(elementType), "JSX."+JsxNames.IntrinsicElements)
			return nil
		}
		fakeSignature := c.createSignatureForJSXIntrinsic(caller, intrinsicType)
		return []*Signature{fakeSignature}
	}
	apparentElemType := c.getApparentType(elementType)
	// Resolve the signatures, preferring constructor
	signatures := c.getSignaturesOfType(apparentElemType, SignatureKindConstruct)
	if len(signatures) == 0 {
		// No construct signatures, try call signatures
		signatures = c.getSignaturesOfType(apparentElemType, SignatureKindCall)
	}
	if len(signatures) == 0 && apparentElemType.flags&TypeFlagsUnion != 0 {
		// If each member has some combination of new/call signatures; make a union signature list for those
		signatures = c.getUnionSignatures(core.Map(apparentElemType.Types(), func(t *Type) []*Signature {
			return c.getUninstantiatedJsxSignaturesOfType(t, caller)
		}))
	}
	return signatures
}

func (c *Checker) getEffectiveFirstArgumentForJsxSignature(signature *Signature, node *ast.Node) *Type {
	if ast.IsJsxOpeningFragment(node) || c.getJsxReferenceKind(node) != JsxReferenceKindComponent {
		return c.getJsxPropsTypeFromCallSignature(signature, node)
	}
	return c.getJsxPropsTypeFromClassType(signature, node)
}

func (c *Checker) getJsxPropsTypeFromCallSignature(sig *Signature, context *ast.Node) *Type {
	propsType := c.getTypeOfFirstParameterOfSignatureWithFallback(sig, c.unknownType)
	propsType = c.getJsxManagedAttributesFromLocatedAttributes(context, c.getJsxNamespaceAt(context), propsType)
	intrinsicAttribs := c.getJsxType(JsxNames.IntrinsicAttributes, context)
	if !c.isErrorType(intrinsicAttribs) {
		propsType = c.intersectTypes(intrinsicAttribs, propsType)
	}
	return propsType
}

func (c *Checker) getJsxPropsTypeFromClassType(sig *Signature, context *ast.Node) *Type {
	ns := c.getJsxNamespaceAt(context)
	forcedLookupLocation := c.getJsxElementPropertiesName(ns)
	var attributesType *Type
	switch forcedLookupLocation {
	case ast.InternalSymbolNameMissing:
		attributesType = c.getTypeOfFirstParameterOfSignatureWithFallback(sig, c.unknownType)
	case "":
		attributesType = c.getReturnTypeOfSignature(sig)
	default:
		attributesType = c.getJsxPropsTypeForSignatureFromMember(sig, forcedLookupLocation)
		if attributesType == nil && len(context.Attributes().Properties()) != 0 {
			// There is no property named 'props' on this instance type
			c.error(context, diagnostics.JSX_element_class_does_not_support_attributes_because_it_does_not_have_a_0_property, forcedLookupLocation)
		}
	}
	if attributesType == nil {
		return c.unknownType
	}
	attributesType = c.getJsxManagedAttributesFromLocatedAttributes(context, ns, attributesType)
	if IsTypeAny(attributesType) {
		// Props is of type 'any' or unknown
		return attributesType
	}
	// Normal case -- add in IntrinsicClassAttributes<T> and IntrinsicAttributes
	apparentAttributesType := attributesType
	intrinsicClassAttribs := c.getJsxType(JsxNames.IntrinsicClassAttributes, context)
	if !c.isErrorType(intrinsicClassAttribs) {
		typeParams := c.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(intrinsicClassAttribs.symbol)
		hostClassType := c.getReturnTypeOfSignature(sig)
		var libraryManagedAttributeType *Type
		if typeParams != nil {
			// apply JSX.IntrinsicClassAttributes<hostClassType, ...>
			inferredArgs := c.fillMissingTypeArguments([]*Type{hostClassType}, typeParams, c.getMinTypeArgumentCount(typeParams), ast.IsInJSFile(context))
			libraryManagedAttributeType = c.instantiateType(intrinsicClassAttribs, newTypeMapper(typeParams, inferredArgs))
		} else {
			libraryManagedAttributeType = intrinsicClassAttribs
		}
		apparentAttributesType = c.intersectTypes(libraryManagedAttributeType, apparentAttributesType)
	}
	intrinsicAttribs := c.getJsxType(JsxNames.IntrinsicAttributes, context)
	if !c.isErrorType(intrinsicAttribs) {
		apparentAttributesType = c.intersectTypes(intrinsicAttribs, apparentAttributesType)
	}
	return apparentAttributesType
}

func (c *Checker) getJsxPropsTypeForSignatureFromMember(sig *Signature, forcedLookupLocation string) *Type {
	if sig.composite != nil {
		// JSX Elements using the legacy `props`-field based lookup (eg, react class components) need to treat the `props` member as an input
		// instead of an output position when resolving the signature. We need to go back to the input signatures of the composite signature,
		// get the type of `props` on each return type individually, and then _intersect them_, rather than union them (as would normally occur
		// for a union signature). It's an unfortunate quirk of looking in the output of the signature for the type we want to use for the input.
		// The default behavior of `getTypeOfFirstParameterOfSignatureWithFallback` when no `props` member name is defined is much more sane.
		var results []*Type
		for _, signature := range sig.composite.signatures {
			instance := c.getReturnTypeOfSignature(signature)
			if IsTypeAny(instance) {
				return instance
			}
			propType := c.getTypeOfPropertyOfType(instance, forcedLookupLocation)
			if propType == nil {
				return nil
			}
			results = append(results, propType)
		}
		return c.getIntersectionType(results)
		// Same result for both union and intersection signatures
	}
	instanceType := c.getReturnTypeOfSignature(sig)
	if IsTypeAny(instanceType) {
		return instanceType
	}
	return c.getTypeOfPropertyOfType(instanceType, forcedLookupLocation)
}

func (c *Checker) getJsxManagedAttributesFromLocatedAttributes(context *ast.Node, ns *ast.Symbol, attributesType *Type) *Type {
	managedSym := c.getJsxLibraryManagedAttributes(ns)
	if managedSym != nil {
		ctorType := c.getStaticTypeOfReferencedJsxConstructor(context)
		result := c.instantiateAliasOrInterfaceWithDefaults(managedSym, []*Type{ctorType, attributesType}, ast.IsInJSFile(context))
		if result != nil {
			return result
		}
	}
	return attributesType
}

func (c *Checker) instantiateAliasOrInterfaceWithDefaults(managedSym *ast.Symbol, typeArguments []*Type, inJavaScript bool) *Type {
	declaredManagedType := c.getDeclaredTypeOfSymbol(managedSym)
	// fetches interface type, or initializes symbol links type parameters
	if managedSym.Flags&ast.SymbolFlagsTypeAlias != 0 {
		params := c.typeAliasLinks.Get(managedSym).typeParameters
		if len(params) >= len(typeArguments) {
			args := c.fillMissingTypeArguments(typeArguments, params, len(typeArguments), inJavaScript)
			if len(args) == 0 {
				return declaredManagedType
			}
			return c.getTypeAliasInstantiation(managedSym, args, nil)
		}
	}
	if declaredManagedType.objectFlags&ObjectFlagsClassOrInterface != 0 && len(declaredManagedType.AsInterfaceType().TypeParameters()) >= len(typeArguments) {
		args := c.fillMissingTypeArguments(typeArguments, declaredManagedType.AsInterfaceType().TypeParameters(), len(typeArguments), inJavaScript)
		return c.createTypeReference(declaredManagedType, args)
	}
	return nil
}

func (c *Checker) getJsxLibraryManagedAttributes(jsxNamespace *ast.Symbol) *ast.Symbol {
	if jsxNamespace != nil {
		return c.getSymbol(jsxNamespace.Exports, JsxNames.LibraryManagedAttributes, ast.SymbolFlagsType)
	}
	return nil
}

func (c *Checker) getJsxElementTypeSymbol(jsxNamespace *ast.Symbol) *ast.Symbol {
	// JSX.ElementType [symbol]
	if jsxNamespace != nil {
		return c.getSymbol(jsxNamespace.Exports, JsxNames.ElementType, ast.SymbolFlagsType)
	}
	return nil
}

// e.g. "props" for React.d.ts,
// or InternalSymbolNameMissing if ElementAttributesProperty doesn't exist (which means all
//
//	non-intrinsic elements' attributes type is 'any'),
//
// or "" if it has 0 properties (which means every
//
//	non-intrinsic elements' attributes type is the element instance type)
func (c *Checker) getJsxElementPropertiesName(jsxNamespace *ast.Symbol) string {
	return c.getNameFromJsxElementAttributesContainer(JsxNames.ElementAttributesPropertyNameContainer, jsxNamespace)
}

func (c *Checker) getJsxElementChildrenPropertyName(jsxNamespace *ast.Symbol) string {
	if c.compilerOptions.Jsx == core.JsxEmitReactJSX || c.compilerOptions.Jsx == core.JsxEmitReactJSXDev {
		// In these JsxEmit modes the children property is fixed to 'children'
		return "children"
	}
	return c.getNameFromJsxElementAttributesContainer(JsxNames.ElementChildrenAttributeNameContainer, jsxNamespace)
}

// Look into JSX namespace and then look for container with matching name as nameOfAttribPropContainer.
// Get a single property from that container if existed. Report an error if there are more than one property.
//
// @param nameOfAttribPropContainer a string of value JsxNames.ElementAttributesPropertyNameContainer or JsxNames.ElementChildrenAttributeNameContainer
//
//	if other string is given or the container doesn't exist, return undefined.
func (c *Checker) getNameFromJsxElementAttributesContainer(nameOfAttribPropContainer string, jsxNamespace *ast.Symbol) string {
	// JSX.ElementAttributesProperty | JSX.ElementChildrenAttribute [symbol]
	if jsxNamespace != nil {
		jsxElementAttribPropInterfaceSym := c.getSymbol(jsxNamespace.Exports, nameOfAttribPropContainer, ast.SymbolFlagsType)
		if jsxElementAttribPropInterfaceSym != nil {
			jsxElementAttribPropInterfaceType := c.getDeclaredTypeOfSymbol(jsxElementAttribPropInterfaceSym)
			propertiesOfJsxElementAttribPropInterface := c.getPropertiesOfType(jsxElementAttribPropInterfaceType)
			// Element Attributes has zero properties, so the element attributes type will be the class instance type
			if len(propertiesOfJsxElementAttribPropInterface) == 0 {
				return ""
			}
			if len(propertiesOfJsxElementAttribPropInterface) == 1 {
				return propertiesOfJsxElementAttribPropInterface[0].Name
			}
			if len(propertiesOfJsxElementAttribPropInterface) > 1 && len(jsxElementAttribPropInterfaceSym.Declarations) != 0 {
				// More than one property on ElementAttributesProperty is an error
				c.error(jsxElementAttribPropInterfaceSym.Declarations[0], diagnostics.The_global_type_JSX_0_may_not_have_more_than_one_property, nameOfAttribPropContainer)
			}
		}
	}
	return ast.InternalSymbolNameMissing
}

func (c *Checker) getStaticTypeOfReferencedJsxConstructor(context *ast.Node) *Type {
	if ast.IsJsxOpeningFragment(context) {
		return c.getJSXFragmentType(context)
	}
	if isJsxIntrinsicTagName(context.TagName()) {
		result := c.getIntrinsicAttributesTypeFromJsxOpeningLikeElement(context)
		fakeSignature := c.createSignatureForJSXIntrinsic(context, result)
		return c.getOrCreateTypeFromSignature(fakeSignature)
	}
	tagType := c.checkExpressionCached(context.TagName())
	if tagType.flags&TypeFlagsStringLiteral != 0 {
		result := c.getIntrinsicAttributesTypeFromStringLiteralType(tagType, context)
		if result == nil {
			return c.errorType
		}
		fakeSignature := c.createSignatureForJSXIntrinsic(context, result)
		return c.getOrCreateTypeFromSignature(fakeSignature)
	}
	return tagType
}

func (c *Checker) getIntrinsicAttributesTypeFromStringLiteralType(t *Type, location *ast.Node) *Type {
	// If the elemType is a stringLiteral type, we can then provide a check to make sure that the string literal type is one of the Jsx intrinsic element type
	// For example:
	//      var CustomTag: "h1" = "h1";
	//      <CustomTag> Hello World </CustomTag>
	intrinsicElementsType := c.getJsxType(JsxNames.IntrinsicElements, location)
	if !c.isErrorType(intrinsicElementsType) {
		stringLiteralTypeName := getStringLiteralValue(t)
		intrinsicProp := c.getPropertyOfType(intrinsicElementsType, stringLiteralTypeName)
		if intrinsicProp != nil {
			return c.getTypeOfSymbol(intrinsicProp)
		}
		indexSignatureType := c.getIndexTypeOfType(intrinsicElementsType, c.stringType)
		if indexSignatureType != nil {
			return indexSignatureType
		}
		return nil
	}
	// If we need to report an error, we already done so here. So just return any to prevent any more error downstream
	return c.anyType
}

func (c *Checker) getJsxReferenceKind(node *ast.Node) JsxReferenceKind {
	if isJsxIntrinsicTagName(node.TagName()) {
		return JsxReferenceKindMixed
	}
	tagType := c.getApparentType(c.checkExpression(node.TagName()))
	if len(c.getSignaturesOfType(tagType, SignatureKindConstruct)) != 0 {
		return JsxReferenceKindComponent
	}
	if len(c.getSignaturesOfType(tagType, SignatureKindCall)) != 0 {
		return JsxReferenceKindFunction
	}
	return JsxReferenceKindMixed
}

func (c *Checker) createSignatureForJSXIntrinsic(node *ast.Node, result *Type) *Signature {
	elementType := c.errorType
	if namespace := c.getJsxNamespaceAt(node); namespace != nil {
		if typeSymbol := c.getSymbol(c.getExportsOfSymbol(namespace), JsxNames.Element, ast.SymbolFlagsType); typeSymbol != nil {
			elementType = c.getDeclaredTypeOfSymbol(typeSymbol)
		}
	}
	// returnNode := typeSymbol && c.nodeBuilder.symbolToEntityName(typeSymbol, ast.SymbolFlagsType, node)
	// declaration := factory.createFunctionTypeNode(nil, []ParameterDeclaration{factory.createParameterDeclaration(nil, nil /*dotDotDotToken*/, "props", nil /*questionToken*/, c.nodeBuilder.typeToTypeNode(result, node))}, ifElse(returnNode != nil, factory.createTypeReferenceNode(returnNode, nil /*typeArguments*/), factory.createKeywordTypeNode(ast.KindAnyKeyword)))
	parameterSymbol := c.newSymbol(ast.SymbolFlagsFunctionScopedVariable, "props")
	c.valueSymbolLinks.Get(parameterSymbol).resolvedType = result
	return c.newSignature(SignatureFlagsNone, nil, nil, nil, []*ast.Symbol{parameterSymbol}, elementType, nil, 1)
}

// Get attributes type of the given intrinsic opening-like Jsx element by resolving the tag name.
// The function is intended to be called from a function which has checked that the opening element is an intrinsic element.
// @param node an intrinsic JSX opening-like element
func (c *Checker) getIntrinsicAttributesTypeFromJsxOpeningLikeElement(node *ast.Node) *Type {
	debug.Assert(isJsxIntrinsicTagName(node.TagName()))
	links := c.jsxElementLinks.Get(node)
	if links.resolvedJsxElementAttributesType != nil {
		return links.resolvedJsxElementAttributesType
	}
	symbol := c.getIntrinsicTagSymbol(node)
	if links.jsxFlags&JsxFlagsIntrinsicNamedElement != 0 {
		links.resolvedJsxElementAttributesType = core.OrElse(c.getTypeOfSymbol(symbol), c.errorType)
		return links.resolvedJsxElementAttributesType
	}
	if links.jsxFlags&JsxFlagsIntrinsicIndexedElement != 0 {
		indexInfo := c.getApplicableIndexInfoForName(c.getJsxType(JsxNames.IntrinsicElements, node), node.TagName().Text())
		if indexInfo != nil {
			links.resolvedJsxElementAttributesType = indexInfo.valueType
			return links.resolvedJsxElementAttributesType
		}
	}
	links.resolvedJsxElementAttributesType = c.errorType
	return links.resolvedJsxElementAttributesType
}

// Looks up an intrinsic tag name and returns a symbol that either points to an intrinsic
// property (in which case nodeLinks.jsxFlags will be IntrinsicNamedElement) or an intrinsic
// string index signature (in which case nodeLinks.jsxFlags will be IntrinsicIndexedElement).
// May also return unknownSymbol if both of these lookups fail.
func (c *Checker) getIntrinsicTagSymbol(node *ast.Node) *ast.Symbol {
	links := c.symbolNodeLinks.Get(node)
	if links.resolvedSymbol != nil {
		return links.resolvedSymbol
	}
	intrinsicElementsType := c.getJsxType(JsxNames.IntrinsicElements, node)
	if !c.isErrorType(intrinsicElementsType) {
		// Property case
		tagName := node.TagName()
		if !ast.IsIdentifier(tagName) && !ast.IsJsxNamespacedName(tagName) {
			panic("Invalid tag name")
		}
		propName := tagName.Text()
		intrinsicProp := c.getPropertyOfType(intrinsicElementsType, propName)
		if intrinsicProp != nil {
			c.jsxElementLinks.Get(node).jsxFlags |= JsxFlagsIntrinsicNamedElement
			links.resolvedSymbol = intrinsicProp
			return links.resolvedSymbol
		}
		// Intrinsic string indexer case
		indexSymbol := c.getApplicableIndexSymbol(intrinsicElementsType, c.getStringLiteralType(propName))
		if indexSymbol != nil {
			c.jsxElementLinks.Get(node).jsxFlags |= JsxFlagsIntrinsicIndexedElement
			links.resolvedSymbol = indexSymbol
			return links.resolvedSymbol
		}
		if c.getTypeOfPropertyOrIndexSignatureOfType(intrinsicElementsType, propName) != nil {
			c.jsxElementLinks.Get(node).jsxFlags |= JsxFlagsIntrinsicIndexedElement
			links.resolvedSymbol = intrinsicElementsType.symbol
			return links.resolvedSymbol
		}
		// Wasn't found
		c.error(node, diagnostics.Property_0_does_not_exist_on_type_1, tagName.Text(), "JSX."+JsxNames.IntrinsicElements)
		links.resolvedSymbol = c.unknownSymbol
		return links.resolvedSymbol
	}
	if c.noImplicitAny {
		c.error(node, diagnostics.JSX_element_implicitly_has_type_any_because_no_interface_JSX_0_exists, JsxNames.IntrinsicElements)
	}
	links.resolvedSymbol = c.unknownSymbol
	return links.resolvedSymbol
}

func (c *Checker) getJsxStatelessElementTypeAt(location *ast.Node) *Type {
	jsxElementType := c.getJsxElementTypeAt(location)
	if jsxElementType == nil {
		return nil
	}
	return c.getUnionType([]*Type{jsxElementType, c.nullType})
}

func (c *Checker) getJsxElementClassTypeAt(location *ast.Node) *Type {
	t := c.getJsxType(JsxNames.ElementClass, location)
	if c.isErrorType(t) {
		return nil
	}
	return t
}

func (c *Checker) getJsxElementTypeAt(location *ast.Node) *Type {
	return c.getJsxType(JsxNames.Element, location)
}

func (c *Checker) getJsxElementTypeTypeAt(location *ast.Node) *Type {
	ns := c.getJsxNamespaceAt(location)
	if ns == nil {
		return nil
	}
	sym := c.getJsxElementTypeSymbol(ns)
	if sym == nil {
		return nil
	}
	t := c.instantiateAliasOrInterfaceWithDefaults(sym, nil, ast.IsInJSFile(location))
	if t == nil || c.isErrorType(t) {
		return nil
	}
	return t
}

func (c *Checker) getJsxType(name string, location *ast.Node) *Type {
	if namespace := c.getJsxNamespaceAt(location); namespace != nil {
		if exports := c.getExportsOfSymbol(namespace); exports != nil {
			if typeSymbol := c.getSymbol(exports, name, ast.SymbolFlagsType); typeSymbol != nil {
				return c.getDeclaredTypeOfSymbol(typeSymbol)
			}
		}
	}
	return c.errorType
}

func (c *Checker) getJsxNamespaceAt(location *ast.Node) *ast.Symbol {
	var links *JsxElementLinks
	if location != nil {
		links = c.jsxElementLinks.Get(location)
	}
	if links != nil && links.jsxNamespace != nil && links.jsxNamespace != c.unknownSymbol {
		return links.jsxNamespace
	}
	if links == nil || links.jsxNamespace != c.unknownSymbol {
		resolvedNamespace := c.getJsxNamespaceContainerForImplicitImport(location)
		if resolvedNamespace == nil || resolvedNamespace == c.unknownSymbol {
			namespaceName := c.getJsxNamespace(location)
			resolvedNamespace = c.resolveName(location, namespaceName, ast.SymbolFlagsNamespace, nil /*nameNotFoundMessage*/, false /*isUse*/, false /*excludeGlobals*/)
		}
		if resolvedNamespace != nil {
			candidate := c.resolveSymbol(c.getSymbol(c.getExportsOfSymbol(c.resolveSymbol(resolvedNamespace)), JsxNames.JSX, ast.SymbolFlagsNamespace))
			if candidate != nil && candidate != c.unknownSymbol {
				if links != nil {
					links.jsxNamespace = candidate
				}
				return candidate
			}
		}
		if links != nil {
			links.jsxNamespace = c.unknownSymbol
		}
	}
	// JSX global fallback
	s := c.resolveSymbol(c.getGlobalSymbol(JsxNames.JSX, ast.SymbolFlagsNamespace, nil /*diagnostic*/))
	if s == c.unknownSymbol {
		return nil
	}
	return s
}

func (c *Checker) getJsxNamespace(location *ast.Node) string {
	if location != nil {
		file := ast.GetSourceFileOfNode(location)
		if file != nil {
			links := c.sourceFileLinks.Get(file)
			if ast.IsJsxOpeningFragment(location) {
				if links.localJsxFragmentNamespace != "" {
					return links.localJsxFragmentNamespace
				}
				jsxFragmentPragma := ast.GetPragmaFromSourceFile(file, "jsxfrag")
				if jsxFragmentPragma != nil {
					links.localJsxFragmentFactory = c.parseIsolatedEntityName(jsxFragmentPragma.Args["factory"].Value)
					if links.localJsxFragmentFactory != nil {
						links.localJsxFragmentNamespace = ast.GetFirstIdentifier(links.localJsxFragmentFactory).Text()
						return links.localJsxFragmentNamespace
					}
				}
				entity := c.getJsxFragmentFactoryEntity(location)
				if entity != nil {
					links.localJsxFragmentFactory = entity
					links.localJsxFragmentNamespace = ast.GetFirstIdentifier(entity).Text()
					return links.localJsxFragmentNamespace
				}
			} else {
				localJsxNamespace := c.getLocalJsxNamespace(file)
				if localJsxNamespace != "" {
					links.localJsxNamespace = localJsxNamespace
					return links.localJsxNamespace
				}
			}
		}
	}
	if c._jsxNamespace == "" {
		c._jsxNamespace = "React"
		if c.compilerOptions.JsxFactory != "" {
			c._jsxFactoryEntity = c.parseIsolatedEntityName(c.compilerOptions.JsxFactory)
			if c._jsxFactoryEntity != nil {
				c._jsxNamespace = ast.GetFirstIdentifier(c._jsxFactoryEntity).Text()
			}
		} else if c.compilerOptions.ReactNamespace != "" {
			c._jsxNamespace = c.compilerOptions.ReactNamespace
		}
	}
	if c._jsxFactoryEntity == nil {
		c._jsxFactoryEntity = c.factory.NewQualifiedName(c.factory.NewIdentifier(c._jsxNamespace), c.factory.NewIdentifier("createElement"))
	}
	return c._jsxNamespace
}

func (c *Checker) getLocalJsxNamespace(file *ast.SourceFile) string {
	links := c.sourceFileLinks.Get(file)
	if links.localJsxNamespace != "" {
		return links.localJsxNamespace
	}
	jsxPragma := ast.GetPragmaFromSourceFile(file, "jsx")
	if jsxPragma != nil {
		links.localJsxFactory = c.parseIsolatedEntityName(jsxPragma.Args["factory"].Value)
		if links.localJsxFactory != nil {
			links.localJsxNamespace = ast.GetFirstIdentifier(links.localJsxFactory).Text()
			return links.localJsxNamespace
		}
	}
	return ""
}

func (c *Checker) getJsxFactoryEntity(location *ast.Node) *ast.Node {
	if location != nil {
		c.getJsxNamespace(location)
		if localJsxFactory := c.sourceFileLinks.Get(ast.GetSourceFileOfNode(location)).localJsxFactory; localJsxFactory != nil {
			return localJsxFactory
		}
	}
	return c._jsxFactoryEntity
}

func (c *Checker) getJsxFragmentFactoryEntity(location *ast.Node) *ast.EntityName {
	if location != nil {
		file := ast.GetSourceFileOfNode(location)
		if file != nil {
			links := c.sourceFileLinks.Get(file)
			if links.localJsxFragmentFactory != nil {
				return links.localJsxFragmentFactory
			}
			jsxFragPragma := ast.GetPragmaFromSourceFile(file, "jsxfrag")
			if jsxFragPragma != nil {
				links.localJsxFragmentFactory = c.parseIsolatedEntityName(jsxFragPragma.Args["factory"].Value)
				return links.localJsxFragmentFactory
			}
		}
	}
	if c.compilerOptions.JsxFragmentFactory != "" {
		return c.parseIsolatedEntityName(c.compilerOptions.JsxFragmentFactory)
	}
	return nil
}

func (c *Checker) parseIsolatedEntityName(name string) *ast.Node {
	result := parser.ParseIsolatedEntityName(name)
	if result != nil {
		markAsSynthetic(result)
	}
	return result
}

func markAsSynthetic(node *ast.Node) bool {
	node.Loc = core.NewTextRange(-1, -1)
	node.ForEachChild(markAsSynthetic)
	return false
}

func (c *Checker) getJsxNamespaceContainerForImplicitImport(location *ast.Node) *ast.Symbol {
	var file *ast.SourceFile
	var links *JsxElementLinks
	if location != nil {
		if file = ast.GetSourceFileOfNode(location); file != nil {
			links = c.jsxElementLinks.Get(file.AsNode())
		}
	}
	if links != nil && links.jsxImplicitImportContainer != nil {
		return core.IfElse(links.jsxImplicitImportContainer == c.unknownSymbol, nil, links.jsxImplicitImportContainer)
	}
	moduleReference, specifier := c.getJSXRuntimeImportSpecifier(file)
	if moduleReference == "" {
		return nil
	}
	errorMessage := diagnostics.This_JSX_tag_requires_the_module_path_0_to_exist_but_none_could_be_found_Make_sure_you_have_types_for_the_appropriate_package_installed
	mod := c.resolveExternalModule(core.OrElse(specifier, location), moduleReference, errorMessage, location, false)
	var result *ast.Symbol
	if mod != nil && mod != c.unknownSymbol {
		result = c.getMergedSymbol(c.resolveSymbol(mod))
	}
	if links != nil {
		links.jsxImplicitImportContainer = core.OrElse(result, c.unknownSymbol)
	}
	return result
}

func (c *Checker) getJSXRuntimeImportSpecifier(file *ast.SourceFile) (moduleReference string, specifier *ast.Node) {
	return c.program.GetJSXRuntimeImportSpecifier(file.Path())
}
