blob: 041c4ecf70550b3f7c67a3e3058bc59e5b43000f [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package analysis
import (
"fmt"
fidlcommon "fidl-lsp/third_party/common"
"fidl-lsp/state"
)
// TypeOfSymbol returns the type information of `sym`.
//
// analysis.Type contains all the information needed to create a human-readable
// description of the type.
func (a *Analyzer) TypeOfSymbol(fs *state.FileSystem, sym state.Symbol) (Type, error) {
// If `sym` is a library name, return a `library`-kinded TypeInfo.
libName, err := fidlcommon.ReadLibraryName(sym.Name)
if err == nil {
// TODO: We could use getLibraryWithFile here, but it would be more
// complicated: if this library name is a declaration, we should pass it
// the file the symbol is in; but if it's a library import, we should
// pass it a file of that imported library.
if _, isLib := a.getLibrary(libName); isLib {
return Type{
IsLib: true,
}, nil
}
}
// Otherwise, we assume it is a local or fully-qualified name
name, err := a.symbolToFullyQualifiedName(fs, sym)
if err != nil {
return Type{}, fmt.Errorf(
"could not convert symbol `%s` to fully-qualified name: %s",
sym.Name,
err,
)
}
var symInfo *symbolInfo
if name.lib != nil {
symInfo, err = a.lookupSymbolInfoInLibrary(name.name, name.lib)
if err != nil {
return Type{}, fmt.Errorf(
"could not find type of symbol `%s`: %s",
name.name.FullyQualifiedName(),
err,
)
}
} else {
symInfo, err = a.lookupSymbolInfo(name.name)
if err != nil {
return Type{}, fmt.Errorf(
"could not find type of symbol `%s`: %s",
name.name.FullyQualifiedName(),
err,
)
}
}
// Resolve identifier type, if necessary.
if symInfo.typeInfo.Kind == IdentifierType && !symInfo.typeInfo.Identifier.IsDecl {
// This means that rather than being a declaration, symInfo is a value
// of an identifier type, so we lookup that type's info based on the
// type name.
typeName, err := fidlcommon.ReadName(symInfo.typeInfo.Identifier.Identifier)
if err != nil {
return Type{}, fmt.Errorf(
"invalid identifier type `%s`: %s",
symInfo.typeInfo.Identifier.Identifier,
err,
)
}
t, err := a.lookupSymbolInfo(typeName)
if err != nil {
return Type{}, fmt.Errorf(
"could not find identifier type `%s` of symbol `%s`: %s",
typeName.FullyQualifiedName(),
name.name.FullyQualifiedName(),
err,
)
}
identifierType := t.typeInfo
identifierType.Identifier.IsDecl = false
identifierType.Identifier.Nullable = symInfo.typeInfo.Identifier.Nullable
identifierType.Identifier.Identifier = symInfo.typeInfo.Identifier.Identifier
return identifierType, nil
}
// Resolve aliased identifier type, if necessary.
if symInfo.typeInfo.Kind == IdentifierType &&
symInfo.typeInfo.Identifier.Kind == TypeAliasKind &&
symInfo.typeInfo.Identifier.TypeAlias.Type.Kind == IdentifierType {
// This means that `sym` is a type alias to an identifier type, so we
// need to lookup that identifier type's info based on the type name.
aliasedType := symInfo.typeInfo.Identifier.TypeAlias.Type
typeName, err := fidlcommon.ReadName(aliasedType.Identifier.Identifier)
if err != nil {
return Type{}, fmt.Errorf(
"invalid identifier type `%s`: %s",
aliasedType.Identifier.Identifier,
err,
)
}
t, err := a.lookupSymbolInfo(typeName)
if err != nil {
return Type{}, fmt.Errorf(
"could not find identifier type `%s` of symbol `%s`: %s",
typeName.FullyQualifiedName(),
name.name.FullyQualifiedName(),
err,
)
}
identifierType := t.typeInfo
identifierType.Identifier.Nullable = aliasedType.Identifier.Nullable
identifierType.Identifier.Identifier = aliasedType.Identifier.Identifier
symInfo.typeInfo.Identifier.TypeAlias.Type = identifierType
}
return symInfo.typeInfo, nil
}