mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[Compile Time Constant Extraction] Extract Interpolated String Literals
This commit is contained in:
@@ -38,6 +38,9 @@ public:
|
||||
Enum,
|
||||
Type,
|
||||
KeyPath,
|
||||
FunctionCall,
|
||||
MemberReference,
|
||||
InterpolatedString,
|
||||
Runtime
|
||||
};
|
||||
|
||||
@@ -185,7 +188,7 @@ private:
|
||||
std::optional<std::vector<FunctionParameter>> Parameters;
|
||||
};
|
||||
|
||||
/// An type value representation
|
||||
/// A type value representation
|
||||
class TypeValue : public CompileTimeValue {
|
||||
public:
|
||||
TypeValue(swift::Type Type) : CompileTimeValue(ValueKind::Type), Type(Type) {}
|
||||
@@ -228,6 +231,68 @@ private:
|
||||
std::vector<Component> Components;
|
||||
};
|
||||
|
||||
/// A function call representation. This is for a function declaration such as
|
||||
/// let foo = bar(baz: "abc")
|
||||
class FunctionCallValue : public CompileTimeValue {
|
||||
public:
|
||||
FunctionCallValue(std::string Identifier,
|
||||
std::optional<std::vector<FunctionParameter>> Parameters)
|
||||
: CompileTimeValue(ValueKind::FunctionCall), Identifier(Identifier),
|
||||
Parameters(Parameters) {}
|
||||
|
||||
std::string getIdentifier() const { return Identifier; }
|
||||
std::optional<std::vector<FunctionParameter>> getParameters() const {
|
||||
return Parameters;
|
||||
}
|
||||
|
||||
static bool classof(const CompileTimeValue *T) {
|
||||
return T->getKind() == ValueKind::FunctionCall;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string Identifier;
|
||||
std::optional<std::vector<FunctionParameter>> Parameters;
|
||||
};
|
||||
|
||||
/// A member reference representation such as
|
||||
/// let foo = MyStruct.bar
|
||||
class MemberReferenceValue : public CompileTimeValue {
|
||||
public:
|
||||
MemberReferenceValue(swift::Type BaseType, std::string MemberLabel)
|
||||
: CompileTimeValue(ValueKind::MemberReference), BaseType(BaseType),
|
||||
MemberLabel(MemberLabel) {}
|
||||
|
||||
std::string getMemberLabel() const { return MemberLabel; }
|
||||
swift::Type getBaseType() const { return BaseType; }
|
||||
|
||||
static bool classof(const CompileTimeValue *T) {
|
||||
return T->getKind() == ValueKind::MemberReference;
|
||||
}
|
||||
|
||||
private:
|
||||
swift::Type BaseType;
|
||||
std::string MemberLabel;
|
||||
};
|
||||
|
||||
/// A representation of an Interpolated String Literal
|
||||
class InterpolatedStringLiteralValue : public CompileTimeValue {
|
||||
public:
|
||||
InterpolatedStringLiteralValue(
|
||||
std::vector<std::shared_ptr<CompileTimeValue>> Segments)
|
||||
: CompileTimeValue(ValueKind::InterpolatedString), Segments(Segments) {}
|
||||
|
||||
std::vector<std::shared_ptr<CompileTimeValue>> getSegments() const {
|
||||
return Segments;
|
||||
}
|
||||
|
||||
static bool classof(const CompileTimeValue *T) {
|
||||
return T->getKind() == ValueKind::InterpolatedString;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<CompileTimeValue>> Segments;
|
||||
};
|
||||
|
||||
/// A representation of an arbitrary value that does not fall under
|
||||
/// any of the above categories.
|
||||
class RuntimeValue : public CompileTimeValue {
|
||||
|
||||
@@ -286,13 +286,25 @@ static std::shared_ptr<CompileTimeValue> extractCompileTimeValue(Expr *expr) {
|
||||
|
||||
case ExprKind::Call: {
|
||||
auto callExpr = cast<CallExpr>(expr);
|
||||
if (callExpr->getFn()->getKind() == ExprKind::ConstructorRefCall) {
|
||||
auto functionKind = callExpr->getFn()->getKind();
|
||||
|
||||
if (functionKind == ExprKind::DeclRef) {
|
||||
auto declRefExpr = cast<DeclRefExpr>(callExpr->getFn());
|
||||
auto caseName =
|
||||
declRefExpr->getDecl()->getName().getBaseIdentifier().str().str();
|
||||
|
||||
std::vector<FunctionParameter> parameters =
|
||||
extractFunctionArguments(callExpr->getArgs());
|
||||
return std::make_shared<FunctionCallValue>(caseName, parameters);
|
||||
}
|
||||
|
||||
if (functionKind == ExprKind::ConstructorRefCall) {
|
||||
std::vector<FunctionParameter> parameters =
|
||||
extractFunctionArguments(callExpr->getArgs());
|
||||
return std::make_shared<InitCallValue>(callExpr->getType(), parameters);
|
||||
}
|
||||
|
||||
if (callExpr->getFn()->getKind() == ExprKind::DotSyntaxCall) {
|
||||
if (functionKind == ExprKind::DotSyntaxCall) {
|
||||
auto dotSyntaxCallExpr = cast<DotSyntaxCallExpr>(callExpr->getFn());
|
||||
auto fn = dotSyntaxCallExpr->getFn();
|
||||
if (fn->getKind() == ExprKind::DeclRef) {
|
||||
@@ -408,6 +420,38 @@ static std::shared_ptr<CompileTimeValue> extractCompileTimeValue(Expr *expr) {
|
||||
return extractCompileTimeValue(injectIntoOptionalExpr->getSubExpr());
|
||||
}
|
||||
|
||||
case ExprKind::Load: {
|
||||
auto loadExpr = cast<LoadExpr>(expr);
|
||||
return extractCompileTimeValue(loadExpr->getSubExpr());
|
||||
}
|
||||
|
||||
case ExprKind::MemberRef: {
|
||||
auto memberExpr = cast<MemberRefExpr>(expr);
|
||||
if (isa<TypeExpr>(memberExpr->getBase())) {
|
||||
auto baseTypeExpr = cast<TypeExpr>(memberExpr->getBase());
|
||||
auto label = memberExpr->getDecl().getDecl()->getBaseIdentifier().str();
|
||||
return std::make_shared<MemberReferenceValue>(
|
||||
baseTypeExpr->getInstanceType(), label.str());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ExprKind::InterpolatedStringLiteral: {
|
||||
auto interpolatedStringExpr = cast<InterpolatedStringLiteralExpr>(expr);
|
||||
auto tapExpr = interpolatedStringExpr->getAppendingExpr();
|
||||
auto &Ctx = tapExpr->getVar()->getASTContext();
|
||||
|
||||
std::vector<std::shared_ptr<CompileTimeValue>> segments;
|
||||
interpolatedStringExpr->forEachSegment(
|
||||
Ctx, [&](bool isInterpolation, CallExpr *segment) -> void {
|
||||
auto arg = segment->getArgs()->get(0);
|
||||
auto expr = arg.getExpr();
|
||||
segments.push_back(extractCompileTimeValue(expr));
|
||||
});
|
||||
|
||||
return std::make_shared<InterpolatedStringLiteralValue>(segments);
|
||||
}
|
||||
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
@@ -725,23 +769,69 @@ void writeValue(llvm::json::OStream &JSON,
|
||||
break;
|
||||
}
|
||||
|
||||
case CompileTimeValue::KeyPath: {
|
||||
auto keyPathValue = cast<KeyPathValue>(value);
|
||||
JSON.attribute("valueKind", "KeyPath");
|
||||
JSON.attributeObject("value", [&]() {
|
||||
JSON.attribute("path", keyPathValue->getPath());
|
||||
JSON.attribute("rootType", toFullyQualifiedTypeNameString(keyPathValue->getRootType()));
|
||||
JSON.attributeArray("components", [&] {
|
||||
auto components = keyPathValue->getComponents();
|
||||
for (auto c : components) {
|
||||
case CompileTimeValue::ValueKind::KeyPath: {
|
||||
auto keyPathValue = cast<KeyPathValue>(value);
|
||||
JSON.attribute("valueKind", "KeyPath");
|
||||
JSON.attributeObject("value", [&]() {
|
||||
JSON.attribute("path", keyPathValue->getPath());
|
||||
JSON.attribute("rootType", toFullyQualifiedTypeNameString(
|
||||
keyPathValue->getRootType()));
|
||||
JSON.attributeArray("components", [&] {
|
||||
auto components = keyPathValue->getComponents();
|
||||
for (auto c : components) {
|
||||
JSON.object([&] {
|
||||
JSON.attribute("label", c.Label);
|
||||
JSON.attribute("type", toFullyQualifiedTypeNameString(c.Type));
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case CompileTimeValue::ValueKind::FunctionCall: {
|
||||
auto functionCallValue = cast<FunctionCallValue>(value);
|
||||
JSON.attribute("valueKind", "FunctionCall");
|
||||
JSON.attributeObject("value", [&]() {
|
||||
JSON.attribute("name", functionCallValue->getIdentifier());
|
||||
if (functionCallValue->getParameters().has_value()) {
|
||||
auto params = functionCallValue->getParameters().value();
|
||||
JSON.attributeArray("arguments", [&] {
|
||||
for (auto FP : params) {
|
||||
JSON.object([&] {
|
||||
JSON.attribute("label", c.Label);
|
||||
JSON.attribute("type", toFullyQualifiedTypeNameString(c.Type));
|
||||
JSON.attribute("label", FP.Label);
|
||||
JSON.attribute("type", toFullyQualifiedTypeNameString(FP.Type));
|
||||
writeValue(JSON, FP.Value);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case CompileTimeValue::ValueKind::MemberReference: {
|
||||
auto memberReferenceValue = cast<MemberReferenceValue>(value);
|
||||
JSON.attribute("valueKind", "MemberReference");
|
||||
JSON.attributeObject("value", [&]() {
|
||||
JSON.attribute("baseType", toFullyQualifiedTypeNameString(
|
||||
memberReferenceValue->getBaseType()));
|
||||
JSON.attribute("memberLabel", memberReferenceValue->getMemberLabel());
|
||||
});
|
||||
break;
|
||||
}
|
||||
case CompileTimeValue::ValueKind::InterpolatedString: {
|
||||
auto interpolatedStringValue = cast<InterpolatedStringLiteralValue>(value);
|
||||
JSON.attribute("valueKind", "InterpolatedStringLiteral");
|
||||
JSON.attributeObject("value", [&]() {
|
||||
JSON.attributeArray("segments", [&] {
|
||||
auto segments = interpolatedStringValue->getSegments();
|
||||
for (auto s : segments) {
|
||||
JSON.object([&] { writeValue(JSON, s); });
|
||||
}
|
||||
});
|
||||
break;
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case CompileTimeValue::ValueKind::Runtime: {
|
||||
|
||||
@@ -110,7 +110,24 @@ public struct Bat {
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "label": "fuz",
|
||||
// CHECK-NEXT: "type": "Swift.Int",
|
||||
// CHECK-NEXT: "valueKind": "Runtime"
|
||||
// CHECK-NEXT: "valueKind": "FunctionCall",
|
||||
// CHECK-NEXT: "value": {
|
||||
// CHECK-NEXT: "name": "adder",
|
||||
// CHECK-NEXT: "arguments": [
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "label": "",
|
||||
// CHECK-NEXT: "type": "Swift.Int",
|
||||
// CHECK-NEXT: "valueKind": "RawLiteral",
|
||||
// CHECK-NEXT: "value": "2"
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "label": "",
|
||||
// CHECK-NEXT: "type": "Swift.Int",
|
||||
// CHECK-NEXT: "valueKind": "RawLiteral",
|
||||
// CHECK-NEXT: "value": "3"
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
@@ -123,7 +140,24 @@ public struct Bat {
|
||||
// CHECK-NEXT: "isComputed": "false",
|
||||
// CHECK-NEXT: "file": "{{.*}}test{{/|\\\\}}ConstExtraction{{/|\\\\}}ExtractCalls.swift",
|
||||
// CHECK-NEXT: "line": 15,
|
||||
// CHECK-NEXT: "valueKind": "Runtime"
|
||||
// CHECK-NEXT: "valueKind": "FunctionCall",
|
||||
// CHECK-NEXT: "value": {
|
||||
// CHECK-NEXT: "name": "adder",
|
||||
// CHECK-NEXT: "arguments": [
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "label": "",
|
||||
// CHECK-NEXT: "type": "Swift.Int",
|
||||
// CHECK-NEXT: "valueKind": "RawLiteral",
|
||||
// CHECK-NEXT: "value": "2"
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "label": "",
|
||||
// CHECK-NEXT: "type": "Swift.Int",
|
||||
// CHECK-NEXT: "valueKind": "RawLiteral",
|
||||
// CHECK-NEXT: "value": "3"
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "label": "init4",
|
||||
|
||||
@@ -48,7 +48,17 @@ public struct Enums: MyProto {
|
||||
// CHECK-NEXT: "mangledTypeName": "n/a - deprecated",
|
||||
// CHECK-NEXT: "isStatic": "false",
|
||||
// CHECK-NEXT: "isComputed": "true",
|
||||
// CHECK-NEXT: "valueKind": "Runtime"
|
||||
// CHECK-NEXT: "valueKind": "FunctionCall",
|
||||
// CHECK-NEXT: "value": {
|
||||
// CHECK-NEXT: "name": "_hashValue",
|
||||
// CHECK-NEXT: "arguments": [
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "label": "for",
|
||||
// CHECK-NEXT: "type": "ExtractEnums.SimpleEnum",
|
||||
// CHECK-NEXT: "valueKind": "Runtime"
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ],
|
||||
// CHECK-NEXT: "cases": [
|
||||
|
||||
135
test/ConstExtraction/ExtractInterpolatedStringLiterals.swift
Normal file
135
test/ConstExtraction/ExtractInterpolatedStringLiterals.swift
Normal file
@@ -0,0 +1,135 @@
|
||||
// RUN: %empty-directory(%t)
|
||||
// RUN: echo "[MyProto]" > %t/protocols.json
|
||||
|
||||
// RUN: %target-swift-frontend -typecheck -emit-const-values-path %t/ExtractInterpolatedStringLiterals.swiftconstvalues -const-gather-protocols-file %t/protocols.json -primary-file %s
|
||||
// RUN: cat %t/ExtractInterpolatedStringLiterals.swiftconstvalues 2>&1 | %FileCheck %s
|
||||
|
||||
protocol MyProto {}
|
||||
|
||||
func generateString(input: String) -> String {
|
||||
return "function" + input
|
||||
}
|
||||
|
||||
public struct Internal: MyProto {
|
||||
static var internalTitle: String = "Inner title"
|
||||
}
|
||||
|
||||
public struct MyType {
|
||||
var nested: NestedOne
|
||||
|
||||
struct NestedOne {
|
||||
var foo: String
|
||||
}
|
||||
}
|
||||
|
||||
public struct External: MyProto {
|
||||
var interpolatedTitle = "Start Interpolation with Member Reference: \(Internal.internalTitle). Followed By Function Call: \(generateString(input: "test")). End with KeyPath: \(\MyType.nested.foo)."
|
||||
}
|
||||
|
||||
// CHECK: [
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "typeName": "ExtractInterpolatedStringLiterals.Internal",
|
||||
// CHECK-NEXT: "mangledTypeName": "33ExtractInterpolatedStringLiterals8InternalV",
|
||||
// CHECK-NEXT: "kind": "struct",
|
||||
// CHECK-NEXT: "file": "{{.*}}test{{/|\\\\}}ConstExtraction{{/|\\\\}}ExtractInterpolatedStringLiterals.swift",
|
||||
// CHECK-NEXT: "line": 13,
|
||||
// CHECK-NEXT: "conformances": [
|
||||
// CHECK-NEXT: "ExtractInterpolatedStringLiterals.MyProto"
|
||||
// CHECK-NEXT: ],
|
||||
// CHECK-NEXT: "associatedTypeAliases": [],
|
||||
// CHECK-NEXT: "properties": [
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "label": "internalTitle",
|
||||
// CHECK-NEXT: "type": "Swift.String",
|
||||
// CHECK-NEXT: "mangledTypeName": "n/a - deprecated",
|
||||
// CHECK-NEXT: "isStatic": "true",
|
||||
// CHECK-NEXT: "isComputed": "false",
|
||||
// CHECK-NEXT: "file": "{{.*}}test{{/|\\\\}}ConstExtraction{{/|\\\\}}ExtractInterpolatedStringLiterals.swift",
|
||||
// CHECK-NEXT: "line": 14,
|
||||
// CHECK-NEXT: "valueKind": "RawLiteral",
|
||||
// CHECK-NEXT: "value": "Inner title"
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "typeName": "ExtractInterpolatedStringLiterals.External",
|
||||
// CHECK-NEXT: "mangledTypeName": "33ExtractInterpolatedStringLiterals8ExternalV",
|
||||
// CHECK-NEXT: "kind": "struct",
|
||||
// CHECK-NEXT: "file": "{{.*}}test{{/|\\\\}}ConstExtraction{{/|\\\\}}ExtractInterpolatedStringLiterals.swift",
|
||||
// CHECK-NEXT: "line": 25,
|
||||
// CHECK-NEXT: "conformances": [
|
||||
// CHECK-NEXT: "ExtractInterpolatedStringLiterals.MyProto"
|
||||
// CHECK-NEXT: ],
|
||||
// CHECK-NEXT: "associatedTypeAliases": [],
|
||||
// CHECK-NEXT: "properties": [
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "label": "interpolatedTitle",
|
||||
// CHECK-NEXT: "type": "Swift.String",
|
||||
// CHECK-NEXT: "mangledTypeName": "n/a - deprecated",
|
||||
// CHECK-NEXT: "isStatic": "false",
|
||||
// CHECK-NEXT: "isComputed": "false",
|
||||
// CHECK-NEXT: "file": "{{.*}}test{{/|\\\\}}ConstExtraction{{/|\\\\}}ExtractInterpolatedStringLiterals.swift",
|
||||
// CHECK-NEXT: "line": 26,
|
||||
// CHECK-NEXT: "valueKind": "InterpolatedStringLiteral",
|
||||
// CHECK-NEXT: "value": {
|
||||
// CHECK-NEXT: "segments": [
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "valueKind": "RawLiteral",
|
||||
// CHECK-NEXT: "value": "Start Interpolation with Member Reference: "
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "valueKind": "MemberReference",
|
||||
// CHECK-NEXT: "value": {
|
||||
// CHECK-NEXT: "baseType": "ExtractInterpolatedStringLiterals.Internal",
|
||||
// CHECK-NEXT: "memberLabel": "internalTitle"
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "valueKind": "RawLiteral",
|
||||
// CHECK-NEXT: "value": ". Followed By Function Call: "
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "valueKind": "FunctionCall",
|
||||
// CHECK-NEXT: "value": {
|
||||
// CHECK-NEXT: "name": "generateString",
|
||||
// CHECK-NEXT: "arguments": [
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "label": "input",
|
||||
// CHECK-NEXT: "type": "Swift.String",
|
||||
// CHECK-NEXT: "valueKind": "RawLiteral",
|
||||
// CHECK-NEXT: "value": "test"
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "valueKind": "RawLiteral",
|
||||
// CHECK-NEXT: "value": ". End with KeyPath: "
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "valueKind": "KeyPath",
|
||||
// CHECK-NEXT: "value": {
|
||||
// CHECK-NEXT: "path": "nested.foo",
|
||||
// CHECK-NEXT: "rootType": "ExtractInterpolatedStringLiterals.MyType",
|
||||
// CHECK-NEXT: "components": [
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "label": "nested",
|
||||
// CHECK-NEXT: "type": "ExtractInterpolatedStringLiterals.MyType.NestedOne"
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "label": "foo",
|
||||
// CHECK-NEXT: "type": "Swift.String"
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "valueKind": "RawLiteral",
|
||||
// CHECK-NEXT: "value": "."
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ]
|
||||
Reference in New Issue
Block a user