Skip to content
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

Fixing Java field access #1862

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -265,38 +265,43 @@ class ImportResolver(ctx: TranslationContext) : ComponentPass(ctx) {
}

private fun handleImportDeclaration(import: ImportDeclaration) {
// We always need to search at the global scope because we are "importing" something, so by
// definition, this is not in the scope of the current file.
val scope = scopeManager.globalScope ?: return

// Let's do some importing. We need to import either a wildcard
if (import.wildcardImport) {
val list =
scopeManager.lookupSymbolByName(
import.import,
import.language,
import.location,
scope
)
val symbol = list.singleOrNull()
if (symbol != null) {
// In this case, the symbol must point to a name scope
val symbolScope = scopeManager.lookupScope(symbol)
if (symbolScope is NameScope) {
import.importedSymbols = symbolScope.symbols
}
}
} else {
// or a symbol directly
val list =
scopeManager
.lookupSymbolByName(import.import, import.language, import.location, scope)
.toMutableList()
import.importedSymbols = mutableMapOf(import.symbol to list)
}
import.updateImportedSymbols()
}

override fun cleanup() {
// Nothing to do
}
}

/**
* This function updates the [ImportDeclaration.importedSymbols]. This is done once at the beginning
* by the [ImportResolver]. However, we need to update this list once we infer new symbols in
* namespaces that are imported at a later stage (e.g., in the [TypeResolver]), otherwise they won't
* be visible to the later passes.
*/
context(Pass<*>)
fun ImportDeclaration.updateImportedSymbols() {
// We always need to search at the global scope because we are "importing" something, so by
// definition, this is not in the scope of the current file.
val scope = scopeManager.globalScope ?: return

// Let's do some importing. We need to import either a wildcard
if (this.wildcardImport) {
val list = scopeManager.lookupSymbolByName(this.import, this.language, this.location, scope)
val symbol = list.singleOrNull()
if (symbol != null) {
// In this case, the symbol must point to a name scope
val symbolScope = scopeManager.lookupScope(symbol)
if (symbolScope is NameScope) {
this.importedSymbols = symbolScope.symbols
}
}
} else {
// or a symbol directly
val list =
scopeManager
.lookupSymbolByName(this.import, this.language, this.location, scope)
.toMutableList()
this.importedSymbols = mutableMapOf(this.symbol to list)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ open class SymbolResolver(ctx: TranslationContext) : ComponentPass(ctx) {
// Preparation for a future without legacy call resolving. Taking the first candidate is not
// ideal since we are running into an issue with function pointers here (see workaround
// below).
var wouldResolveTo = ref.candidates.singleOrNull()
var wouldResolveTo = ref.candidates.firstOrNull()
Copy link
Contributor

Choose a reason for hiding this comment

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

This change does no longer account for ambiguities. The old code with singleOrNull() prefers not to resolve anything if it's not sure about the candidate. I think this was also intended, wasn't it?

Copy link
Member Author

Choose a reason for hiding this comment

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

It was semi-intended. I am ok with the old behavior, but we should at least

  1. have a warning if we do not resolve it
  2. forward the decision to the Language. In Java it is possible to have an ambiguity here if you statically import a field and a function and both can have the same name and end up in candidates. I need to deal with that here


// For now, we need to ignore reference expressions that are directly embedded into call
// expressions, because they are the "callee" property. In the future, we will use this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import de.fraunhofer.aisec.cpg.ScopeManager
import de.fraunhofer.aisec.cpg.TranslationContext
import de.fraunhofer.aisec.cpg.TypeManager
import de.fraunhofer.aisec.cpg.graph.*
import de.fraunhofer.aisec.cpg.graph.declarations.ImportDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration
import de.fraunhofer.aisec.cpg.graph.types.DeclaresType
import de.fraunhofer.aisec.cpg.graph.types.ObjectType
Expand Down Expand Up @@ -141,6 +142,9 @@ open class TypeResolver(ctx: TranslationContext) : ComponentPass(ctx) {
t.recordDeclaration = node
}
}
} else if (node is ImportDeclaration) {
// Update the imports, as they might have changed because of inference
node.updateImportedSymbols()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ fun assertUsageOf(usingNode: Node?, usedNode: Node?) {
fun assertUsageOfMemberAndBase(usingNode: Node?, usedBase: Node?, usedMember: Declaration?) {
assertNotNull(usingNode)
if (usingNode !is MemberExpression && !ENFORCE_MEMBER_EXPRESSION) {
// Assumtion here is that the target of the member portion of the expression and not the
// Assumption here is that the target of the member portion of the expression and not the
// base is resolved
assertUsageOf(usingNode, usedMember)
} else {
Expand Down
Loading
Loading