Swift: make only certain elements hideable in the AST#13249
Swift: make only certain elements hideable in the AST#13249redsun82 merged 6 commits intocodeql-cli-2.13.3from
Conversation
| this.resolvesFrom(e) and | ||
| result = e.getFullyUnresolved() | ||
| ) | ||
| } |
There was a problem hiding this comment.
Is it possible to have a hideable parent of a non-hideable child?
There was a problem hiding this comment.
you mean in AST parent/child terms, or in superclass/subclass terms? Both can mix, which just made me think of an issue with this change: hideability should actually travel both directions of inheritance. For example marking Expr explicitly as hideable, doesn't only mean that all Expr subclasses are hideable, but also superclasses such as AstNode. Failing to do so like I'm doing here means that something like BraceStmt.getElement/1 would fail to hide conversions which would be quite confusing.
There was a problem hiding this comment.
The worry that prompted this question was the removal of getFullyUnresolved() calls from non-hideable elements. Which seems maybe wrong if a hideable element can resolve to it (I'm not really sure why we use getFullyUnresolved() though).
There was a problem hiding this comment.
yeah, it was wrong, I ended up reverting that. This does mean one could implement getResolveStep on a non-hideable class, which would compile but not really work. Just something to keep in mind. We can add final overrides to the non-hideable AstNode classes to avoid such a problem if we see this happening at some point.
1e4fc05 to
b4edc92
Compare
geoffw0
left a comment
There was a problem hiding this comment.
You've addressed my concerns, and I want this change, but I can't confidently say I have a full understanding of everything that is going on.
My understanding is:
- the goal of this change is to reduce the amount of generated QL and potentially make things a bit speedier as well.
- previously everything was hideable.
- now 'just'
Type,ExprandPatternare hideable...- plus everything above them in the QL class tree (e.g.
Element), because when you have anElement xit could in fact be aType/Expr/Pattern. - plus by anything below them in the QL class tree (e.g.
BitwiseOperation), because those things are aType/Expr/Pattern.
- plus everything above them in the QL class tree (e.g.
- but plenty of specific classes outside of that are not hideable any more.
- for non-hideable classes, we can skip including the
getImmediate*machinery and calls to.getResolveStep()and the like.- but not the other direction,
.getFullyUnresolved(), because the result could be hideable.
- but not the other direction,
I would appreciate if you could correct any misconceptions in the above, and perhaps get a second pair of eyes on this?
I've added @MathiasVP as a reviewer too, as yes, two set of eyes is better than one. Your analysis is correct, although it is to be noted that as far as the current actually hidden nodes go, it is also not really possible for unhideable nodes to have a |
|
Happy with this PR pending @MathiasVP s opinion and a successful DCA run. |
MathiasVP
left a comment
There was a problem hiding this comment.
I think the changes makes sense. A couple of open questions. I'd also like to run a few CFG consistency queries on this PR to make sure we didn't break CFG construction in some subtle way.
| // constants that resolve to the same value. | ||
| exists(ControlFlowElement parent | | ||
| parent.asAstNode() = n.asAstNode().getResolveStep() and | ||
| parent.asAstNode() = n.asAstNode().(HideableElement).getResolveStep() and |
There was a problem hiding this comment.
I'm not sure I like this change very much 🤔. So now, if you have a value of a very general class (like ... Locatable, for example) and you want to resolve it to skip hidden elements you have to explicitly consider two cases:
- Either it's an instance of
HideableElementand so you have to do an inline cast and callgetResolveStep, or - It's not a
HideableElementand you don't have to do anything.
There was a problem hiding this comment.
this was indeed reverted in a later commit
| n = any(ConditionElement condElem).getBoolean().getFullyUnresolved() | ||
| or | ||
| n = any(ConditionElement condElem).getAvailability().getFullyUnresolved() | ||
| n = any(ConditionElement condElem).getAvailability() |
There was a problem hiding this comment.
An open question:
Looking through the changes to CFG construction, we've had bugs in the past because someone (most likely me 😅) forgot to call recursively on the fully unresolved / fully converted sub-element which led to inconsistent CFGs. And in those cases it was pretty easy to see syntactically that:
- We were calling
getFullyUnresolved(orgetFullyConverted) in 99% of the cases, and - 1% of the cases were missing a call to
getFullyUnresolved(orgetFullyConverted)
And so that 1% was most likely a bug. But now some places will need the to call getFullyUnresolved (when it's available), and other places won't need to do that (because there's no such method on the class).
Do you think this is a problem?
There was a problem hiding this comment.
I backtracked on this on a later commit. As I understood that "hideability" travels both directions of a hierarchy, Element ended up hideable no matter what, so in the end I kept the resolve/getFullyUnresolved predicates on Element, and undid all these changes. I agree that that was a possible source of bugs.
Sorry for the mix up.
|
@redsun82 it took me a few restarts to get a pair of semmle-code and codeql SHAs with compatible dbschemes, but that last experiment seems to have worked 😂. |
|
DCA looks good to me, apart the known Thomvis/Construct wobbly number of extracted files. |
|
@geoffw0: @MathiasVP's comments are addressed by later commits, and DCA shows nothing suspicious, so I think we can merge this. |
|
Ah, wait, there is this comment by @MathiasVP:
Do you know what it means @geoffw0? On the other hand, as far as I understand this was a comment based on the CFG library changes, that got completely reverted. |
I guess it means he wants to run a few more checks locally and build confidence. When/if @MathiasVP gives the 👍, I'm happy for this to be merged. |
I guess it means running the equivalent of https://github.com/github/codeql/blob/main/ruby/ql/consistency-queries/CfgConsistency.ql . For Ruby we run consistency checks automatically in CI as part of |
By tweaking code generation and adding an explicit
@ql.hideableattribute in the schema, only certain specific types get theresolveinfrastructure, decreasing the number ofImmediatepredicates being generated.