From b96a461e99fc855723e3c986bf7834caeaae995f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Rochala?= <48657087+rochala@users.noreply.github.com> Date: Thu, 22 May 2025 11:44:18 +0200 Subject: [PATCH 1/2] Fix completion mode filtering + optimize scopeCompletions (#23172) I've changed scopeCompletions to lazy val to memoize because I beliveve we don't need to recompute it. The actual fix is at the comment. There is no need to create a new Completer just to filter all of remaining completions at later stage. Fixes #23150 --- .../tools/dotc/interactive/Completion.scala | 19 ++++++++------ .../pc/tests/completion/CompletionSuite.scala | 25 +++++++++++++++++++ 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/interactive/Completion.scala b/compiler/src/dotty/tools/dotc/interactive/Completion.scala index 81afc68e2342..c841184675fc 100644 --- a/compiler/src/dotty/tools/dotc/interactive/Completion.scala +++ b/compiler/src/dotty/tools/dotc/interactive/Completion.scala @@ -242,9 +242,7 @@ object Completion: case tpd.Select(qual @ tpd.This(_), _) :: _ if qual.span.isSynthetic => completer.scopeCompletions.names case StringContextApplication(qual) => completer.scopeCompletions.names ++ completer.selectionCompletions(qual) - case tpd.Select(qual, _) :: _ if qual.typeOpt.hasSimpleKind => - completer.selectionCompletions(qual) - case tpd.Select(qual, _) :: _ => Map.empty + case tpd.Select(qual, _) :: _ => completer.selectionCompletions(qual) case (tree: tpd.ImportOrExport) :: _ => completer.directMemberCompletions(tree.expr) case _ => completer.scopeCompletions.names @@ -367,7 +365,7 @@ object Completion: * For the results of all `xyzCompletions` methods term names and type names are always treated as different keys in the same map * and they never conflict with each other. */ - class Completer(val mode: Mode, pos: SourcePosition, untpdPath: List[untpd.Tree], matches: Name => Boolean): + class Completer(val mode: Mode, pos: SourcePosition, untpdPath: List[untpd.Tree], matches: Name => Boolean)(using Context): /** Completions for terms and types that are currently in scope: * the members of the current class, local definitions and the symbols that have been imported, * recursively adding completions from outer scopes. @@ -381,7 +379,7 @@ object Completion: * (even if the import follows it syntactically) * - a more deeply nested import shadowing a member or a local definition causes an ambiguity */ - def scopeCompletions(using context: Context): CompletionResult = + lazy val scopeCompletions: CompletionResult = /** Temporary data structure representing denotations with the same name introduced in a given scope * as a member of a type, by a local definition or by an import clause @@ -475,9 +473,15 @@ object Completion: def selectionCompletions(qual: tpd.Tree)(using Context): CompletionMap = val adjustedQual = widenQualifier(qual) - implicitConversionMemberCompletions(adjustedQual) ++ + if qual.symbol.is(Package) then + directMemberCompletions(adjustedQual) + else if qual.typeOpt.hasSimpleKind then + implicitConversionMemberCompletions(adjustedQual) ++ extensionCompletions(adjustedQual) ++ directMemberCompletions(adjustedQual) + else + Map.empty + /** Completions for members of `qual`'s type. * These include inherited definitions but not members added by extensions or implicit conversions @@ -584,8 +588,7 @@ object Completion: // There are four possible ways for an extension method to be applicable // 1. The extension method is visible under a simple name, by being defined or inherited or imported in a scope enclosing the reference. - val termCompleter = new Completer(Mode.Term, pos, untpdPath, matches) - val extMethodsInScope = termCompleter.scopeCompletions.names.toList.flatMap: + val extMethodsInScope = scopeCompletions.names.toList.flatMap: case (name, denots) => denots.collect: case d: SymDenotation if d.isTerm && d.termRef.symbol.is(Extension) => (d.termRef, name.asTermName) diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala index 1bae825f7fde..84ee3129b0ea 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala @@ -2203,3 +2203,28 @@ class CompletionSuite extends BaseCompletionSuite: |""".stripMargin, "" ) + + @Test def `no-completions-on-package-selection` = + check( + """package one.@@ + |""".stripMargin, + "" + ) + + @Test def `no-extension-completion-on-packages` = + check( + """object M: + | scala.runt@@ + |""".stripMargin, + """runtime scala + |PartialFunction scala""".stripMargin // those are the actual members of scala + ) + + @Test def `no-extension-completions-on-package-objects` = + check( + """package object magic { def test: Int = ??? } + |object M: + | magic.@@ + |""".stripMargin, + "test: Int" + ) From 708d541cab1e6aacee4a7912fdefcfc7a9ec8af8 Mon Sep 17 00:00:00 2001 From: Tomasz Godzik Date: Mon, 26 May 2025 14:21:11 +0200 Subject: [PATCH 2/2] Fix completion mode filtering + optimize scopeCompletions (#23172) I've changed scopeCompletions to lazy val to memoize because I beliveve we don't need to recompute it. The actual fix is at the comment. There is no need to create a new Completer just to filter all of remaining completions at later stage. Fixes #23150 [Cherry-picked 1eee888622273a1d0f8cd1053f8ce1029c4f0193][modified]