mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
ConstValues: record the values of const variable references in the side-car file
This commit is contained in:
@@ -491,6 +491,7 @@ public:
|
||||
return getSemanticsProvidingExpr()->getKind() == ExprKind::InOut;
|
||||
}
|
||||
|
||||
bool printConstExprValue(llvm::raw_ostream *OS) const;
|
||||
bool isSemanticallyConstExpr() const;
|
||||
|
||||
/// Returns false if this expression needs to be wrapped in parens when
|
||||
|
||||
@@ -2220,8 +2220,12 @@ static parseJsonEmit(SDKContext &Ctx, StringRef FileName) {
|
||||
return {std::move(FileBufOrErr.get()), Result};
|
||||
}
|
||||
enum class ConstKind: uint8_t {
|
||||
String = 0,
|
||||
Int,
|
||||
StringLiteral = 0,
|
||||
IntegerLiteral,
|
||||
FloatLiteral,
|
||||
BooleanLiteral,
|
||||
Array,
|
||||
Dictionary,
|
||||
};
|
||||
|
||||
struct ConstExprInfo {
|
||||
@@ -2230,9 +2234,11 @@ struct ConstExprInfo {
|
||||
unsigned offset = 0;
|
||||
unsigned length = 0;
|
||||
StringRef value;
|
||||
StringRef referencedD;
|
||||
ConstExprInfo(StringRef filePath, ConstKind kind, unsigned offset,
|
||||
unsigned length, StringRef value):
|
||||
filePath(filePath), kind(kind), offset(offset), length(length), value(value) {}
|
||||
unsigned length, StringRef value, StringRef referencedD):
|
||||
filePath(filePath), kind(kind), offset(offset), length(length), value(value),
|
||||
referencedD(referencedD) {}
|
||||
ConstExprInfo() = default;
|
||||
};
|
||||
|
||||
@@ -2242,7 +2248,7 @@ class ConstExtractor: public ASTWalker {
|
||||
SourceManager &SM;
|
||||
std::vector<ConstExprInfo> allConsts;
|
||||
|
||||
void record(Expr *E, ConstKind kind, StringRef Value) {
|
||||
void record(Expr *E, ConstKind kind, StringRef Value, StringRef ReferencedD) {
|
||||
auto startLoc = E->getStartLoc();
|
||||
// Asserts?
|
||||
if (startLoc.isInvalid())
|
||||
@@ -2254,14 +2260,70 @@ class ConstExtractor: public ASTWalker {
|
||||
auto length = SM.getByteDistance(startLoc, endLoc);
|
||||
auto file = SM.getIdentifierForBuffer(bufferId);
|
||||
auto offset = SM.getLocOffsetInBuffer(startLoc, bufferId);
|
||||
allConsts.emplace_back(file, kind, offset, length, Value);
|
||||
allConsts.emplace_back(file, kind, offset, length, Value, ReferencedD);
|
||||
}
|
||||
|
||||
void record(Expr *E, Expr *ValueProvider, StringRef ReferecedD = "") {
|
||||
std::string content;
|
||||
llvm::raw_string_ostream os(content);
|
||||
ValueProvider->printConstExprValue(&os);
|
||||
assert(!content.empty());
|
||||
auto buffered = SCtx.buffer(content);
|
||||
switch(ValueProvider->getKind()) {
|
||||
#define CASE(X) case ExprKind::X: record(E, ConstKind::X, buffered, ReferecedD); break;
|
||||
CASE(StringLiteral)
|
||||
CASE(IntegerLiteral)
|
||||
CASE(FloatLiteral)
|
||||
CASE(BooleanLiteral)
|
||||
CASE(Dictionary)
|
||||
CASE(Array)
|
||||
#undef CASE
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
StringRef getDeclName(Decl *D) {
|
||||
if (auto *VD = dyn_cast<ValueDecl>(D)) {
|
||||
std::string content;
|
||||
llvm::raw_string_ostream os(content);
|
||||
VD->getName().print(os);
|
||||
return SCtx.buffer(content);
|
||||
}
|
||||
return StringRef();
|
||||
}
|
||||
|
||||
bool handleSimpleReference(Expr *E) {
|
||||
assert(E);
|
||||
Decl *ReferencedDecl = nullptr;
|
||||
if (auto *MRE = dyn_cast<MemberRefExpr>(E)) {
|
||||
ReferencedDecl = MRE->getDecl().getDecl();
|
||||
} else if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
|
||||
ReferencedDecl = DRE->getDecl();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
assert(ReferencedDecl);
|
||||
if (auto *VAR = dyn_cast<VarDecl>(ReferencedDecl)) {
|
||||
if (!VAR->getAttrs().hasAttribute<CompileTimeConstAttr>()) {
|
||||
return false;
|
||||
}
|
||||
if (auto *PD = VAR->getParentPatternBinding()) {
|
||||
if (auto *init = PD->getInit(PD->getPatternEntryIndexForVarDecl(VAR))) {
|
||||
record(E, init, getDeclName(ReferencedDecl));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
|
||||
if (E->isSemanticallyConstExpr()) {
|
||||
if (auto *SL = dyn_cast<StringLiteralExpr>(E)) {
|
||||
record(SL, ConstKind::String, SL->getValue());
|
||||
}
|
||||
record(E, E);
|
||||
return { false, E };
|
||||
}
|
||||
if (handleSimpleReference(E)) {
|
||||
return { false, E };
|
||||
}
|
||||
return { true, E };
|
||||
}
|
||||
@@ -2279,14 +2341,20 @@ template <> struct swift::json::ObjectTraits<ConstExprInfo> {
|
||||
StringRef kind;
|
||||
switch(info.kind) {
|
||||
#define CASE(X) case ConstKind::X: kind = #X; break;
|
||||
CASE(String)
|
||||
CASE(Int)
|
||||
CASE(StringLiteral)
|
||||
CASE(IntegerLiteral)
|
||||
CASE(FloatLiteral)
|
||||
CASE(BooleanLiteral)
|
||||
CASE(Dictionary)
|
||||
CASE(Array)
|
||||
#undef CASE
|
||||
}
|
||||
out.mapRequired("kind", kind);
|
||||
out.mapRequired("offset", info.offset);
|
||||
out.mapRequired("length", info.length);
|
||||
out.mapRequired("value", info.value);
|
||||
if (!info.referencedD.empty())
|
||||
out.mapRequired("decl", info.referencedD);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -198,35 +198,72 @@ Expr *Expr::getSemanticsProvidingExpr() {
|
||||
return this;
|
||||
}
|
||||
|
||||
bool Expr::isSemanticallyConstExpr() const {
|
||||
auto E = getSemanticsProvidingExpr();
|
||||
if (!E) {
|
||||
return false;
|
||||
}
|
||||
switch(E->getKind()) {
|
||||
case ExprKind::IntegerLiteral:
|
||||
case ExprKind::NilLiteral:
|
||||
case ExprKind::BooleanLiteral:
|
||||
case ExprKind::FloatLiteral:
|
||||
case ExprKind::StringLiteral:
|
||||
case ExprKind::KeyPath:
|
||||
bool Expr::printConstExprValue(llvm::raw_ostream *OS) const {
|
||||
auto print = [&](StringRef text) {
|
||||
if (OS) {
|
||||
*OS << text;
|
||||
}
|
||||
};
|
||||
auto *E = getSemanticsProvidingExpr();
|
||||
assert(E);
|
||||
switch(getKind()) {
|
||||
case ExprKind::BooleanLiteral: {
|
||||
auto isTrue = cast<BooleanLiteralExpr>(E)->getValue();
|
||||
print(isTrue ? "true" : "false");
|
||||
return true;
|
||||
}
|
||||
case ExprKind::IntegerLiteral:
|
||||
case ExprKind::FloatLiteral: {
|
||||
auto digits = cast<NumberLiteralExpr>(E)->getDigitsText();
|
||||
assert(!digits.empty());
|
||||
print(digits);
|
||||
return true;
|
||||
}
|
||||
case ExprKind::NilLiteral: {
|
||||
print("nil");
|
||||
return true;
|
||||
}
|
||||
case ExprKind::StringLiteral: {
|
||||
auto *LE = cast<StringLiteralExpr>(E);
|
||||
print("\"");
|
||||
print(LE->getValue());
|
||||
print("\"");
|
||||
return true;
|
||||
}
|
||||
case ExprKind::KeyPath: {
|
||||
// FIXME: print keypath
|
||||
print("\\.<NOT_IMPLEMENTED>");
|
||||
return true;
|
||||
}
|
||||
case ExprKind::Array:
|
||||
case ExprKind::Dictionary: {
|
||||
print("[");
|
||||
auto *CE = cast<CollectionExpr>(E);
|
||||
for (auto *EL: CE->getElements()) {
|
||||
if (!EL->isSemanticallyConstExpr())
|
||||
for (unsigned N = CE->getNumElements(), I = 0; I != N; I ++) {
|
||||
auto Ele = CE->getElement(I);
|
||||
auto needComma = I + 1 != N;
|
||||
if (!Ele->printConstExprValue(OS)) {
|
||||
return false;
|
||||
}
|
||||
if (needComma)
|
||||
print(", ");
|
||||
}
|
||||
print("]");
|
||||
return true;
|
||||
}
|
||||
case ExprKind::Tuple: {
|
||||
print("(");
|
||||
auto *TE = cast<TupleExpr>(E);
|
||||
for (auto *EL: TE->getElements()) {
|
||||
if (!EL->isSemanticallyConstExpr()) {
|
||||
for (unsigned N = TE->getNumElements(), I = 0; I != N; I ++) {
|
||||
auto Ele = TE->getElement(I);
|
||||
auto needComma = I + 1 != N;
|
||||
if (!Ele->printConstExprValue(OS)) {
|
||||
return false;
|
||||
}
|
||||
if (needComma)
|
||||
print(", ");
|
||||
}
|
||||
print(")");
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
@@ -234,6 +271,10 @@ bool Expr::isSemanticallyConstExpr() const {
|
||||
}
|
||||
}
|
||||
|
||||
bool Expr::isSemanticallyConstExpr() const {
|
||||
return printConstExprValue(nullptr);
|
||||
}
|
||||
|
||||
Expr *Expr::getValueProvidingExpr() {
|
||||
Expr *E = getSemanticsProvidingExpr();
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
_const let global_str = "abc"
|
||||
_const let global_int = 3
|
||||
_const let global_float = 3.2
|
||||
|
||||
class C {
|
||||
static _const let class_bool = false
|
||||
static _const let class_arr = [2, 2, 3]
|
||||
static _const let class_dict = [2:1, 2:1, 3:1]
|
||||
}
|
||||
|
||||
func foo() {
|
||||
_ = global_str
|
||||
_ = global_int
|
||||
_ = global_float
|
||||
_ = C.class_bool
|
||||
_ = C.class_arr
|
||||
_ = C.class_dict
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
// RUN: %FileCheck %s < %t/abi.json
|
||||
|
||||
// CHECK: "kind": "String"
|
||||
// CHECK: "value": "abc"
|
||||
// CHECK: "kind": "String"
|
||||
// CHECK: "value": "def"
|
||||
// CHECK: "kind": "StringLiteral"
|
||||
// CHECK: "value": "\"abc\""
|
||||
// CHECK: "kind": "StringLiteral"
|
||||
// CHECK: "value": "\"def\""
|
||||
|
||||
41
test/api-digester/const_values_simple_references.swift
Normal file
41
test/api-digester/const_values_simple_references.swift
Normal file
@@ -0,0 +1,41 @@
|
||||
// RUN: %empty-directory(%t)
|
||||
|
||||
// RUN: %target-swift-frontend -emit-module -o %t/Foo.swiftmodule -emit-abi-descriptor-path %t/abi.json %S/Inputs/ConstExtraction/SimpleReferences.swift
|
||||
// RUN: %api-digester -deserialize-sdk -input-paths %t/abi.json -o %t.result
|
||||
|
||||
// RUN: %FileCheck %s < %t/abi.json
|
||||
|
||||
// CHECK: "offset": 249,
|
||||
// CHECK-NEXT: "length": 10,
|
||||
// CHECK-NEXT: "value": "\"abc\"",
|
||||
// CHECK-NEXT: "decl": "global_str"
|
||||
|
||||
// CHECK: "kind": "IntegerLiteral",
|
||||
// CHECK-NEXT: "offset": 266,
|
||||
// CHECK-NEXT: "length": 10,
|
||||
// CHECK-NEXT: "value": "3",
|
||||
// CHECK-NEXT: "decl": "global_int"
|
||||
|
||||
// CHECK: "kind": "FloatLiteral",
|
||||
// CHECK-NEXT: "offset": 283,
|
||||
// CHECK-NEXT: "length": 12,
|
||||
// CHECK-NEXT: "value": "3.2",
|
||||
// CHECK-NEXT: "decl": "global_float"
|
||||
|
||||
// CHECK: "kind": "BooleanLiteral",
|
||||
// CHECK-NEXT: "offset": 302,
|
||||
// CHECK-NEXT: "length": 12,
|
||||
// CHECK-NEXT: "value": "false",
|
||||
// CHECK-NEXT: "decl": "class_bool"
|
||||
|
||||
// CHECK: "kind": "Array",
|
||||
// CHECK-NEXT: "offset": 321,
|
||||
// CHECK-NEXT: "length": 11,
|
||||
// CHECK-NEXT: "value": "[2, 2, 3]",
|
||||
// CHECK-NEXT: "decl": "class_arr"
|
||||
|
||||
// CHECK: "kind": "Dictionary",
|
||||
// CHECK-NEXT: "offset": 339,
|
||||
// CHECK-NEXT: "length": 12,
|
||||
// CHECK-NEXT: "value": "[(2, 1), (2, 1), (3, 1)]",
|
||||
// CHECK-NEXT: "decl": "class_dict"
|
||||
Reference in New Issue
Block a user