| // 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 |
| } |