-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Rust: Take trait visibility into account when resolving paths and methods #20321
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -216,7 +216,7 @@ abstract class ItemNode extends Locatable { | |
| // items made available through `use` are available to nodes that contain the `use` | ||
| exists(UseItemNode use | | ||
| use = this.getASuccessor(_, _) and | ||
| result = use.(ItemNode).getASuccessor(name, kind) | ||
| result = use.getASuccessor(name, kind) | ||
| ) | ||
| or | ||
| exists(ExternCrateItemNode ec | result = ec.(ItemNode).getASuccessor(name, kind) | | ||
|
|
@@ -240,12 +240,7 @@ abstract class ItemNode extends Locatable { | |
| ) | ||
| or | ||
| // items made available by an implementation where `this` is the implementing type | ||
| exists(ItemNode node | | ||
| this = node.(ImplItemNodeImpl).resolveSelfTyCand() and | ||
| result = node.getASuccessor(name, kind) and | ||
| kind.isExternalOrBoth() and | ||
| result instanceof AssocItemNode | ||
| ) | ||
| typeImplEdge(this, _, name, kind, result) | ||
|
||
| or | ||
| // trait items with default implementations made available in an implementation | ||
| exists(ImplItemNodeImpl impl, ItemNode trait | | ||
|
|
@@ -1311,6 +1306,7 @@ private predicate declares(ItemNode item, Namespace ns, string name) { | |
| class RelevantPath extends Path { | ||
| RelevantPath() { not this = any(VariableAccess va).(PathExpr).getPath() } | ||
|
|
||
| /** Holds if this is an unqualified path with the textual value `name`. */ | ||
| pragma[nomagic] | ||
| predicate isUnqualified(string name) { | ||
| not exists(this.getQualifier()) and | ||
|
|
@@ -1421,6 +1417,35 @@ private ItemNode unqualifiedPathLookup(RelevantPath p, Namespace ns, SuccessorKi | |
| pragma[nomagic] | ||
| private predicate isUnqualifiedSelfPath(RelevantPath path) { path.isUnqualified("Self") } | ||
|
|
||
| /** Provides the input to `TraitIsVisible`. */ | ||
| signature predicate relevantTraitVisibleSig(Element element, Trait trait); | ||
|
|
||
| /** | ||
| * Provides the `traitIsVisible` predicate for determining if a trait is visible | ||
| * at a given element. | ||
| */ | ||
| module TraitIsVisible<relevantTraitVisibleSig/2 relevantTraitVisible> { | ||
| /** Holds if the trait might be looked up in `encl`. */ | ||
| private predicate traitLookup(ItemNode encl, Element element, Trait trait) { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The implementation of this predicate is inspired by |
||
| // lookup in immediately enclosing item | ||
| relevantTraitVisible(element, trait) and | ||
| encl.getADescendant() = element | ||
| or | ||
| // lookup in an outer scope, but only if the trait is not declared in inner scope | ||
| exists(ItemNode mid | | ||
| traitLookup(mid, element, trait) and | ||
| not trait = mid.getASuccessor(_, _) and | ||
| encl = getOuterScope(mid) | ||
| ) | ||
| } | ||
|
|
||
| /** Holds if the trait `trait` is visible at `element`. */ | ||
| pragma[nomagic] | ||
| predicate traitIsVisible(Element element, Trait trait) { | ||
| exists(ItemNode encl | traitLookup(encl, element, trait) and trait = encl.getASuccessor(_, _)) | ||
| } | ||
| } | ||
|
|
||
| pragma[nomagic] | ||
| private ItemNode resolvePathCand0(RelevantPath path, Namespace ns) { | ||
| exists(ItemNode res | | ||
|
|
@@ -1446,6 +1471,10 @@ private ItemNode resolvePathCandQualifier(RelevantPath qualifier, RelevantPath p | |
| name = path.getText() | ||
| } | ||
|
|
||
| /** | ||
| * Gets the item that `path` resolves to in `ns` when `qualifier` is the | ||
| * qualifier of `path` and `qualifier` resolves to `q`, if any. | ||
| */ | ||
| pragma[nomagic] | ||
| private ItemNode resolvePathCandQualified( | ||
| RelevantPath qualifier, ItemNode q, RelevantPath path, Namespace ns | ||
|
|
@@ -1520,11 +1549,31 @@ private ItemNode resolvePathCand(RelevantPath path) { | |
| ) | ||
| } | ||
|
|
||
| /** Get a trait that should be visible when `path` resolves to `node`, if any. */ | ||
| private Trait getResolvePathTraitUsed(RelevantPath path, AssocItemNode node) { | ||
| exists(TypeItemNode type, ImplItemNodeImpl impl | | ||
| node = resolvePathCandQualified(_, type, path, _) and | ||
| typeImplEdge(type, impl, _, _, node) and | ||
| result = impl.resolveTraitTyCand() | ||
| ) | ||
| } | ||
|
|
||
| private predicate pathTraitUsed(Element path, Trait trait) { | ||
| trait = getResolvePathTraitUsed(path, _) | ||
| } | ||
|
|
||
| /** Gets the item that `path` resolves to, if any. */ | ||
| cached | ||
| ItemNode resolvePath(RelevantPath path) { | ||
| result = resolvePathCand(path) and | ||
| not path = any(Path parent | exists(resolvePathCand(parent))).getQualifier() | ||
| not path = any(Path parent | exists(resolvePathCand(parent))).getQualifier() and | ||
| ( | ||
| // When the result is an associated item of a trait implementation the | ||
| // implemented trait must be visible. | ||
| TraitIsVisible<pathTraitUsed/2>::traitIsVisible(path, getResolvePathTraitUsed(path, result)) | ||
| or | ||
| not exists(getResolvePathTraitUsed(path, result)) | ||
| ) | ||
| or | ||
| // if `path` is the qualifier of a resolvable `parent`, then we should | ||
| // resolve `path` to something consistent with what `parent` resolves to | ||
|
|
@@ -1606,8 +1655,16 @@ private predicate useImportEdge(Use use, string name, ItemNode item, SuccessorKi | |
| not tree.hasRename() and | ||
| name = item.getName() | ||
| or | ||
| name = tree.getRename().getName().getText() and | ||
| name != "_" | ||
| exists(Rename rename | rename = tree.getRename() | | ||
| name = rename.getName().getText() | ||
| or | ||
| // When the rename doesn't have a name it's an underscore import. This | ||
| // makes the imported item visible but unnameable. We represent this | ||
| // by using the name `_` which can never occur in a path. See also: | ||
| // https://doc.rust-lang.org/reference/items/use-declarations.html#r-items.use.as-underscore | ||
| not rename.hasName() and | ||
| name = "_" | ||
|
Comment on lines
+1661
to
+1666
|
||
| ) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note, the old disjunct here did in fact not do anything. When there is a rename it's name is never is empty. Instead |
||
| ) | ||
| ) | ||
| ) | ||
|
|
@@ -1629,6 +1686,18 @@ private predicate externCrateEdge(ExternCrateItemNode ec, string name, CrateItem | |
| ) | ||
| } | ||
|
|
||
| /** | ||
| * Holds if `typeItem` is the implementing type of `impl` and the implementation | ||
| * makes `assoc` available as `name` at `kind`. | ||
| */ | ||
| private predicate typeImplEdge( | ||
| TypeItemNode typeItem, ImplItemNodeImpl impl, string name, SuccessorKind kind, AssocItemNode assoc | ||
| ) { | ||
| typeItem = impl.resolveSelfTyCand() and | ||
| assoc = impl.getASuccessor(name, kind) and | ||
| kind.isExternalOrBoth() | ||
| } | ||
|
|
||
| pragma[nomagic] | ||
| private predicate preludeItem(string name, ItemNode i) { | ||
| exists(Crate stdOrCore, ModuleLikeNode mod, ModuleItemNode prelude, ModuleItemNode rust | | ||
|
|
@@ -1693,7 +1762,7 @@ private module Debug { | |
| useImportEdge(use, name, item, kind) | ||
| } | ||
|
|
||
| ItemNode debuggetASuccessor(ItemNode i, string name, SuccessorKind kind) { | ||
| ItemNode debugGetASuccessor(ItemNode i, string name, SuccessorKind kind) { | ||
| i = getRelevantLocatable() and | ||
| result = i.getASuccessor(name, kind) | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1891,7 +1891,7 @@ private predicate methodCandidate(Type type, string name, int arity, Impl impl) | |
| */ | ||
| pragma[nomagic] | ||
| private predicate methodCandidateTrait(Type type, Trait trait, string name, int arity, Impl impl) { | ||
| trait = resolvePath(impl.(ImplItemNode).getTraitPath()) and | ||
| trait = impl.(ImplItemNode).resolveTraitTy() and | ||
|
||
| methodCandidate(type, name, arity, impl) | ||
| } | ||
|
|
||
|
|
@@ -1903,19 +1903,53 @@ private predicate isMethodCall(MethodCall mc, Type rootType, string name, int ar | |
| } | ||
|
|
||
| private module IsInstantiationOfInput implements IsInstantiationOfInputSig<MethodCall> { | ||
| /** Holds if `mc` specifies a trait and might target a method in `impl`. */ | ||
| pragma[nomagic] | ||
| predicate potentialInstantiationOf(MethodCall mc, TypeAbstraction impl, TypeMention constraint) { | ||
| private predicate methodCallTraitCandidate(MethodCall mc, Impl impl) { | ||
| exists(Type rootType, string name, int arity | | ||
| isMethodCall(mc, rootType, name, arity) and | ||
| constraint = impl.(ImplTypeAbstraction).getSelfTy() | ||
| | | ||
| methodCandidateTrait(rootType, mc.getTrait(), name, arity, impl) | ||
| or | ||
| ) | ||
| } | ||
|
|
||
| /** Holds if `mc` does not specify a trait and might target a method in `impl`. */ | ||
| pragma[nomagic] | ||
| private predicate methodCallCandidate(MethodCall mc, Impl impl) { | ||
| exists(Type rootType, string name, int arity | | ||
| not exists(mc.getTrait()) and | ||
| isMethodCall(mc, rootType, name, arity) and | ||
| methodCandidate(rootType, name, arity, impl) | ||
| ) | ||
| } | ||
|
|
||
| private predicate relevantTraitVisible(Element mc, Trait trait) { | ||
| trait = any(ImplItemNode impl | methodCallCandidate(mc, impl)).resolveTraitTy() | ||
| } | ||
|
|
||
| bindingset[impl] | ||
| pragma[inline_late] | ||
| private TypeRepr getImplSelfTy(Impl impl) { result = impl.getSelfTy() } | ||
|
|
||
| pragma[nomagic] | ||
| predicate potentialInstantiationOf(MethodCall mc, TypeAbstraction impl, TypeMention constraint) { | ||
| constraint = getImplSelfTy(impl) and | ||
| ( | ||
| methodCallTraitCandidate(mc, impl) | ||
| or | ||
| methodCallCandidate(mc, impl) and | ||
| ( | ||
| not exists(impl.(ImplItemNode).resolveTraitTy()) | ||
| or | ||
| // If the `impl` block implements a trait, that trait must be visible in | ||
| // order for the `impl` to be valid. | ||
| exists(Trait trait | | ||
| pragma[only_bind_into](trait) = impl.(ImplItemNode).resolveTraitTy() and | ||
| TraitIsVisible<relevantTraitVisible/2>::traitIsVisible(mc, pragma[only_bind_into](trait)) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The pragma here is to prevent |
||
| ) | ||
| ) | ||
| ) | ||
| } | ||
|
|
||
| predicate relevantTypeMention(TypeMention constraint) { | ||
| exists(Impl impl | methodCandidate(_, _, _, impl) and constraint = impl.getSelfTy()) | ||
| } | ||
|
|
||
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 removed explicit cast
use.(ItemNode)was unnecessary sinceuseis already declared asUseItemNodewhich extendsItemNode. This change improves code readability by removing redundant casting.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.
Correct.