diff --git a/internal/cmds/lint/error.go b/internal/cmds/lint/error.go index 9768f483..bfca8c81 100644 --- a/internal/cmds/lint/error.go +++ b/internal/cmds/lint/error.go @@ -84,6 +84,14 @@ func (e errUsageExceedsLimit) Error() string { e.fset.Position(e.node.Pos()), identName(e.node), e.limit, e.text) } +type errUnusedModule struct { + file string +} + +func (e errUnusedModule) Error() string { + return fmt.Sprintf("%s: error: unused module", e.file) +} + func isSilent(n ast.Node, checks ...string) bool { scanner := bufio.NewScanner(strings.NewReader(ast.FirstToken(n).Comments())) for scanner.Scan() { diff --git a/internal/cmds/lint/main.go b/internal/cmds/lint/main.go index 0041ca6d..9761f2e3 100644 --- a/internal/cmds/lint/main.go +++ b/internal/cmds/lint/main.go @@ -2,6 +2,7 @@ package lint import ( "fmt" + "path/filepath" "regexp" "strings" "sync" @@ -72,6 +73,12 @@ When TTCN-3 code is refactored incrementally, it happens that references to legacy code are faster added than one can remove them. This check helps with a warning, as soon as the usage of a symbol exceed a defined limit. + +Unused Symbols + + unused.modules Checks for unused modules + + Example configuration file: aligned_braces: true @@ -83,6 +90,9 @@ Example configuration file: limit: 12 text: Use "bar" instead. + unused: + modules: true + complexity: max: 15 ignore_guards: true @@ -122,6 +132,9 @@ For information on writing new checks, see . regexes = make(map[string]*regexp.Regexp) issues = 0 + usedModules = make(map[string]Import) + usedModuleMu sync.Mutex + style = struct { MaxLines int `yaml:"max_lines"` AlignedBraces bool `yaml:"aligned_braces"` @@ -157,9 +170,20 @@ For information on writing new checks, see . Limit int count int } + Unused struct { + Modules bool + } }{} ) +type Import struct { + Fset *loc.FileSet + Node *ast.ImportDecl + Path string + From string + Imported string +} + func init() { Command.PersistentFlags().StringVarP(&config, "config", "c", "", "path to YAML formatted file containing linter configuration") } @@ -229,6 +253,7 @@ func lint(cmd *cobra.Command, args []string) error { switch n := n.(type) { case *ast.Ident: checkUsage(fset, n) + case *ast.Module: checkNaming(fset, n, style.Naming.Modules) checkBraces(fset, n.LBrace, n.RBrace) @@ -322,6 +347,7 @@ func lint(cmd *cobra.Command, args []string) error { checkBraces(fset, n.LBrace, n.RBrace) case *ast.ImportDecl: checkBraces(fset, n.LBrace, n.RBrace) + checkImport(fset, n, mod.Module) case *ast.GroupDecl: checkBraces(fset, n.LBrace, n.RBrace) case *ast.WithSpec: @@ -378,6 +404,9 @@ func lint(cmd *cobra.Command, args []string) error { } wg.Wait() + + checkSuite(suite) + switch issues { case 0: return nil @@ -498,6 +527,54 @@ func checkUsage(fset *loc.FileSet, n *ast.Ident) { } } +func checkImport(fset *loc.FileSet, n *ast.ImportDecl, mod *ast.Module) { + if !style.Unused.Modules { + return + } + + imported := identName(n.Module) + importing := identName(mod.Name) + + usedModuleMu.Lock() + usedModules[imported] = Import{ + Node: n, + Fset: fset, + Path: fset.Position(n.Pos()).Filename, + From: importing, + Imported: imported, + } + usedModuleMu.Unlock() + +} + +func checkSuite(suite *ntt.Suite) { + + if !style.Unused.Modules { + return + } + + pkgs, _ := suite.Imports() + for _, pkg := range pkgs { + files, _ := filepath.Glob(pkg.Path() + "/*.ttcn3") + for _, file := range files { + if isWhiteListed(style.Ignore.Files, file) { + continue + } + + mod := filepath.Base(file) + mod = strings.TrimSuffix(mod, filepath.Ext(mod)) + + if isWhiteListed(style.Ignore.Modules, mod) { + return + } + + if _, found := usedModules[mod]; !found { + report(&errUnusedModule{file: file}) + } + } + } +} + func matchAny(patterns []string, s string) bool { for _, p := range patterns {