Files
swift-mirror/test/Profiler/coverage_macros.swift
Hamish Knight 98f0200f64 [Coverage] Avoid recording regions from macro expansions
Ignore any regions recorded while inside a macro
expansion, but account for any control flow that
may have happened such that the exit count is
correctly adjusted. This allows e.g throwing function
calls to behave correctly within the expansion.

rdar://129081384
2024-06-03 12:14:56 +01:00

133 lines
6.5 KiB
Swift

// REQUIRES: swift_swift_parser
// RUN: %empty-directory(%t)
// RUN: %host-build-swift -swift-version 5 -emit-library -o %t/%target-library-name(MacroDefinition) -module-name=MacroDefinition %S/../Macros/Inputs/syntax_macro_definitions.swift -g -no-toolchain-stdlib-rpath
// RUN: %target-swift-frontend -emit-sil -emit-sorted-sil -Xllvm -sil-full-demangle -profile-generate -profile-coverage-mapping -module-name coverage_macros %s -load-plugin-library %t/%target-library-name(MacroDefinition) | %FileCheck %s --implicit-check-not sil_coverage_map
// RUN: %target-swift-frontend -emit-ir -profile-generate -profile-coverage-mapping -load-plugin-library %t/%target-library-name(MacroDefinition) %s
@attached(accessor)
macro accessViaStorage() = #externalMacro(module: "MacroDefinition", type: "AccessViaStorageMacro")
@attached(
member,
names: named(init), named(Storage), named(storage), named(getStorage()), named(method)
)
macro addMembers() = #externalMacro(module: "MacroDefinition", type: "AddMembers")
@freestanding(expression)
macro nestedDeclInExpr() -> () -> Void = #externalMacro(module: "MacroDefinition", type: "NestedDeclInExprMacro")
@freestanding(expression)
macro fnCall<T>(_: () throws -> T) -> T = #externalMacro(module: "MacroDefinition", type: "NullaryFunctionCallMacro")
@freestanding(expression)
macro fnCall<T>(_: () throws -> T, _: () throws -> T) -> (T, T) = #externalMacro(module: "MacroDefinition", type: "NullaryFunctionCallMacro")
@freestanding(expression)
macro id<T>(_: T) -> T = #externalMacro(module: "MacroDefinition", type: "TupleMacro")
// This needs to be matched up here due to the sorting of the SIL; just make
// sure the counter '2' is for the initialization.
// CHECK-LABEL: sil hidden [lazy_getter] [noinline] @$s15coverage_macros2S1V2z3SiSgvg : $@convention(method) (@inout S1) -> Optional<Int>
// CHECK: switch_enum {{%[0-9]+}} : $Optional<Optional<Int>>, case #Optional.some!enumelt: {{bb[0-9]}}, case #Optional.none!enumelt: [[INITBB:bb[0-9]]]
// CHECK: [[INITBB]]
// CHECK-NEXT: increment_profiler_counter 2
// This needs to be matched up here due to the sorting of the SIL; just make
// sure we emit the counter increments for the error branches.
// CHECK-LABEL: sil hidden @$s15coverage_macros5test2Si_SityKF
// CHECK: increment_profiler_counter 0,{{.*}}s15coverage_macros5test2Si_SityKF
// CHECK: {{bb[0-9]+}}({{%[0-9]+}} : $any Error):
// CHECK-NEXT: increment_profiler_counter 1,{{.*}}s15coverage_macros5test2Si_SityKF
// CHECK: {{bb[0-9]+}}({{%[0-9]+}} : $any Error):
// CHECK-NEXT: increment_profiler_counter 2,{{.*}}s15coverage_macros5test2Si_SityKF
// Note we use implicit-check-not, so this test ensures we don't emit
// coverage maps for the macro expansions.
// CHECK-LABEL: sil_coverage_map{{.*}}s15coverage_macros10throwingFnSiyKF
func throwingFn() throws -> Int { 0 }
struct S1 {
// CHECK-LABEL: sil_coverage_map{{.*}}variable initialization expression of coverage_macros.S1.x
var x: Int = 0
// CHECK-LABEL: sil_coverage_map{{.*}}variable initialization expression of coverage_macros.S1.y
var y: Int = 0
// CHECK-LABEL: sil_coverage_map{{.*}}variable initialization expression of coverage_macros.S1.z
var z: Int = #id(.random() ? 3 : 4) // CHECK-NEXT: [[@LINE]]:16 -> [[@LINE]]:38 : 0
// CHECK-LABEL: sil_coverage_map{{.*}}variable initialization expression of coverage_macros.S1.z1
var z1: Int? = try? #id(throwingFn()) // CHECK-NEXT: [[@LINE]]:18 -> [[@LINE]]:40 : 0
// CHECK-LABEL: sil_coverage_map{{.*}}variable initialization expression of coverage_macros.S1.z2
var z2: Int? = #id(try? throwingFn()) // CHECK-NEXT: [[@LINE]]:18 -> [[@LINE]]:40 : 0
// The counter for the initialization region is '2', see the match of s15coverage_macros2S1V2z3SiSgvg above.
// CHECK-LABEL: sil_coverage_map{{.*}}coverage_macros.S1.z3.getter
lazy var z3: Int? = #id(try? throwingFn()) // CHECK-NEXT: [[@LINE]]:23 -> [[@LINE]]:45 : 2
}
@addMembers
struct S2 {
// CHECK-LABEL: sil_coverage_map{{.*}}variable initialization expression of coverage_macros.S2.(_storage
private var _storage = S1()
@accessViaStorage
var x: Int
// No coverage map for the initializer, as it gets subsumed.
@accessViaStorage
var y: Int = 17
}
// CHECK-LABEL: sil_coverage_map{{.*}}s15coverage_macros3fooyyF
func foo() {
_ = #nestedDeclInExpr()
}
// For cases where control flow happens in the macro expansion, we drop
// all the regions that occur within the expansion, but account for any
// control flow changes when exiting the expansion.
//
// CHECK-LABEL: sil_coverage_map{{.*}}s15coverage_macros5test1yyKF
func test1() throws { // CHECK-NEXT: [[@LINE]]:21 -> [[@LINE+2]]:2 : 0
_ = try #fnCall(throwingFn) // CHECK-NEXT: [[@LINE]]:30 -> [[@LINE+1]]:2 : (0 - 1)
} // CHECK-NEXT: }
// CHECK-LABEL: sil_coverage_map{{.*}}s15coverage_macros5test2Si_SityKF
func test2() throws -> (Int, Int) { // CHECK-NEXT: [[@LINE]]:35 -> [[@LINE+3]]:2 : 0
let x = try #fnCall(throwingFn, throwingFn) // CHECK-NEXT: [[@LINE]]:46 -> [[@LINE+1]]:11 : ((0 - 1) - 2)
return x // CHECK-NEXT: }
}
// In this case the control flow is entirely contained within the macro, with
// the same exit count, so no change.
//
// CHECK-LABEL: sil_coverage_map{{.*}}s15coverage_macros5test3SiyF
func test3() -> Int { // CHECK-NEXT: [[@LINE]]:21 -> [[@LINE+3]]:2 : 0
let x = #id(.random() ? 3 : 4) // CHECK-NEXT: }
return x
}
// CHECK-LABEL: sil_coverage_map{{.*}}s15coverage_macros5test4Si_SityKF
func test4() throws -> (Int, Int) { // CHECK-NEXT: [[@LINE]]:35 -> [[@LINE+3]]:2 : 0
let x = try #id(#fnCall(throwingFn, throwingFn)) // CHECK-NEXT: [[@LINE]]:51 -> [[@LINE+1]]:11 : ((0 - 1) - 2)
return x // CHECK-NEXT: }
}
// 0: entry, 1: first else, 2: first error branch, 3: second else,
// 4: second error branch, 5: third else, 6: third error branch,
// 7: fourth error branch
// CHECK-LABEL: sil_coverage_map{{.*}}s15coverage_macros5test5Si_S2ityKF
func test5() throws -> (Int, Int, Int) { // CHECK-NEXT: [[@LINE]]:40 -> [[@LINE+6]]:2 : 0
let x = #id(.random() ? try throwingFn() : 4) // CHECK-NEXT: [[@LINE]]:48 -> [[@LINE+4]]:19 : (0 - 2)
let y = #id(.random() ? 5 : try throwingFn()) // CHECK-NEXT: [[@LINE]]:48 -> [[@LINE+3]]:19 : ((0 - 2) - 4)
let z = #id(.random() ? try throwingFn()
: try throwingFn()) // CHECK-NEXT: [[@LINE]]:44 -> [[@LINE+1]]:19 : ((((0 - 2) - 4) - 6) - 7)
return (x, y, z) // CHECK-NEXT: }
}