22// Use of this source code is governed by a BSD 3-Clause
33// license that can be found in the LICENSE file.
44
5+ use "std/conv"
56use "std/jule"
67use "std/jule/ast"
78use "std/jule/build"
@@ -289,11 +290,12 @@ fn buildImpl(mut decl: &ast::Impl): &Impl {
289290// Just builds symbols, not analyze metadata
290291// like struct's implemented traits.
291292struct symBuilder {
292- owner: &symBuilder
293- importer: Importer
294- errors: []log::Log
295- ast: &ast::AST
296- table: &SymTab
293+ owner: &symBuilder
294+ importPath: str // Import path, this symBuilder created for.
295+ importer: Importer
296+ errors: []log::Log
297+ ast: &ast::AST
298+ table: &SymTab
297299}
298300
299301impl symBuilder {
@@ -369,20 +371,46 @@ impl symBuilder {
369371 // Designed for non-std package paths.
370372 // Returns empty string if error occurs.
371373 fn checkAbsPath(mut &self, mut filepath: str, mut decl: &ast::Use): str {
372- modPath := self.importer.GetModPath()
374+ // If there is no module, no common directories.
375+ // For the local packages, the root package needs a module.
376+ mut modPath := self.importer.GetModPath()
373377 if len(modPath) == 0 {
374378 self.pushErr(decl.Path, log::ModuleNotFound)
375379 self.pushSuggestion(log::UseModInit)
376380 ret ""
377381 }
378382
383+ // Get module name.
384+ modName := getModNameFromPath(modPath)
385+
386+ // Get module name of the filepath.
387+ mut filepathModName := filepath
388+ i := strings::IndexByte(filepath, os::PathSeparator)
389+ if i > 0 {
390+ filepathModName = filepath[:i]
391+ }
392+
393+ // All import filepaths are must be start with module name.
394+ if modName != filepathModName {
395+ self.pushErr(decl.Path, "import path must use the root name of the module")
396+ self.pushSuggestion("module name of this package is: " + conv::Quote(modName))
397+ ret ""
398+ }
399+
400+ // To handle absolute package path correct,
401+ // select the previous directory of the module.
402+ // Because filepath is already starts with the module path.
403+ modPath = filepath::Dir(modPath)
404+
405+ // Join filepath with the absolute module path and
406+ // make sure we have an absolue path to the package.
379407 filepath = filepath::Join(modPath, filepath)
380408 filepath = filepath::Abs(filepath) else {
381409 self.pushErr(decl.Path, log::UseNotFound, decl.Path.Kind)
382410 ret ""
383411 }
384412
385- // Exist?
413+ // Check the absolute path is exist and a directory (package).
386414 info := os::Stat(filepath) else {
387415 self.pushErr(decl.Path, log::UseNotFound, decl.Path.Kind)
388416 ret ""
@@ -435,7 +463,7 @@ impl symBuilder {
435463 }
436464 std = parts[0] == "std"
437465 for _, part in parts {
438- if part == "" || jule::IsBlank(part) {
466+ if part == "" || jule::IsBlank(part) || strings::ContainsRune(part, '.') {
439467 self.pushErr(decl.Path, log::InvalidImportPath, decl.Path.Kind)
440468 ret false, ""
441469 }
@@ -502,63 +530,32 @@ impl symBuilder {
502530 ret false
503531 }
504532
505- fn getAsLinkPath(mut &self, mut path: str): str {
506- mut sb := strings::Builder{}
507- sb.Grow(len(path))
508- stdlib := build::PathStdlib()
509- if strings::HasPrefix(path, stdlib) {
510- path = path[len(stdlib):] // cut absolute path prefix
511- sb.WriteStr(`"std`)!
512- sb.WriteStr(strings::ReplaceAll(path, str(filepath::Separator), jule::ImportPathSep))!
513- sb.WriteByte('"')!
514- ret sb.Str()
515- }
516-
517- root := filepath::Abs(self.importer.GetModPath()) else { use "" }
518- path = path[len(root):]
519- if len(path) == 0 {
520- path = filepath::Base(root)
521- } else if path[0] == filepath::Separator {
522- path = path[1:]
523- }
524- sb.WriteByte('"')!
525- sb.WriteStr(strings::ReplaceAll(path, str(filepath::Separator), jule::ImportPathSep))!
526- sb.WriteByte('"')!
527- ret sb.Str()
528- }
529-
530533 fn pushCycleError(mut &self, sb: &symBuilder, path: str, mut &message: *strings::Builder) {
531534 const Padding = 7
532- refersTo := log::Logf(
533- log::RefersTo,
534- self.getAsLinkPath(sb.table.File.Dir()),
535- self.getAsLinkPath(path))
535+ refersTo := log::Logf(log::RefersTo,
536+ "\""+sb.importPath+"\"",
537+ "\""+path+"\"")
536538 message.WriteStr(strings::Repeat(" ", Padding))!
537539 message.WriteStr(refersTo)!
538540 message.WriteByte('\n')!
539541 }
540542
541543 fn pushCrossCycleError(mut &self, target: &symBuilder, imp: &ImportInfo, errorToken: &token::Token) {
542544 mut message := strings::Builder{}
543- message.Grow(1 << 5)
544- self.pushCycleError(self, imp.Path, &message)
545- mut owner := self.owner
546- mut old := self
547- for owner.owner != nil {
548- self.pushCycleError(old.owner, old.table.File.Dir(), &message)
549- if owner.owner == target {
550- self.pushCycleError(target, owner.table.File.Dir(), &message)
545+ self.pushCycleError(self, imp.LinkPath, &message)
546+ mut owner, mut old := self.owner, self
547+ for owner != nil; owner, old = owner.owner, owner {
548+ self.pushCycleError(old.owner, old.importPath, &message)
549+ if owner == target {
551550 break
552551 }
553- old = owner
554- owner = owner.owner
555552 }
556553 self.pushErr(errorToken, log::PkgIllegalCrossCycle, message.Str())
557554 }
558555
559556 fn checkImportCycles(mut &self, imp: &ImportInfo, decl: &ast::Use): bool {
560557 if imp.Path == self.table.File.Dir() {
561- self.pushErr(decl.Token, log::PkgIllegalCycleRefersItself, self.getAsLinkPath( imp.Path) )
558+ self.pushErr(decl.Token, log::PkgIllegalCycleRefersItself, imp.LinkPath )
562559 ret false
563560 }
564561 if self.owner == nil {
@@ -644,7 +641,7 @@ impl symBuilder {
644641
645642 for (_, mut ast) in asts {
646643 mut table := (&SymTab)(nil)
647- table, errors = buildSymbols(ast, self.importer, self)
644+ table, errors = buildSymbols(imp.LinkPath, ast, self.importer, self)
648645 // Break import if file has error(s).
649646 if len(errors) > 0 {
650647 self.errors = append(self.errors, errors...)
@@ -793,4 +790,22 @@ impl symBuilder {
793790// See developer reference (6).
794791fn isImplicitImport(imp: &ImportInfo): bool {
795792 ret imp.Decl.Token == nil
793+ }
794+
795+ // Returns module name by module path provided by importer.
796+ // Returns "." if module path is empty.
797+ fn getModName(importer: Importer): str {
798+ ret getModNameFromPath(importer.GetModPath())
799+ }
800+
801+ // Returns module name by module path.
802+ // Returns "." if module path is empty.
803+ fn getModNameFromPath(path: str): str {
804+ // This path is the absolute path, it should be.
805+ // So, the module name is the base name of the absolue module path.
806+ //
807+ // Example:
808+ // "foo" -> "foo"
809+ // "foo/bar/baz" -> "baz"
810+ ret filepath::Base(path)
796811}
0 commit comments