blob: 9ae2465e1a105b52a25b9dda606dae3efb8155d9 [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 {
if _, isLib := a.libs[libName]; isLib {
return Type{
IsLib: true,
}, nil
}
}
// TODO: if we want to support hovering over members (e.g. struct fields,
// method parameters, bits members) or protocol methods, we need to check
// here whether the symbol is namespaced -- whether it is inside a
// declaration.
// If it is, prepend it with that declaration's name. For example, this
// struct field:
//
// struct Foo {
// MyType my_field;
// ~~~~~~~~
// }
//
// Would become "library.name/Foo.my_field", as this is how it's stored in
// the symbol map.
// 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,
)
}
symInfo, err := a.lookupSymbolInfo(name)
if err != nil {
return Type{}, fmt.Errorf("could not find type of symbol `%s`: %s", 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.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.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
}