1717
1818#include " swift/AST/ASTContext.h"
1919#include " swift/AST/ASTWalker.h"
20+ #include " swift/Bridging/ASTGen.h"
2021#include " swift/AST/Decl.h"
2122#include " swift/AST/Expr.h"
2223#include " swift/AST/Initializer.h"
@@ -39,6 +40,103 @@ using namespace ast_scope;
3940
4041#pragma mark ASTScope
4142
43+ class LoggingASTScopeDeclConsumer
44+ : public namelookup::AbstractASTScopeDeclConsumer {
45+ private:
46+ namelookup::AbstractASTScopeDeclConsumer *originalConsumer;
47+
48+ public:
49+ mutable SmallVector<BridgedConsumedLookupResult> recordedElements;
50+
51+ LoggingASTScopeDeclConsumer (
52+ namelookup::AbstractASTScopeDeclConsumer *consumer)
53+ : originalConsumer(consumer) {}
54+
55+ ~LoggingASTScopeDeclConsumer () = default ;
56+
57+ // / Called for every ValueDecl visible from the lookup.
58+ // /
59+ // / Takes an array in order to batch the consumption before setting
60+ // / IndexOfFirstOuterResult when necessary.
61+ // /
62+ // / Additionally, each name is logged to `recordedElements` and
63+ // / can be later used in validation of `SwiftLexicalLookup` result.
64+ // /
65+ // / \param baseDC either a type context or the local context of a
66+ // / `self` parameter declaration. See LookupResult for a discussion
67+ // / of type -vs- instance lookup results.
68+ // /
69+ // / \return true if the lookup should be stopped at this point.
70+ bool consume (ArrayRef<ValueDecl *> values,
71+ NullablePtr<DeclContext> baseDC = nullptr ) override {
72+ bool result = originalConsumer->consume (values, baseDC);
73+
74+ for (auto value : values) {
75+ if (auto sourceLoc = value->getLoc ()) {
76+ recordedElements.push_back (BridgedConsumedLookupResult (
77+ value->getBaseIdentifier (), sourceLoc, result));
78+ } else {
79+ // If sourceLoc is unavailable, use location of it's parent.
80+ recordedElements.push_back (BridgedConsumedLookupResult (
81+ value->getBaseIdentifier (),
82+ value->getDeclContext ()->getAsDecl ()->getLoc (), result));
83+ }
84+ }
85+
86+ return result;
87+ };
88+
89+ // / Look for members of a nominal type or extension scope.
90+ // /
91+ // / Each call is recorded in `recordedElements` with a special flag set.
92+ // / It can be later used in validation of `SwiftLexicalLookup` result.
93+ // /
94+ // / \return true if the lookup should be stopped at this point.
95+ bool lookInMembers (const DeclContext *scopeDC) const override {
96+ bool result = originalConsumer->lookInMembers (scopeDC);
97+
98+ if (auto *extDecl = dyn_cast<ExtensionDecl>(scopeDC)) {
99+ recordedElements.push_back (BridgedConsumedLookupResult (
100+ Identifier (), extDecl->getExtendedTypeRepr ()->getLoc (),
101+ 0b10 + result));
102+ } else {
103+ recordedElements.push_back (BridgedConsumedLookupResult (
104+ scopeDC->getSelfNominalTypeDecl ()->getBaseIdentifier (),
105+ scopeDC->getAsDecl ()->getLoc (), 0b10 + result));
106+ }
107+
108+ return result;
109+ };
110+
111+ // / Called for local VarDecls that might not yet be in scope.
112+ // /
113+ // / Note that the set of VarDecls visited here are going to be a
114+ // / superset of those visited in consume().
115+ bool consumePossiblyNotInScope (ArrayRef<VarDecl *> values) override {
116+ bool result = originalConsumer->consumePossiblyNotInScope (values);
117+ return result;
118+ }
119+
120+ // / Called right before looking at the parent scope of a BraceStmt.
121+ // /
122+ // / \return true if the lookup should be stopped at this point.
123+ bool finishLookupInBraceStmt (BraceStmt *stmt) override {
124+ return originalConsumer->finishLookupInBraceStmt (stmt);
125+ }
126+
127+ #ifndef NDEBUG
128+ void startingNextLookupStep () override {
129+ originalConsumer->startingNextLookupStep ();
130+ }
131+ void finishingLookup (std::string input) const override {
132+ originalConsumer->finishingLookup (input);
133+ }
134+ bool isTargetLookup () const override {
135+ return originalConsumer->isTargetLookup ();
136+ }
137+ #endif
138+ };
139+
42140void ASTScope::unqualifiedLookup (
43141 SourceFile *SF, SourceLoc loc,
44142 namelookup::AbstractASTScopeDeclConsumer &consumer) {
@@ -48,7 +146,30 @@ void ASTScope::unqualifiedLookup(
48146
49147 if (auto *s = SF->getASTContext ().Stats )
50148 ++s->getFrontendCounters ().NumASTScopeLookups ;
51- ASTScopeImpl::unqualifiedLookup (SF, loc, consumer);
149+
150+ // Perform validation of SwiftLexicalLookup if option
151+ // Feature::UnqualifiedLookupValidation is enabled and lookup was not
152+ // performed in a macro.
153+ if (SF->getASTContext ().LangOpts .hasFeature (
154+ Feature::UnqualifiedLookupValidation) &&
155+ !SF->getEnclosingSourceFile ()) {
156+ LoggingASTScopeDeclConsumer loggingASTScopeDeclConsumer =
157+ LoggingASTScopeDeclConsumer (&consumer);
158+
159+ ASTScopeImpl::unqualifiedLookup (SF, loc, loggingASTScopeDeclConsumer);
160+
161+ bool passed = swift_ASTGen_validateUnqualifiedLookup (
162+ SF->getExportedSourceFile (), SF->getASTContext (), loc,
163+ loggingASTScopeDeclConsumer.finishLookupInBraceStmt (nullptr ),
164+ BridgedArrayRef (loggingASTScopeDeclConsumer.recordedElements .data (),
165+ loggingASTScopeDeclConsumer.recordedElements .size ()));
166+
167+ if (!passed) {
168+ SF->getASTContext ().Diags .diagnose (loc, diag::lookup_outputs_dont_match);
169+ }
170+ } else {
171+ ASTScopeImpl::unqualifiedLookup (SF, loc, consumer);
172+ }
52173}
53174
54175llvm::SmallVector<LabeledStmt *, 4 > ASTScope::lookupLabeledStmts (
0 commit comments