-
Notifications
You must be signed in to change notification settings - Fork 3.1k
fix: complete backticked idents containing $
#11024
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This works in 3.6.3. |
this(sym.name) = this(sym.name) + toMember(sym, symtpe) | ||
} else if (!sym.isError && !sym.isArtifact && sym.hasRawInfo) { | ||
val decodedName = sym.name.decodedName | ||
if (!decodedName.containsChar('$') || decodedName.encoded == sym.name.toString) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
!decodedName.containsChar('$')
allows names that have no $
or use encodings like $lt
.
decodedName.encoded == sym.name.toString
allows names that have a $
but use no encoding.
So, not allowed are names that use an encoding and also have a $
. Something like foo$_<
. Is that the intention?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that is the intention. Are there ever any non-internal names that would use the encoding and have a $
? (Is your foo$_<
a legitimate user-writable thing?)
I'm a bit more concerned by the other half of the equation though: are there any internal names where the $
is escaped? I thought there wouldn't be, but there's a test failure around a $init$
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
foo$_<
seems to be a legal identifier.
I think instaed of the filter that only disallows these weird identifiers, you could just as well allow any name.
The typical internal names are $outer
, g$1
for a nested method g
, f$default$1
for a default of method f
, $init$
for trait initialization, $anon
for methods implementing lambdas, foo$sp$XX
for speicalized methods, and many others. Note that constructors have name $lessinit$greater
/ <init>
.
Many of them are probably not a concern because they are created in later phases, I assume the presentation compiler queries the symbol table right after the typer phase. Some are definitely created early, but might have the isArtifact
flag which is checked above. You could add another check for sym.isSynthetic
, I think this would make sense, it might fix the $init$
case. (Artifact is translated to ACC_SYNTHETIC
in bytecode, while synthetic remains a Scala symbol table only flag). Unfortunately the way we apply the artifact and synthetic flags is not very consistent. So if there remain synthetic identifiers that go through, we could add specific filters for those.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll try out removing any $
based checks and see how far I get with the symbol flags.
decodedName.encoded == sym.name.toString
allows names that have a $ but use no encoding.
Probably moot assuming your suggestion works out, but I think I misread this. The intention is that if someone wrote $
in one of their variables it would be encoded, such that if you decode and then re-encode, you'll get the same internal (encoded) representation. OTOH, if an internal name is generated with a $
in its encoded form, then decoding passes the $
through unchanged, but re-encoding escapes it (so the quoted predicate above fails).
I agree this is pretty roundabout. Really I wanted NameTransformer.isInCodomainOfEncode
and x.decoded.encoded == x
is a poor-man's version of that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if someone wrote
$
in one of their variables it would be encoded
I think this is not the case, $
is not encoded as it's valid in bytecode.
scala> :power
scala> val n = TermName("$hey")
scala> n.encodedName
val res0: n.ThisNameType = $hey
scala> n.encodedName.decodedName
val res1: $r.intp.global.TermName = $hey
05c069f
to
d47b957
Compare
@lrytz I think this is ready for review again |
[inaccessible] private[this] val self: callccInterpreter.type | ||
[inaccessible] private[this] val self: callccInterpreter.type | ||
[inaccessible] private[this] val self: callccInterpreter.type | ||
[inaccessible] private[this] val self: callccInterpreter.type |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you know why these 4x self
show up now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, these are the fields you get from implicit classes:
scala.Predef.any2stringadd
scala.Predef.StringFormat
scala.Predef.Ensuring
scala.Predef.ArrowAssoc
In all 4 cases, the implicit class field is called self
.
They did not used to show up because the decoded names have dollar signs (I'm guessing this is connected to the fact they're declare in the object Predef
, though I didn't look further)
scala$Predef$any2stringadd$$self
scala$Predef$StringFormat$$self
scala$Predef$Ensuring$$self
scala$Predef$ArrowAssoc$$self
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, thanks. I don't have the background why [inaccessible]
symbols are included in completions. I wonder if inaccessible symbols of implicit conversion targets should really be there as well, or if that is by mistake.
The scala$Predef$any2stringadd$$self
name is an "expanded" name set by sym.makeNotPrivate
, sym.hasFlag(EXPANDEDNAME)
should be true here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The [inaccessible]
means that it doesn't actually show up in the completions. These tests have completions that generally do include results that need an implicit conversion, so I think this is the right behavior.
@@ -1009,7 +1009,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") | |||
def add(sym: Symbol, pre: Type, implicitlyAdded: Boolean)(toMember: (Symbol, Type) => M): Unit = { | |||
if ((sym.isGetter || sym.isSetter) && sym.accessed != NoSymbol) { | |||
add(sym.accessed, pre, implicitlyAdded)(toMember) | |||
} else if (!sym.name.decodedName.containsName("$") && !sym.isError && !sym.isArtifact && sym.hasRawInfo) { | |||
} else if (!sym.name.decodedName.startsWith('$') && !sym.isError && !sym.isArtifact && sym.hasRawInfo) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you test what happens if the condition is removed entirely? Maybe we can make a more specific filter, e.g. for $init$
and ...?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removing the condition causes failures only due to $init$
. Full list is just
partest \
/Users/alec/Code/scala/test/files/presentation/infix-completion \
/Users/alec/Code/scala/test/files/presentation/infix-completion2 \
/Users/alec/Code/scala/test/files/presentation/scope-completion-3
Only reason I didn't do that is that when I went into StdNames
looking for the right constant against which to compare, I saw a bunch of other names that probably should never be in completions and which start in $
. I'm not sure if those are ever going to show up...
scala/src/reflect/scala/reflect/internal/StdNames.scala
Lines 370 to 405 in f87d1a0
val LAZY_SLOW_SUFFIX: NameType = nameType("$lzycompute") | |
val UNIVERSE_BUILD_PREFIX: NameType = nameType("$u.internal.reificationSupport.") | |
val UNIVERSE_PREFIX: NameType = nameType("$u.") | |
val UNIVERSE_SHORT: NameType = nameType("$u") | |
val MIRROR_PREFIX: NameType = nameType("$m.") | |
val MIRROR_SHORT: NameType = nameType("$m") | |
val MIRROR_UNTYPED: NameType = nameType("$m$untyped") | |
val REIFY_FREE_PREFIX: NameType = nameType("free$") | |
val REIFY_FREE_THIS_SUFFIX: NameType = nameType(s"$$this") | |
val REIFY_FREE_VALUE_SUFFIX: NameType = nameType(s"$$value") // looks like missing interpolator due to `value` in scope | |
val REIFY_SYMDEF_PREFIX: NameType = nameType("symdef$") | |
val QUASIQUOTE_CASE: NameType = nameType("$quasiquote$case$") | |
val QUASIQUOTE_EARLY_DEF: NameType = nameType("$quasiquote$early$def$") | |
val QUASIQUOTE_FILE: String = "<quasiquote>" | |
val QUASIQUOTE_FOR_ENUM: NameType = nameType("$quasiquote$for$enum$") | |
val QUASIQUOTE_NAME_PREFIX: String = "nn$" | |
val QUASIQUOTE_PACKAGE_STAT: NameType = nameType("$quasiquote$package$stat$") | |
val QUASIQUOTE_PARAM: NameType = nameType("$quasiquote$param$") | |
val QUASIQUOTE_PAT_DEF: NameType = nameType("$quasiquote$pat$def$") | |
val QUASIQUOTE_PREFIX: String = "qq$" | |
val QUASIQUOTE_REFINE_STAT: NameType = nameType("$quasiquote$refine$stat$") | |
val QUASIQUOTE_TUPLE: NameType = nameType("$quasiquote$tuple$") | |
val QUASIQUOTE_UNLIFT_HELPER: String = "$quasiquote$unlift$helper$" | |
val MIXIN_CONSTRUCTOR: NameType = nameType("$init$") | |
val MODULE_INSTANCE_FIELD: NameType = nameType(NameTransformer.MODULE_INSTANCE_NAME) // "MODULE$" | |
val OUTER: NameType = nameType("$outer") | |
val OUTER_LOCAL: NameType = OUTER.localName | |
val OUTER_ARG: NameType = nameType("arg" + OUTER) | |
val OUTER_SYNTH: NameType = nameType("<outer>") // emitted by pattern matcher, replaced by outer accessor in explicitouter | |
val ROOTPKG: NameType = nameType("_root_") | |
val SELECTOR_DUMMY: NameType = nameType("<unapply-selector>") | |
val SELF: NameType = nameType(s"$$this") | |
val SETTER_SUFFIX: NameType = nameType(NameTransformer.SETTER_SUFFIX_STRING) | |
val SPECIALIZED_INSTANCE: NameType = nameType("specInstance$") | |
val STAR: NameType = nameType("*") | |
val THIS: NameType = nameType(s"_$$this") |
If you think just guarding against $init$
is enough, happy to update to that. I only left the startsWith('$')
after seeing how many names in the above list start with $
...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not obvious.. some of these are suffixes. $outer
is not, but outer fields are probably added in a later phase. There's also the sym.isArtifact
check which should be true for most synthetics, but $init$
seems to be a counter-example.
Since the goal of this PR is to show names containing $
, it seems a bit ad-hoc to me to change the filter to startsWith('$')
. So maybe it's better to remove it and address new unwanted candidates such as $init$
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tried what you suggested out - rather than do anything around '$'
, I removed the check entirely and added ARTIFACT
to $init$
. I did a spot check of the other $
-containing symbols and AFAICT they most have ARTIFACT
set or should not show up in completions for other reasons.
This does mean that more test output changes (at least those that are printing the flags for $init$
defs)
728d49a
to
c6db746
Compare
I think the CI failure is spurious... |
@harpocrates CI is fixed. I might have sent you down the wrong path though. The |
Funny that I can buy that What do you propose there? Going back to just checking against the |
Yeah, it's history and probably not worth changing at this point..
Ah.. you're right
None of these have the So yes, I think we should go for filtering |
Sounds good - doing this now. @lrytz Unrelated, but now that 2.13.17 is in snapshot mode, I get errors trying to compile anything in SBT:
I can work around this by rebasing onto an old |
I assume you opened the repo in vs code / metals, which added the semanticdb plugin. IIRC metals adds a Or you revert |
Replace the ad-hoc check for hiding from completions identifiers containing a `$` with a check against specific flavours of compiler-generated $-containing names.
c6db746
to
1b0c18f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you! Sorry it was a winding road.
The previous check around filtering out names whose decoded representation contained
$
was probably written with the idea being that most dollar signs are just encoding artifacts, however it misses the fact that every once in a blue moon, the decoding result might have a$
that was actually written by the user (as opposed to something synthetic likeMODULE$
)