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;
|
return getSemanticsProvidingExpr()->getKind() == ExprKind::InOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool printConstExprValue(llvm::raw_ostream *OS) const;
|
||||||
bool isSemanticallyConstExpr() const;
|
bool isSemanticallyConstExpr() const;
|
||||||
|
|
||||||
/// Returns false if this expression needs to be wrapped in parens when
|
/// 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};
|
return {std::move(FileBufOrErr.get()), Result};
|
||||||
}
|
}
|
||||||
enum class ConstKind: uint8_t {
|
enum class ConstKind: uint8_t {
|
||||||
String = 0,
|
StringLiteral = 0,
|
||||||
Int,
|
IntegerLiteral,
|
||||||
|
FloatLiteral,
|
||||||
|
BooleanLiteral,
|
||||||
|
Array,
|
||||||
|
Dictionary,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ConstExprInfo {
|
struct ConstExprInfo {
|
||||||
@@ -2230,9 +2234,11 @@ struct ConstExprInfo {
|
|||||||
unsigned offset = 0;
|
unsigned offset = 0;
|
||||||
unsigned length = 0;
|
unsigned length = 0;
|
||||||
StringRef value;
|
StringRef value;
|
||||||
|
StringRef referencedD;
|
||||||
ConstExprInfo(StringRef filePath, ConstKind kind, unsigned offset,
|
ConstExprInfo(StringRef filePath, ConstKind kind, unsigned offset,
|
||||||
unsigned length, StringRef value):
|
unsigned length, StringRef value, StringRef referencedD):
|
||||||
filePath(filePath), kind(kind), offset(offset), length(length), value(value) {}
|
filePath(filePath), kind(kind), offset(offset), length(length), value(value),
|
||||||
|
referencedD(referencedD) {}
|
||||||
ConstExprInfo() = default;
|
ConstExprInfo() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2242,7 +2248,7 @@ class ConstExtractor: public ASTWalker {
|
|||||||
SourceManager &SM;
|
SourceManager &SM;
|
||||||
std::vector<ConstExprInfo> allConsts;
|
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();
|
auto startLoc = E->getStartLoc();
|
||||||
// Asserts?
|
// Asserts?
|
||||||
if (startLoc.isInvalid())
|
if (startLoc.isInvalid())
|
||||||
@@ -2254,14 +2260,70 @@ class ConstExtractor: public ASTWalker {
|
|||||||
auto length = SM.getByteDistance(startLoc, endLoc);
|
auto length = SM.getByteDistance(startLoc, endLoc);
|
||||||
auto file = SM.getIdentifierForBuffer(bufferId);
|
auto file = SM.getIdentifierForBuffer(bufferId);
|
||||||
auto offset = SM.getLocOffsetInBuffer(startLoc, 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 {
|
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
|
||||||
if (E->isSemanticallyConstExpr()) {
|
if (E->isSemanticallyConstExpr()) {
|
||||||
if (auto *SL = dyn_cast<StringLiteralExpr>(E)) {
|
record(E, E);
|
||||||
record(SL, ConstKind::String, SL->getValue());
|
return { false, E };
|
||||||
}
|
}
|
||||||
|
if (handleSimpleReference(E)) {
|
||||||
|
return { false, E };
|
||||||
}
|
}
|
||||||
return { true, E };
|
return { true, E };
|
||||||
}
|
}
|
||||||
@@ -2279,14 +2341,20 @@ template <> struct swift::json::ObjectTraits<ConstExprInfo> {
|
|||||||
StringRef kind;
|
StringRef kind;
|
||||||
switch(info.kind) {
|
switch(info.kind) {
|
||||||
#define CASE(X) case ConstKind::X: kind = #X; break;
|
#define CASE(X) case ConstKind::X: kind = #X; break;
|
||||||
CASE(String)
|
CASE(StringLiteral)
|
||||||
CASE(Int)
|
CASE(IntegerLiteral)
|
||||||
|
CASE(FloatLiteral)
|
||||||
|
CASE(BooleanLiteral)
|
||||||
|
CASE(Dictionary)
|
||||||
|
CASE(Array)
|
||||||
#undef CASE
|
#undef CASE
|
||||||
}
|
}
|
||||||
out.mapRequired("kind", kind);
|
out.mapRequired("kind", kind);
|
||||||
out.mapRequired("offset", info.offset);
|
out.mapRequired("offset", info.offset);
|
||||||
out.mapRequired("length", info.length);
|
out.mapRequired("length", info.length);
|
||||||
out.mapRequired("value", info.value);
|
out.mapRequired("value", info.value);
|
||||||
|
if (!info.referencedD.empty())
|
||||||
|
out.mapRequired("decl", info.referencedD);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -198,35 +198,72 @@ Expr *Expr::getSemanticsProvidingExpr() {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Expr::isSemanticallyConstExpr() const {
|
bool Expr::printConstExprValue(llvm::raw_ostream *OS) const {
|
||||||
auto E = getSemanticsProvidingExpr();
|
auto print = [&](StringRef text) {
|
||||||
if (!E) {
|
if (OS) {
|
||||||
return false;
|
*OS << text;
|
||||||
}
|
}
|
||||||
switch(E->getKind()) {
|
};
|
||||||
case ExprKind::IntegerLiteral:
|
auto *E = getSemanticsProvidingExpr();
|
||||||
case ExprKind::NilLiteral:
|
assert(E);
|
||||||
case ExprKind::BooleanLiteral:
|
switch(getKind()) {
|
||||||
case ExprKind::FloatLiteral:
|
case ExprKind::BooleanLiteral: {
|
||||||
case ExprKind::StringLiteral:
|
auto isTrue = cast<BooleanLiteralExpr>(E)->getValue();
|
||||||
case ExprKind::KeyPath:
|
print(isTrue ? "true" : "false");
|
||||||
return true;
|
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::Array:
|
||||||
case ExprKind::Dictionary: {
|
case ExprKind::Dictionary: {
|
||||||
|
print("[");
|
||||||
auto *CE = cast<CollectionExpr>(E);
|
auto *CE = cast<CollectionExpr>(E);
|
||||||
for (auto *EL: CE->getElements()) {
|
for (unsigned N = CE->getNumElements(), I = 0; I != N; I ++) {
|
||||||
if (!EL->isSemanticallyConstExpr())
|
auto Ele = CE->getElement(I);
|
||||||
|
auto needComma = I + 1 != N;
|
||||||
|
if (!Ele->printConstExprValue(OS)) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
if (needComma)
|
||||||
|
print(", ");
|
||||||
}
|
}
|
||||||
|
print("]");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case ExprKind::Tuple: {
|
case ExprKind::Tuple: {
|
||||||
|
print("(");
|
||||||
auto *TE = cast<TupleExpr>(E);
|
auto *TE = cast<TupleExpr>(E);
|
||||||
for (auto *EL: TE->getElements()) {
|
for (unsigned N = TE->getNumElements(), I = 0; I != N; I ++) {
|
||||||
if (!EL->isSemanticallyConstExpr()) {
|
auto Ele = TE->getElement(I);
|
||||||
|
auto needComma = I + 1 != N;
|
||||||
|
if (!Ele->printConstExprValue(OS)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (needComma)
|
||||||
|
print(", ");
|
||||||
}
|
}
|
||||||
|
print(")");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -234,6 +271,10 @@ bool Expr::isSemanticallyConstExpr() const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Expr::isSemanticallyConstExpr() const {
|
||||||
|
return printConstExprValue(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
Expr *Expr::getValueProvidingExpr() {
|
Expr *Expr::getValueProvidingExpr() {
|
||||||
Expr *E = getSemanticsProvidingExpr();
|
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
|
// RUN: %FileCheck %s < %t/abi.json
|
||||||
|
|
||||||
// CHECK: "kind": "String"
|
// CHECK: "kind": "StringLiteral"
|
||||||
// CHECK: "value": "abc"
|
// CHECK: "value": "\"abc\""
|
||||||
// CHECK: "kind": "String"
|
// CHECK: "kind": "StringLiteral"
|
||||||
// CHECK: "value": "def"
|
// 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