Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cpp/ql/lib/cpp.qll
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,4 @@ import semmle.code.cpp.Preprocessor
import semmle.code.cpp.Iteration
import semmle.code.cpp.NameQualifiers
import DefaultOptions
private import semmle.code.cpp.internal.Overlay
Comment thread
jketema marked this conversation as resolved.
73 changes: 73 additions & 0 deletions cpp/ql/lib/semmle/code/cpp/internal/Overlay.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/**
Comment thread
jketema marked this conversation as resolved.
* Defines entity discard predicates for C++ overlay analysis.
Comment thread
IdrissRio marked this conversation as resolved.
*/

/**
* Holds always for the overlay variant and never for the base variant.
* This local predicate is used to define local predicates that behave
* differently for the base and overlay variant.
*/
overlay[local]
predicate isOverlay() { databaseMetadata("isOverlay", "true") }

overlay[local]
private string getLocationFilePath(@location_default loc) {
exists(@file file | locations_default(loc, file, _, _, _, _) | files(file, result))
}

/**
* An element with a single location. Discard if in a changed file.
*/
overlay[local]
abstract private class Discardable extends @element {
abstract string getFilePath();

predicate existsInBase() { not isOverlay() }

string toString() { none() }
}

overlay[discard_entity]
private predicate discardable(@element e) {
e = any(Discardable d | d.existsInBase() and overlayChangedFiles(d.getFilePath()))
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we're going to need to do something more complex for C. For example, if you have foo.h:

#define A 1

and bar.h:

#if A
int a;
#elif B
int b;
#endif

then if you change foo.h to

#define B 1

then bar.h's declarations change, but bar.h has not changed.

It gets even more complex when you might have different header variants, e.g. we might have one include or bar.h where A is defined, but a different include elsewhere where B is defined.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this something we should do here, or should it happen when we detect which files changed?


/**
* An element with potentially multiple locations, e.g., variables, functions and types.
* Discard only if all locations are in changed files.
*/
overlay[local]
abstract private class MultiDiscardable extends @element {
abstract string getFilePath();

predicate existsInBase() { not isOverlay() }

string toString() { none() }
}

overlay[discard_entity]
private predicate multiDiscardable(@element e) {
e =
any(MultiDiscardable d |
d.existsInBase() and
forall(string path | path = d.getFilePath() | overlayChangedFiles(path))
)
}

overlay[local]
private class DiscardableVarDecl extends Discardable instanceof @var_decl {
override string getFilePath() {
exists(@location_default loc | var_decls(this, _, _, _, loc) |
result = getLocationFilePath(loc)
)
}
}

overlay[local]
private class DiscardableVariable extends MultiDiscardable instanceof @variable {
override string getFilePath() {
exists(@var_decl vd, @location_default loc | var_decls(vd, this, _, _, loc) |
result = getLocationFilePath(loc)
)
}
}