Swift: extract opaque types and their decls

This commit is contained in:
Paolo Tranquilli
2022-11-09 09:27:25 +01:00
parent cfbaf5e53b
commit b399d8df7e
28 changed files with 288 additions and 31 deletions

View File

@@ -390,4 +390,15 @@ codeql::IfConfigDecl DeclTranslator::translateIfConfigDecl(const swift::IfConfig
return entry;
}
std::optional<codeql::OpaqueTypeDecl> DeclTranslator::translateOpaqueTypeDecl(
const swift::OpaqueTypeDecl& decl) {
if (auto entry = createNamedEntry(decl)) {
fillTypeDecl(decl, *entry);
entry->naming_declaration = dispatcher.fetchLabel(decl.getNamingDecl());
entry->opaque_generic_params = dispatcher.fetchRepeatedLabels(decl.getOpaqueGenericParams());
return entry;
}
return std::nullopt;
}
} // namespace codeql

View File

@@ -45,6 +45,7 @@ class DeclTranslator : public AstTranslatorBase<DeclTranslator> {
codeql::ImportDecl translateImportDecl(const swift::ImportDecl& decl);
std::optional<codeql::ModuleDecl> translateModuleDecl(const swift::ModuleDecl& decl);
codeql::IfConfigDecl translateIfConfigDecl(const swift::IfConfigDecl& decl);
std::optional<codeql::OpaqueTypeDecl> translateOpaqueTypeDecl(const swift::OpaqueTypeDecl& decl);
private:
std::string mangledName(const swift::ValueDecl& decl);

View File

@@ -250,4 +250,12 @@ codeql::ModuleType TypeTranslator::translateModuleType(const swift::ModuleType&
entry.module = dispatcher.fetchLabel(type.getModule());
return entry;
}
codeql::OpaqueTypeArchetypeType TypeTranslator::translateOpaqueTypeArchetypeType(
const swift::OpaqueTypeArchetypeType& type) {
auto entry = createTypeEntry(type);
fillArchetypeType(type, entry);
entry.declaration = dispatcher.fetchLabel(type.getDecl());
return entry;
}
} // namespace codeql

View File

@@ -69,6 +69,8 @@ class TypeTranslator : public TypeTranslatorBase<TypeTranslator> {
codeql::BuiltinIntegerType translateBuiltinIntegerType(const swift::BuiltinIntegerType& type);
codeql::OpenedArchetypeType translateOpenedArchetypeType(const swift::OpenedArchetypeType& type);
codeql::ModuleType translateModuleType(const swift::ModuleType& type);
codeql::OpaqueTypeArchetypeType translateOpaqueTypeArchetypeType(
const swift::OpaqueTypeArchetypeType& type);
private:
void fillType(const swift::TypeBase& type, codeql::Type& entry);

View File

@@ -259,6 +259,12 @@ module Raw {
class OpaqueTypeDecl extends @opaque_type_decl, GenericTypeDecl {
override string toString() { result = "OpaqueTypeDecl" }
ValueDecl getNamingDeclaration() { opaque_type_decls(this, result) }
GenericTypeParamType getOpaqueGenericParam(int index) {
opaque_type_decl_opaque_generic_params(this, index, result)
}
}
class ParamDecl extends @param_decl, VarDecl {
@@ -1477,6 +1483,8 @@ module Raw {
class OpaqueTypeArchetypeType extends @opaque_type_archetype_type, ArchetypeType {
override string toString() { result = "OpaqueTypeArchetypeType" }
OpaqueTypeDecl getDeclaration() { opaque_type_archetype_types(this, result) }
}
class OpenedArchetypeType extends @opened_archetype_type, ArchetypeType {

View File

@@ -2,9 +2,70 @@
private import codeql.swift.generated.Synth
private import codeql.swift.generated.Raw
import codeql.swift.elements.decl.GenericTypeDecl
import codeql.swift.elements.type.GenericTypeParamType
import codeql.swift.elements.decl.ValueDecl
module Generated {
/**
* A declaration of an opaque type, that is formally equivalent to a given type but abstracts it
* away.
*
* Such a declaration is implicitly given when a declaration is written with an opaque result type,
* for example
* ```
* func opaque() -> some SignedInteger { return 1 }
* ```
* See https://docs.swift.org/swift-book/LanguageGuide/OpaqueTypes.html.
*/
class OpaqueTypeDecl extends Synth::TOpaqueTypeDecl, GenericTypeDecl {
override string getAPrimaryQlClass() { result = "OpaqueTypeDecl" }
/**
* Gets the naming declaration of this opaque type declaration.
*
* This includes nodes from the "hidden" AST. It can be overridden in subclasses to change the
* behavior of both the `Immediate` and non-`Immediate` versions.
*/
ValueDecl getImmediateNamingDeclaration() {
result =
Synth::convertValueDeclFromRaw(Synth::convertOpaqueTypeDeclToRaw(this)
.(Raw::OpaqueTypeDecl)
.getNamingDeclaration())
}
/**
* Gets the naming declaration of this opaque type declaration.
*/
final ValueDecl getNamingDeclaration() { result = getImmediateNamingDeclaration().resolve() }
/**
* Gets the `index`th opaque generic parameter of this opaque type declaration (0-based).
*
* This includes nodes from the "hidden" AST. It can be overridden in subclasses to change the
* behavior of both the `Immediate` and non-`Immediate` versions.
*/
GenericTypeParamType getImmediateOpaqueGenericParam(int index) {
result =
Synth::convertGenericTypeParamTypeFromRaw(Synth::convertOpaqueTypeDeclToRaw(this)
.(Raw::OpaqueTypeDecl)
.getOpaqueGenericParam(index))
}
/**
* Gets the `index`th opaque generic parameter of this opaque type declaration (0-based).
*/
final GenericTypeParamType getOpaqueGenericParam(int index) {
result = getImmediateOpaqueGenericParam(index).resolve()
}
/**
* Gets any of the opaque generic parameters of this opaque type declaration.
*/
final GenericTypeParamType getAnOpaqueGenericParam() { result = getOpaqueGenericParam(_) }
/**
* Gets the number of opaque generic parameters of this opaque type declaration.
*/
final int getNumberOfOpaqueGenericParams() { result = count(getAnOpaqueGenericParam()) }
}
}

View File

@@ -2,9 +2,33 @@
private import codeql.swift.generated.Synth
private import codeql.swift.generated.Raw
import codeql.swift.elements.type.ArchetypeType
import codeql.swift.elements.decl.OpaqueTypeDecl
module Generated {
/**
* An opaque type, that is a type formally equivalent to an underlying type but abstracting it away.
*
* See https://docs.swift.org/swift-book/LanguageGuide/OpaqueTypes.html.
*/
class OpaqueTypeArchetypeType extends Synth::TOpaqueTypeArchetypeType, ArchetypeType {
override string getAPrimaryQlClass() { result = "OpaqueTypeArchetypeType" }
/**
* Gets the declaration of this opaque type archetype type.
*
* This includes nodes from the "hidden" AST. It can be overridden in subclasses to change the
* behavior of both the `Immediate` and non-`Immediate` versions.
*/
OpaqueTypeDecl getImmediateDeclaration() {
result =
Synth::convertOpaqueTypeDeclFromRaw(Synth::convertOpaqueTypeArchetypeTypeToRaw(this)
.(Raw::OpaqueTypeArchetypeType)
.getDeclaration())
}
/**
* Gets the declaration of this opaque type archetype type.
*/
final OpaqueTypeDecl getDeclaration() { result = getImmediateDeclaration().resolve() }
}
}

View File

@@ -528,7 +528,15 @@ nominal_type_decls( //dir=decl
);
opaque_type_decls( //dir=decl
unique int id: @opaque_type_decl
unique int id: @opaque_type_decl,
int naming_declaration: @value_decl_or_none ref
);
#keyset[id, index]
opaque_type_decl_opaque_generic_params( //dir=decl
int id: @opaque_type_decl ref,
int index: int ref,
int opaque_generic_param: @generic_type_param_type_or_none ref
);
param_decls( //dir=decl
@@ -2222,7 +2230,8 @@ dictionary_types( //dir=type
;
opaque_type_archetype_types( //dir=type
unique int id: @opaque_type_archetype_type
unique int id: @opaque_type_archetype_type,
int declaration: @opaque_type_decl_or_none ref
);
opened_archetype_types( //dir=type
@@ -2389,6 +2398,11 @@ variadic_sequence_types( //dir=type
| @unspecified_element
;
@opaque_type_decl_or_none =
@opaque_type_decl
| @unspecified_element
;
@opaque_value_expr_or_none =
@opaque_value_expr
| @unspecified_element

View File

@@ -5,30 +5,31 @@ predicate toBeTested(Element e) {
e instanceof File
or
exists(ModuleDecl m |
m = e and
not m.isBuiltinModule() and
not m.isSystemModule() and
(m = e or m.getInterfaceType() = e)
not m.isSystemModule()
)
or
exists(Locatable loc |
loc.getLocation().getFile().getName().matches("%swift/ql/test%") and
e.(Locatable).getLocation().getFile().getName().matches("%swift/ql/test%")
or
exists(Element tested |
toBeTested(tested) and
(
e = loc
e = tested.(ValueDecl).getInterfaceType()
or
exists(Type t |
(e = t or e = t.(ExistentialType).getConstraint() or e = t.getCanonicalType()) and
(
t = loc.(ValueDecl).getInterfaceType()
or
t = loc.(NominalTypeDecl).getType()
or
t = loc.(VarDecl).getType()
or
t = loc.(Expr).getType()
)
)
e = tested.(NominalTypeDecl).getType()
or
e = tested.(VarDecl).getType()
or
e = tested.(Expr).getType()
or
e = tested.(Type).getCanonicalType()
or
e = tested.(ExistentialType).getConstraint()
or
e.(UnspecifiedElement).getParent() = tested
or
e.(OpaqueTypeDecl).getNamingDeclaration() = tested
)
)
or
toBeTested(e.(UnspecifiedElement).getParent())
}

View File

@@ -1,4 +0,0 @@
// generated by codegen/codegen.py
After a swift source file is added in this directory and codegen/codegen.py is run again, test queries
will appear and this file will be deleted

View File

@@ -0,0 +1,4 @@
| file://:0:0:0:0 | _ | getModule: | file://:0:0:0:0 | opaque_types | getInterfaceType: | (some Base).Type | getName: | _ | getNamingDeclaration: | opaque_types.swift:9:1:9:51 | baz(_:) |
| file://:0:0:0:0 | _ | getModule: | file://:0:0:0:0 | opaque_types | getInterfaceType: | (some P).Type | getName: | _ | getNamingDeclaration: | opaque_types.swift:5:1:5:45 | bar(_:) |
| file://:0:0:0:0 | _ | getModule: | file://:0:0:0:0 | opaque_types | getInterfaceType: | (some P).Type | getName: | _ | getNamingDeclaration: | opaque_types.swift:13:1:13:59 | bazz() |
| file://:0:0:0:0 | _ | getModule: | file://:0:0:0:0 | opaque_types | getInterfaceType: | (some SignedInteger).Type | getName: | _ | getNamingDeclaration: | opaque_types.swift:1:1:1:45 | foo() |

View File

@@ -0,0 +1,16 @@
// generated by codegen/codegen.py
import codeql.swift.elements
import TestUtils
from
OpaqueTypeDecl x, ModuleDecl getModule, Type getInterfaceType, string getName,
ValueDecl getNamingDeclaration
where
toBeTested(x) and
not x.isUnknown() and
getModule = x.getModule() and
getInterfaceType = x.getInterfaceType() and
getName = x.getName() and
getNamingDeclaration = x.getNamingDeclaration()
select x, "getModule:", getModule, "getInterfaceType:", getInterfaceType, "getName:", getName,
"getNamingDeclaration:", getNamingDeclaration

View File

@@ -0,0 +1,7 @@
// generated by codegen/codegen.py
import codeql.swift.elements
import TestUtils
from OpaqueTypeDecl x, int index
where toBeTested(x) and not x.isUnknown()
select x, index, x.getBaseType(index)

View File

@@ -0,0 +1,7 @@
// generated by codegen/codegen.py
import codeql.swift.elements
import TestUtils
from OpaqueTypeDecl x, int index
where toBeTested(x) and not x.isUnknown()
select x, index, x.getGenericTypeParam(index)

View File

@@ -0,0 +1,4 @@
| file://:0:0:0:0 | _ | 0 | \u03c4_0_0 |
| file://:0:0:0:0 | _ | 0 | \u03c4_1_0 |
| file://:0:0:0:0 | _ | 0 | \u03c4_1_0 |
| file://:0:0:0:0 | _ | 0 | \u03c4_1_0 |

View File

@@ -0,0 +1,7 @@
// generated by codegen/codegen.py
import codeql.swift.elements
import TestUtils
from OpaqueTypeDecl x, int index
where toBeTested(x) and not x.isUnknown()
select x, index, x.getOpaqueGenericParam(index)

View File

@@ -0,0 +1,13 @@
func foo() -> some SignedInteger { return 1 }
protocol P {}
func bar<T: P>(_ x: T) -> some P { return x }
class Base {}
func baz<T: Base>(_ x: T) -> some Base { return x }
class Generic<T: Equatable>: P {}
func bazz<T: Equatable>() -> some P { return Generic<T>() }

View File

@@ -1,4 +0,0 @@
// generated by codegen/codegen.py
After a swift source file is added in this directory and codegen/codegen.py is run again, test queries
will appear and this file will be deleted

View File

@@ -0,0 +1,4 @@
| some Base | getName: | some Base | getCanonicalType: | some Base | getInterfaceType: | \u03c4_1_0 | getDeclaration: | file://:0:0:0:0 | _ |
| some P | getName: | some P | getCanonicalType: | some P | getInterfaceType: | \u03c4_1_0 | getDeclaration: | file://:0:0:0:0 | _ |
| some P | getName: | some P | getCanonicalType: | some P | getInterfaceType: | \u03c4_1_0 | getDeclaration: | file://:0:0:0:0 | _ |
| some SignedInteger | getName: | some SignedInteger | getCanonicalType: | some SignedInteger | getInterfaceType: | \u03c4_0_0 | getDeclaration: | file://:0:0:0:0 | _ |

View File

@@ -0,0 +1,16 @@
// generated by codegen/codegen.py
import codeql.swift.elements
import TestUtils
from
OpaqueTypeArchetypeType x, string getName, Type getCanonicalType, Type getInterfaceType,
OpaqueTypeDecl getDeclaration
where
toBeTested(x) and
not x.isUnknown() and
getName = x.getName() and
getCanonicalType = x.getCanonicalType() and
getInterfaceType = x.getInterfaceType() and
getDeclaration = x.getDeclaration()
select x, "getName:", getName, "getCanonicalType:", getCanonicalType, "getInterfaceType:",
getInterfaceType, "getDeclaration:", getDeclaration

View File

@@ -0,0 +1,13 @@
| some P | 0 | opaque_types.swift:3:1:3:13 | P |
| some P | 0 | opaque_types.swift:3:1:3:13 | P |
| some SignedInteger | 0 | file://:0:0:0:0 | SignedInteger |
| some SignedInteger | 1 | file://:0:0:0:0 | BinaryInteger |
| some SignedInteger | 2 | file://:0:0:0:0 | SignedNumeric |
| some SignedInteger | 3 | file://:0:0:0:0 | CustomStringConvertible |
| some SignedInteger | 4 | file://:0:0:0:0 | Hashable |
| some SignedInteger | 5 | file://:0:0:0:0 | Numeric |
| some SignedInteger | 6 | file://:0:0:0:0 | Strideable |
| some SignedInteger | 7 | file://:0:0:0:0 | Equatable |
| some SignedInteger | 8 | file://:0:0:0:0 | AdditiveArithmetic |
| some SignedInteger | 9 | file://:0:0:0:0 | ExpressibleByIntegerLiteral |
| some SignedInteger | 10 | file://:0:0:0:0 | Comparable |

View File

@@ -0,0 +1,7 @@
// generated by codegen/codegen.py
import codeql.swift.elements
import TestUtils
from OpaqueTypeArchetypeType x, int index
where toBeTested(x) and not x.isUnknown()
select x, index, x.getProtocol(index)

View File

@@ -0,0 +1,7 @@
// generated by codegen/codegen.py
import codeql.swift.elements
import TestUtils
from OpaqueTypeArchetypeType x
where toBeTested(x) and not x.isUnknown()
select x, x.getSuperclass()

View File

@@ -0,0 +1,13 @@
func foo() -> some SignedInteger { return 1 }
protocol P {}
func bar<T: P>(_ x: T) -> some P { return x }
class Base {}
func baz<T: Base>(_ x: T) -> some Base { return x }
class Generic<T: Equatable>: P {}
func bazz<T: Equatable>() -> some P { return Generic<T>() }

View File

@@ -218,7 +218,20 @@ class NominalTypeDecl(GenericTypeDecl, IterableDeclContext):
type: Type
class OpaqueTypeDecl(GenericTypeDecl):
pass
"""
A declaration of an opaque type, that is formally equivalent to a given type but abstracts it
away.
Such a declaration is implicitly given when a declaration is written with an opaque result type,
for example
```
func opaque() -> some SignedInteger { return 1 }
```
See https://docs.swift.org/swift-book/LanguageGuide/OpaqueTypes.html.
"""
naming_declaration: ValueDecl
opaque_generic_params: list["GenericTypeParamType"]
opaque_generic_params: list["GenericTypeParamType"]
class TypeAliasDecl(GenericTypeDecl):
pass
@@ -966,7 +979,10 @@ class NominalType(NominalOrBoundGenericNominalType):
pass
class OpaqueTypeArchetypeType(ArchetypeType):
pass
"""An opaque type, that is a type formally equivalent to an underlying type but abstracting it away.
See https://docs.swift.org/swift-book/LanguageGuide/OpaqueTypes.html."""
declaration: OpaqueTypeDecl
class OpenedArchetypeType(ArchetypeType):
pass