1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
|
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#if swift(>=6)
internal import SwiftDiagnostics
public import SwiftSyntax
#else
import SwiftDiagnostics
import SwiftSyntax
#endif
enum DeclReferenceError: DiagnosticMessage {
case nonLiteral
var message: String {
switch self {
case .nonLiteral:
return "only literals are permitted"
}
}
var diagnosticID: MessageID {
.init(domain: "SwiftMacros", id: "\(self)")
}
var severity: DiagnosticSeverity {
.error
}
}
/// Check sub-expressions to ensure all expressions are literals, and call
/// `diagnoseNonLiteral` for all other expressions.
class OnlyLiteralExprChecker: SyntaxAnyVisitor {
var diagnostics: [Diagnostic] = []
init() {
super.init(viewMode: .fixedUp)
}
// Integer literals
override func visit(_ node: IntegerLiteralExprSyntax) -> SyntaxVisitorContinueKind {
.visitChildren
}
// Floating point literals
override func visit(_ node: FloatLiteralExprSyntax) -> SyntaxVisitorContinueKind {
.visitChildren
}
// Negative numbers
override func visit(_ node: PrefixOperatorExprSyntax) -> SyntaxVisitorContinueKind {
switch node.operator.tokenKind {
case .prefixOperator("-")
// only allow negation on numbers, not other literal types
where node.expression.is(IntegerLiteralExprSyntax.self)
|| node.expression.is(FloatLiteralExprSyntax.self):
return .visitChildren
default:
return diagnoseNonLiteral(node)
}
}
// Bool literals
override func visit(_ node: BooleanLiteralExprSyntax) -> SyntaxVisitorContinueKind {
.visitChildren
}
// nil literals
override func visit(_ node: NilLiteralExprSyntax) -> SyntaxVisitorContinueKind {
.visitChildren
}
// String literals
override func visit(_ node: StringLiteralExprSyntax) -> SyntaxVisitorContinueKind {
.visitChildren
}
// String interpolation
override func visit(_ node: StringLiteralSegmentListSyntax) -> SyntaxVisitorContinueKind {
guard node.count == 1,
case .stringSegment = node.first!
else {
return diagnoseNonLiteral(node)
}
return .visitChildren
}
// Array literals
override func visit(_ node: ArrayExprSyntax) -> SyntaxVisitorContinueKind {
.visitChildren
}
// Dictionary literals
override func visit(_ node: DictionaryExprSyntax) -> SyntaxVisitorContinueKind {
.visitChildren
}
// Tuple literals
override func visit(_ node: TupleExprSyntax) -> SyntaxVisitorContinueKind {
.visitChildren
}
// Regex literals
override func visit(_ node: RegexLiteralExprSyntax) -> SyntaxVisitorContinueKind {
.visitChildren
}
// Macro uses.
override func visit(_ node: MacroExpansionExprSyntax) -> SyntaxVisitorContinueKind {
.visitChildren
}
// References to declarations.
override func visit(_ node: DeclReferenceExprSyntax) -> SyntaxVisitorContinueKind {
return diagnoseNonLiteral(node)
}
override func visitAny(_ node: Syntax) -> SyntaxVisitorContinueKind {
if node.is(ExprSyntax.self) {
// We have an expression that is not one of the allowed forms, so
// diagnose it.
return diagnoseNonLiteral(node)
}
return .visitChildren
}
func diagnoseNonLiteral(_ node: some SyntaxProtocol) -> SyntaxVisitorContinueKind {
diagnostics.append(
Diagnostic(
node: node,
message: DeclReferenceError.nonLiteral
)
)
return .skipChildren
}
}
extension MacroExpansionExprSyntax {
/// For compiler to check a macro expression used as default argument.
///
/// Only literals are permitted as arguments to these expressions.
///
/// If there are diagnostics, they will be wrapped into an error and thrown.
@_spi(Compiler)
public func checkDefaultArgumentMacroExpression() throws {
let visitor = OnlyLiteralExprChecker()
visitor.walk(arguments)
if !visitor.diagnostics.isEmpty {
throw DiagnosticsError(diagnostics: visitor.diagnostics)
}
}
}
|