From de4c8a7f448d2af8be86717c3e5bd5b850515e9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Fri, 31 Jul 2020 17:19:04 -0700 Subject: [PATCH] include available declarations in "not declared" error --- runtime/sema/check_import_declaration.go | 52 ++++++++++++++++++++---- runtime/sema/errors.go | 13 ++++++ 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/runtime/sema/check_import_declaration.go b/runtime/sema/check_import_declaration.go index 02d17c47e4..511279d143 100644 --- a/runtime/sema/check_import_declaration.go +++ b/runtime/sema/check_import_declaration.go @@ -100,19 +100,21 @@ func (checker *Checker) declareImportDeclaration(declaration *ast.ImportDeclarat // Attempt to import the requested value declarations + allValueElements := imp.AllValueElements() foundValues, invalidAccessedValues := checker.importElements( checker.valueActivations, declaration.Identifiers, - imp.AllValueElements(), + allValueElements, imp.IsImportableValue, ) // Attempt to import the requested type declarations + allTypeElements := imp.AllTypeElements() foundTypes, invalidAccessedTypes := checker.importElements( checker.typeActivations, declaration.Identifiers, - imp.AllTypeElements(), + allTypeElements, imp.IsImportableType, ) @@ -141,22 +143,55 @@ func (checker *Checker) declareImportDeclaration(declaration *ast.ImportDeclarat ) } + identifierCount := len(declaration.Identifiers) + // Determine which requested declarations could neither be found // in the value nor in the type declarations of the imported program. // For each missing import, report an error and declare both a value // with an invalid type and an invalid type to avoid spurious errors - // due to uses of the inaccessible value or type + // due to uses of the inaccessible value or type. + // + // Also show which declarations are available, to help with debugging. + + missing := make([]ast.Identifier, 0, identifierCount) - missing := make(map[ast.Identifier]bool, len(declaration.Identifiers)) for _, identifier := range declaration.Identifiers { if foundValues[identifier] || foundTypes[identifier] { continue } - missing[identifier] = true + missing = append(missing, identifier) } - checker.handleMissingImports(missing, location) + if len(missing) > 0 { + capacity := len(allValueElements) + len(allTypeElements) + available := make([]string, 0, capacity) + availableSet := make(map[string]struct{}, capacity) + + for identifier := range allValueElements { + if _, ok := availableSet[identifier]; ok { + continue + } + if !imp.IsImportableValue(identifier) { + continue + } + availableSet[identifier] = struct{}{} + available = append(available, identifier) + } + + for identifier := range allTypeElements { + if _, ok := availableSet[identifier]; ok { + continue + } + if !imp.IsImportableType(identifier) { + continue + } + availableSet[identifier] = struct{}{} + available = append(available, identifier) + } + + checker.handleMissingImports(missing, available, location) + } return nil } @@ -202,12 +237,13 @@ func (checker *Checker) EnsureLoaded(location ast.Location, loadProgram func() * return subChecker, checkerErr } -func (checker *Checker) handleMissingImports(missing map[ast.Identifier]bool, importLocation ast.Location) { - for identifier := range missing { +func (checker *Checker) handleMissingImports(missing []ast.Identifier, available []string, importLocation ast.Location) { + for _, identifier := range missing { checker.report( &NotExportedError{ Name: identifier.Identifier, ImportLocation: importLocation, + Available: available, Pos: identifier.Pos, }, ) diff --git a/runtime/sema/errors.go b/runtime/sema/errors.go index db2c2113c9..474a31e0e8 100644 --- a/runtime/sema/errors.go +++ b/runtime/sema/errors.go @@ -21,6 +21,7 @@ package sema import ( "fmt" "math/big" + "strings" "github.com/onflow/cadence/runtime/ast" "github.com/onflow/cadence/runtime/common" @@ -1024,6 +1025,7 @@ func (*RepeatedImportError) isSemanticError() {} type NotExportedError struct { Name string ImportLocation ast.Location + Available []string Pos ast.Position } @@ -1035,6 +1037,17 @@ func (e *NotExportedError) Error() string { ) } +func (e *NotExportedError) SecondaryError() string { + var builder strings.Builder + builder.WriteString("available exported declarations are:\n") + + for _, available := range e.Available { + builder.WriteString(fmt.Sprintf(" - `%s`\n", available)) + } + + return builder.String() +} + func (*NotExportedError) isSemanticError() {} func (e *NotExportedError) StartPosition() ast.Position {