mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
The tool is currently hard-coded to find functions in the SwiftUI library that take parameters of type `(...) -> T` where `T: View` but where the parameter isn't annotated with `@ViewBuilder`. The long-term vision here, of course, is that this reads and interprets a script file, but that's quite a bit more work (especially to generate a million bindings to the AST). In the meantime, I think having a functional harness that people familiar with the C++ API can easily hack on to make their own tools is still pretty useful. The harness does try to open a script file and lex the first token of it, because that's exactly as far as I got before deciding to hard-code the query I wanted. Since this input is otherwise ignored, you can just point the tool at any old `.swift` file (or just an empty file) and it'll be fine.
137 lines
4.0 KiB
C++
137 lines
4.0 KiB
C++
//===--- ASTScriptEvaluator.cpp -------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2019 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
///
|
|
/// AST script evaluation.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "ASTScript.h"
|
|
#include "ASTScriptConfiguration.h"
|
|
|
|
#include "swift/AST/ASTMangler.h"
|
|
#include "swift/AST/ASTWalker.h"
|
|
#include "swift/AST/Decl.h"
|
|
#include "swift/AST/NameLookup.h"
|
|
#include "swift/Frontend/Frontend.h"
|
|
|
|
using namespace swift;
|
|
using namespace scripting;
|
|
|
|
namespace {
|
|
|
|
class ASTScriptWalker : public ASTWalker {
|
|
const ASTScript &Script;
|
|
ProtocolDecl *ViewProtocol;
|
|
|
|
public:
|
|
ASTScriptWalker(const ASTScript &script, ProtocolDecl *viewProtocol)
|
|
: Script(script), ViewProtocol(viewProtocol) {}
|
|
|
|
bool walkToDeclPre(Decl *D) override {
|
|
visit(D);
|
|
return true;
|
|
}
|
|
|
|
void visit(const Decl *D) {
|
|
auto fn = dyn_cast<AbstractFunctionDecl>(D);
|
|
if (!fn) return;
|
|
|
|
// Suppress warnings.
|
|
(void) Script;
|
|
|
|
for (auto param : *fn->getParameters()) {
|
|
// The parameter must have function type.
|
|
auto paramType = param->getInterfaceType();
|
|
auto paramFnType = paramType->getAs<FunctionType>();
|
|
if (!paramFnType) continue;
|
|
|
|
// The parameter function must return a type parameter that
|
|
// conforms to SwiftUI.View.
|
|
auto paramResultType = paramFnType->getResult();
|
|
if (!paramResultType->isTypeParameter()) continue;
|
|
auto sig = fn->getGenericSignature();
|
|
if (!sig->conformsToProtocol(paramResultType, ViewProtocol)) continue;
|
|
|
|
// The parameter must not be a @ViewBuilder parameter.
|
|
if (param->getFunctionBuilderType()) continue;
|
|
|
|
// Print the function.
|
|
printDecl(fn);
|
|
}
|
|
}
|
|
|
|
void printDecl(const ValueDecl *decl) {
|
|
// FIXME: there's got to be some better way to print an exact reference
|
|
// to a declaration, including its context.
|
|
printDecl(llvm::outs(), decl);
|
|
llvm::outs() << "\n";
|
|
}
|
|
|
|
void printDecl(llvm::raw_ostream &out, const ValueDecl *decl) {
|
|
if (auto accessor = dyn_cast<AccessorDecl>(decl)) {
|
|
printDecl(out, accessor->getStorage());
|
|
out << ".(accessor)";
|
|
} else {
|
|
printDeclContext(out, decl->getDeclContext());
|
|
out << decl->getFullName();
|
|
}
|
|
}
|
|
|
|
void printDeclContext(llvm::raw_ostream &out, const DeclContext *dc) {
|
|
if (!dc) return;
|
|
if (auto module = dyn_cast<ModuleDecl>(dc)) {
|
|
out << module->getName() << ".";
|
|
} else if (auto extension = dyn_cast<ExtensionDecl>(dc)) {
|
|
printDecl(out, extension->getExtendedNominal());
|
|
out << ".";
|
|
} else if (auto decl = dyn_cast_or_null<ValueDecl>(dc->getAsDecl())) {
|
|
printDecl(out, decl);
|
|
out << ".";
|
|
} else {
|
|
printDeclContext(out, dc->getParent());
|
|
}
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
bool ASTScript::execute() const {
|
|
// Hardcode the actual query we want to execute here.
|
|
|
|
auto &ctx = Config.Compiler.getASTContext();
|
|
auto swiftUI = ctx.getLoadedModule(ctx.getIdentifier("SwiftUI"));
|
|
if (!swiftUI) {
|
|
llvm::errs() << "error: SwiftUI module not loaded\n";
|
|
return true;
|
|
}
|
|
|
|
UnqualifiedLookup viewLookup(ctx.getIdentifier("View"), swiftUI, nullptr);
|
|
auto viewProtocol =
|
|
dyn_cast_or_null<ProtocolDecl>(viewLookup.getSingleTypeResult());
|
|
if (!viewProtocol) {
|
|
llvm::errs() << "error: couldn't find SwiftUI.View protocol\n";
|
|
return true;
|
|
}
|
|
|
|
SmallVector<Decl*, 128> topLevelDecls;
|
|
swiftUI->getTopLevelDecls(topLevelDecls);
|
|
|
|
llvm::errs() << "found " << topLevelDecls.size() << " top-level declarations\n";
|
|
|
|
ASTScriptWalker walker(*this, viewProtocol);
|
|
for (auto decl : topLevelDecls) {
|
|
decl->walk(walker);
|
|
}
|
|
|
|
return false;
|
|
}
|