diff --git a/unified/BUILD.bazel b/unified/BUILD.bazel index 55bf87e8e38b..52f720f4cb12 100644 --- a/unified/BUILD.bazel +++ b/unified/BUILD.bazel @@ -45,7 +45,17 @@ codeql_pkg_files( otherwise = ["//unified/extractor"], win64 = ["//unified/extractor-unsupported-os:extractor"], ), - prefix = "tools/{CODEQL_PLATFORM}", + prefix = "{CODEQL_PLATFORM}", +) + +pkg_filegroup( + name = "tools", + srcs = [ + ":extractor-arch", + "//unified/tools", + "//unified/tools/builtins", + ], + prefix = "tools", ) codeql_pack( @@ -53,7 +63,6 @@ codeql_pack( srcs = [ ":codeql-extractor-yml", ":dbscheme-group", - ":extractor-arch", - "//unified/tools", + ":tools", ], ) diff --git a/unified/extractor/src/extractor.rs b/unified/extractor/src/extractor.rs index 82bfe81219b5..8c180c854d1f 100644 --- a/unified/extractor/src/extractor.rs +++ b/unified/extractor/src/extractor.rs @@ -1,10 +1,10 @@ -use clap::Args; -use std::path::PathBuf; - use crate::languages; +use clap::Args; use codeql_extractor::extractor::desugaring; use codeql_extractor::trap; - +use std::path::Path; +use std::path::PathBuf; +use std::{env, fs}; #[derive(Args)] pub struct Options { /// Sets a custom source archive folder @@ -31,6 +31,33 @@ pub fn run(options: Options) -> std::io::Result<()> { lang.prefix = "unified"; } + let scratch_dir = std::env::var("CODEQL_EXTRACTOR_UNIFIED_SCRATCH_DIR") + .expect("failed to read CODEQL_EXTRACTOR_UNIFIED_SCRATCH_DIR environment variable"); + let builtins_path = env::var("CODEQL_EXTRACTOR_UNIFIED_ROOT") + .map(|path| Path::new(&path).join("tools").join("builtins")) + .expect("failed to read CODEQL_EXTRACTOR_UNIFIED_ROOT environment variable"); + let builtins_dir = fs::read_dir(builtins_path).expect("failed to read builtins directory"); + let mut builtins_list = PathBuf::new(); + builtins_list.push(scratch_dir.clone()); + builtins_list.push("builtins"); + builtins_list.set_extension("list"); + + let mut builtins_list_file = fs::OpenOptions::new() + .create_new(true) + .write(true) + .open(&builtins_list) + .expect("failed to open file list"); + for entry in builtins_dir { + let entry = entry.expect("failed to read builtins directory"); + let path = entry.path(); + if path.extension().is_some_and(|ext| ext == "swift") { + use std::io::Write; + writeln!(builtins_list_file, "{}", path.display()) + .expect("failed to write to file list"); + } + } + drop(builtins_list_file); + let extractor = desugaring::Extractor { prefix: "unified".to_string(), languages, @@ -39,7 +66,7 @@ pub fn run(options: Options) -> std::io::Result<()> { "CODEQL_EXTRACTOR_UNIFIED_OPTION_TRAP_COMPRESSION", ), source_archive_dir: options.source_archive_dir, - file_lists: vec![options.file_list], + file_lists: vec![options.file_list, builtins_list], }; extractor.run() diff --git a/unified/ql/lib/codeql/files/FileSystem.qll b/unified/ql/lib/codeql/files/FileSystem.qll index 897a855612be..59c7900c550f 100644 --- a/unified/ql/lib/codeql/files/FileSystem.qll +++ b/unified/ql/lib/codeql/files/FileSystem.qll @@ -37,7 +37,7 @@ module Folder = Impl::Folder; /** A file. */ class File extends Container, Impl::File { /** Holds if this file was extracted from ordinary source code. */ - predicate fromSource() { any() } + predicate fromSource() { exists(this.getRelativePath()) } /** * Gets the number of lines containing code in this file. This value diff --git a/unified/ql/lib/codeql/unified/internal/Builtins.qll b/unified/ql/lib/codeql/unified/internal/Builtins.qll new file mode 100644 index 000000000000..099ced74d2bb --- /dev/null +++ b/unified/ql/lib/codeql/unified/internal/Builtins.qll @@ -0,0 +1,30 @@ +/** + * Provides classes for builtins. + */ + +private import unified + +/** The folder containing builtins. */ +class BuiltinsFolder extends Folder { + BuiltinsFolder() { + not exists(this.getRelativePath()) and + this.getBaseName() = "builtins" and + this.getParentContainer().getBaseName() = "tools" + } +} + +private class BuiltinsTypesFile extends File { + BuiltinsTypesFile() { + this.getBaseName() = "types.swift" and + this.getParentContainer() instanceof BuiltinsFolder + } +} + +/** + * A builtin type, such as `Bool` and `String`. + * + * Builtin types are represented as class-like declarations. + */ +class BuiltinClassLikeDeclaration extends ClassLikeDeclaration { + BuiltinClassLikeDeclaration() { this.getFile() instanceof BuiltinsTypesFile } +} diff --git a/unified/ql/lib/codeql/unified/internal/ControlFlowGraph.qll b/unified/ql/lib/codeql/unified/internal/ControlFlowGraph.qll index 019722484d1a..1a5180c14db0 100644 --- a/unified/ql/lib/codeql/unified/internal/ControlFlowGraph.qll +++ b/unified/ql/lib/codeql/unified/internal/ControlFlowGraph.qll @@ -42,7 +42,9 @@ private module Ast implements AstSig { Callable getEnclosingCallable(AstNode node) { result = node.getEnclosingCallable() } - class Callable = U::Callable; + class Callable extends U::Callable { + Callable() { this.fromSource() } + } AstNode callableGetBody(Callable c) { result = c.getBody() } diff --git a/unified/ql/lib/codeql/unified/internal/FacadeAst.qll b/unified/ql/lib/codeql/unified/internal/FacadeAst.qll index 54efc88a473d..7b431214294a 100644 --- a/unified/ql/lib/codeql/unified/internal/FacadeAst.qll +++ b/unified/ql/lib/codeql/unified/internal/FacadeAst.qll @@ -15,6 +15,9 @@ module Unified { /** Gets the file containing this AST node. */ File getFile() { result = this.getLocation().getFile() } + /** Holds if this AST node comes from ordinary source code. */ + predicate fromSource() { this.getFile().fromSource() } + /** Holds if this AST node has a modifier with the given text. */ predicate hasModifier(string text) { exists(Modifier mod | diff --git a/unified/ql/test/library-tests/BasicTest/test.ql b/unified/ql/test/library-tests/BasicTest/test.ql index fa3c26183b08..165cb1be9a0e 100644 --- a/unified/ql/test/library-tests/BasicTest/test.ql +++ b/unified/ql/test/library-tests/BasicTest/test.ql @@ -1,6 +1,8 @@ import unified -query predicate identifier(Identifier node, string value) { value = node.getValue() } +query predicate identifier(Identifier node, string value) { + node.fromSource() and value = node.getValue() +} query predicate namedPattern(NamedPattern node, string value) { value = node.getName() } diff --git a/unified/ql/test/library-tests/comments/comments.ql b/unified/ql/test/library-tests/comments/comments.ql index db64ff737a71..a17b318cff6e 100644 --- a/unified/ql/test/library-tests/comments/comments.ql +++ b/unified/ql/test/library-tests/comments/comments.ql @@ -1,3 +1,3 @@ import unified -query predicate comments(Comment c, string text) { text = c.getCommentText() } +query predicate comments(Comment c, string text) { c.fromSource() and text = c.getCommentText() } diff --git a/unified/ql/test/library-tests/definitions/test.swift b/unified/ql/test/library-tests/definitions/test.swift index 5bbbd59e21e8..d2597898f4f2 100644 --- a/unified/ql/test/library-tests/definitions/test.swift +++ b/unified/ql/test/library-tests/definitions/test.swift @@ -11,8 +11,8 @@ func test() { let local = 2 // name=local2 local // $ definition=local2 Derived.member // $ definition=Derived definition=Base.member - let _: Derived.Nested? // $ definition=Derived definition=Base.Nested + let _: Derived.Nested? // $ definition=Derived definition=Base.Nested definition=Optional } typealias Alias = Derived // $ definition=Derived -let _: Alias.Nested? // $ definition=Alias definition=Base.Nested +let _: Alias.Nested? // $ definition=Alias definition=Base.Nested definition=Optional diff --git a/unified/ql/test/library-tests/static-name-binding/explicit-instance-field-access.swift b/unified/ql/test/library-tests/static-name-binding/explicit-instance-field-access.swift index 42bf26038f17..a24ed7466903 100644 --- a/unified/ql/test/library-tests/static-name-binding/explicit-instance-field-access.swift +++ b/unified/ql/test/library-tests/static-name-binding/explicit-instance-field-access.swift @@ -15,7 +15,7 @@ private class A { return self.y // $ not handled by static name binding } - class func z() -> Int { // name=A.type.z + class func z() -> Int { // $ access=Int // name=A.type.z return 789 } @@ -37,7 +37,7 @@ private class B : A { // $ access=A return self.y // $ not handled by static name binding } - class func z() -> Int { // name=B.type.z + class func z() -> Int { // $ access=Int // name=B.type.z return 789 } diff --git a/unified/tools/BUILD.bazel b/unified/tools/BUILD.bazel index a4b4baed225c..103ded415c2d 100644 --- a/unified/tools/BUILD.bazel +++ b/unified/tools/BUILD.bazel @@ -6,6 +6,5 @@ codeql_pkg_files( "BUILD.bazel", ], exes = glob(["**/*"]), - prefix = "tools", visibility = ["//unified:__pkg__"], ) diff --git a/unified/tools/builtins/BUILD.bazel b/unified/tools/builtins/BUILD.bazel new file mode 100644 index 000000000000..666db59c0c12 --- /dev/null +++ b/unified/tools/builtins/BUILD.bazel @@ -0,0 +1,8 @@ +load("//misc/bazel:pkg.bzl", "codeql_pkg_files") + +codeql_pkg_files( + name = "builtins", + srcs = glob(["*.swift"]), + prefix = "builtins", + visibility = ["//unified:__subpackages__"], +) diff --git a/unified/tools/builtins/types.swift b/unified/tools/builtins/types.swift new file mode 100644 index 000000000000..7a7d2e0c8c6a --- /dev/null +++ b/unified/tools/builtins/types.swift @@ -0,0 +1,207 @@ +// Provides built-in Swift types + +struct Bool { } + +struct Int { } + +struct String { } + +struct Character { } + +struct Substring { } + +struct Int8 { } + +struct Int16 { } + +struct Int32 { } + +struct Int64 { } + +struct UInt { } + +struct UInt8 { } + +struct UInt16 { } + +struct UInt32 { } + +struct UInt64 { } + +struct Float16 { } + +struct Float { } + +struct Double { } + +struct Float80 { } + +struct Array { } + +struct Dictionary { } + +struct Set { } + +enum Optional { + case none + case some(Wrapped) +} + +enum Result { + case success(Success) + case failure(Failure) +} + +struct Range { } + +struct ClosedRange { } + +struct PartialRangeFrom { } + +struct PartialRangeThrough { } + +struct PartialRangeUpTo { } + +enum Never { } + +struct UnsafePointer { } + +struct UnsafeMutablePointer { } + +struct UnsafeRawPointer { } + +struct UnsafeMutableRawPointer { } + +struct UnsafeBufferPointer { } + +struct UnsafeMutableBufferPointer { } + +struct UnsafeRawBufferPointer { } + +struct UnsafeMutableRawBufferPointer { } + +struct AutoreleasingUnsafeMutablePointer { } + +struct OpaquePointer { } + +struct Unmanaged { } + +struct Function { } // `Args` is always instantiated as a tuple type + +typealias Void = Tuple0 + +struct Tuple0 {} + +struct Tuple1 { + var _0: T0 +} + +struct Tuple2 { + var _0: T0 + var _1: T1 +} + +struct Tuple3 { + var _0: T0 + var _1: T1 + var _2: T2 +} + +struct Tuple4 { + var _0: T0 + var _1: T1 + var _2: T2 + var _3: T3 +} + +struct Tuple5 { + var _0: T0 + var _1: T1 + var _2: T2 + var _3: T3 + var _4: T4 +} + +struct Tuple6 { + var _0: T0 + var _1: T1 + var _2: T2 + var _3: T3 + var _4: T4 + var _5: T5 +} + +struct Tuple7 { + var _0: T0 + var _1: T1 + var _2: T2 + var _3: T3 + var _4: T4 + var _5: T5 + var _6: T6 +} + +struct Tuple8 { + var _0: T0 + var _1: T1 + var _2: T2 + var _3: T3 + var _4: T4 + var _5: T5 + var _6: T6 + var _7: T7 +} + +struct Tuple9 { + var _0: T0 + var _1: T1 + var _2: T2 + var _3: T3 + var _4: T4 + var _5: T5 + var _6: T6 + var _7: T7 + var _8: T8 +} + +struct Tuple10 { + var _0: T0 + var _1: T1 + var _2: T2 + var _3: T3 + var _4: T4 + var _5: T5 + var _6: T6 + var _7: T7 + var _8: T8 + var _9: T9 +} + +struct Tuple11 { + var _0: T0 + var _1: T1 + var _2: T2 + var _3: T3 + var _4: T4 + var _5: T5 + var _6: T6 + var _7: T7 + var _8: T8 + var _9: T9 + var _10: T10 +} + +struct Tuple12 { + var _0: T0 + var _1: T1 + var _2: T2 + var _3: T3 + var _4: T4 + var _5: T5 + var _6: T6 + var _7: T7 + var _8: T8 + var _9: T9 + var _10: T10 + var _11: T11 +}