blob: eefcf290c13ef11c6616a2c00cba5264bc67ec71 [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"
"strings"
fidlcommon "fidl-lsp/third_party/common"
"fidl-lsp/state"
)
type symbolInfo struct {
name string
definition state.Location
typeInfo Type
}
type Type struct {
// If IsLib is true, other fields are empty.
// TODO: is there a better way to represent this?
IsLib bool
// If IsMethod is true, only Attrs is set.
IsMethod bool
Attrs []Attribute
Kind TypeKind
Array *ArrayTypeInfo
Vector *VectorTypeInfo
String *StringTypeInfo
Handle *HandleTypeInfo
Request *RequestTypeInfo
Primitive *PrimitiveTypeInfo
Identifier *IdentifierTypeInfo
}
type ArrayTypeInfo struct {
ElementType Type
ElementCount uint
}
type VectorTypeInfo struct {
ElementType Type
ElementCount *uint
Nullable bool
}
type StringTypeInfo struct {
ElementCount *uint
Nullable bool
}
type HandleTypeInfo struct {
Subtype HandleSubtype
Rights uint
Nullable bool
}
type HandleSubtype string
const (
Handle HandleSubtype = "handle"
Bti = "bti"
Channel = "channel"
Clock = "clock"
DebugLog = "debuglog"
Event = "event"
Eventpair = "eventpair"
Exception = "exception"
Fifo = "fifo"
Guest = "guest"
Interrupt = "interrupt"
Iommu = "iommu"
Job = "job"
Pager = "pager"
PciDevice = "pcidevice"
Pmt = "pmt"
Port = "port"
Process = "process"
Profile = "profile"
Resource = "resource"
Socket = "socket"
Stream = "stream"
SuspendToken = "suspendtoken"
Thread = "thread"
Time = "timer"
Vcpu = "vcpu"
Vmar = "vmar"
Vmo = "vmo"
)
type RequestTypeInfo struct {
Subtype string
Nullable bool
}
type PrimitiveTypeInfo struct {
Subtype PrimitiveSubtype
}
type PrimitiveSubtype string
const (
Bool PrimitiveSubtype = "bool"
Int8 = "int8"
Int16 = "int16"
Int32 = "int32"
Int64 = "int64"
Uint8 = "uint8"
Uint16 = "uint16"
Uint32 = "uint32"
Uint64 = "uint64"
Float32 = "float32"
Float64 = "float64"
)
// IdentifierTypeInfo is not a perfect analog for `identifier-type` from the
// JSON IR, because it serves as the type for both declarations of identifier
// types and values of identifier types.
//
// For example, if you have declared a struct Foo, and a protocol method that
// takes a Foo, IdentifierTypeInfo will hold the same type information for each
// of the following symbols:
//
// struct Foo {};
// ~~~
// protocol P {
// Method(Foo foo);
// ~~~ ~~~
// };
//
// So it is both a kind of "declaration type" as well as an "identifier type".
// Iff `IsDecl` == true, it is a "declaration type"; otherwise, it is an
// "identifier type", and `Identifier` is the name of a declaration type that
// can be looked up in the symbol map.
type IdentifierTypeInfo struct {
// If IsDecl is true, all the type information is contained in the tagged
// type info objects -- Kind and one of {Bits, Const, etc.}.
// If IsDecl is false, Identifier and Nullable are set, and Identifier is
// a key to the declaration of the identifier type, in the symbolMap.
IsDecl bool
Identifier string
Nullable bool
Kind IdentifierKind
Name string
Bits *BitsTypeInfo
Const *ConstTypeInfo
Enum *EnumTypeInfo
TypeAlias *TypeAliasTypeInfo
}
type IdentifierKind string
const (
BitsKind IdentifierKind = "bits"
ConstKind = "const"
EnumKind = "enum"
ProtocolKind = "protocol"
ServiceKind = "service"
StructKind = "struct"
TableKind = "table"
UnionKind = "union"
TypeAliasKind = "typeAlias"
)
type BitsTypeInfo struct {
Type Type
}
type ConstTypeInfo struct {
Type Type
Value string
}
type EnumTypeInfo struct {
Type PrimitiveSubtype
}
type TypeAliasTypeInfo struct {
Type Type
}
func (a *Analyzer) symbolToFullyQualifiedName(fs *state.FileSystem, sym state.Symbol) (fidlcommon.Name, error) {
// If `sym` is a local name (not fully-qualified), we create a FQN by
// attaching its library name.
var fqn string
if !strings.Contains(sym.Name, ".") {
file, err := fs.File(sym.Location.FileID)
if err != nil {
return fidlcommon.Name{}, fmt.Errorf("could not open file `%s`", sym.Location.FileID)
}
libName, err := state.LibraryOfFile(file)
if err != nil {
return fidlcommon.Name{}, fmt.Errorf(
"could not find library of symbol `%s` in file `%s`",
sym.Name,
sym.Location.FileID,
)
}
fqn = libName.FullyQualifiedName() + "/" + sym.Name
} else {
// If the symbol contains '.', we assume it is a fully-qualified name.
i := strings.LastIndex(sym.Name, ".")
fqn = sym.Name[:i] + "/" + sym.Name[i+1:]
}
// Convert `fqn` to a fidlcommon.Name.
name, err := fidlcommon.ReadName(fqn)
if err != nil {
return fidlcommon.Name{}, fmt.Errorf("could not read fully-qualified name `%s`: %s", fqn, err)
}
return name, nil
}
func (a *Analyzer) lookupSymbolInfo(name fidlcommon.Name) (*symbolInfo, error) {
lib, ok := a.libs[name.LibraryName()]
if !ok {
return nil, fmt.Errorf("unknown library `%s`", name.LibraryName().FullyQualifiedName())
}
if lib.ir == nil {
if err := a.importLibrary(lib.json); err != nil {
return nil, fmt.Errorf(
"error importing library `%s`: %s",
name.LibraryName().FullyQualifiedName(),
err,
)
}
}
// Check that we have a symbolMap for this library, and look up this
// symbol's definition location in that symbolMap.
if lib.symbols == nil {
return nil, fmt.Errorf(
"no symbol map for library `%s`",
name.LibraryName().FullyQualifiedName(),
)
}
symInfo, ok := lib.symbols[name.FullyQualifiedName()]
if !ok {
return nil, fmt.Errorf("could not find symbol `%s`", name.FullyQualifiedName())
}
return symInfo, nil
}