Skip to content

Commit 3175c37

Browse files
author
Nuri Amari
committed
Add improved experimental ClangImporter diagnostics
1 parent efb1840 commit 3175c37

24 files changed

+908
-58
lines changed

include/swift/AST/ASTContext.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,6 +1123,9 @@ class ASTContext final {
11231123
InheritedProtocolConformance *
11241124
getInheritedConformance(Type type, ProtocolConformance *inherited);
11251125

1126+
/// Check if \p decl is included in LazyContexts.
1127+
bool isLazyContext(const DeclContext *decl);
1128+
11261129
/// Get the lazy data for the given declaration.
11271130
///
11281131
/// \param lazyLoader If non-null, the lazy loader to use when creating the

include/swift/AST/DiagnosticsClangImporter.def

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,5 +96,27 @@ WARNING(import_multiple_mainactor_attr,none,
9696

9797
ERROR(module_map_not_found, none, "module map file '%0' not found", (StringRef))
9898

99+
NOTE(macro_not_imported_unsupported_operator, none, "operator not supported in macro arithmetic", ())
100+
NOTE(macro_not_imported_unsupported_named_operator, none, "operator '%0' not supported in macro arithmetic", (StringRef))
101+
NOTE(macro_not_imported_invalid_string_literal, none, "invalid string literal", ())
102+
NOTE(macro_not_imported_invalid_numeric_literal, none, "invalid numeric literal", ())
103+
NOTE(macro_not_imported_unsupported_literal, none, "only numeric and string macro literals supported", ())
104+
NOTE(macro_not_imported_nested_cast, none, "non-null nested casts not supported", ())
105+
106+
ERROR(macro_not_imported_function_like, none, "macro '%0' not imported: function like macros not supported", (StringRef))
107+
ERROR(macro_not_imported_unsupported_structure, none, "macro '%0' not imported: structure not supported", (StringRef))
108+
ERROR(macro_not_imported, none, "macro '%0' not imported", (StringRef))
109+
110+
NOTE(return_type_not_imported, none, "return type not imported", ())
111+
NOTE(parameter_type_not_imported, none, "parameter '%0' not imported", (StringRef))
112+
NOTE(incomplete_type, none, "type '%0' is incomplete", (StringRef))
113+
NOTE(unsupported_builtin_type, none, "built-in type '%0' not supported", (StringRef))
114+
115+
ERROR(record_field_not_imported, none, "field '%0' not imported", (StringRef))
116+
ERROR(invoked_func_not_imported, none, "function '%0' not imported", (StringRef))
117+
ERROR(record_method_not_imported, none, "method '%0' not imported", (StringRef))
118+
119+
NOTE(forward_declaration_label, none, "type '%0' forward declared here", (StringRef))
120+
99121
#define UNDEFINE_DIAGNOSTIC_MACROS
100122
#include "DefineDiagnosticMacros.h"

include/swift/AST/LazyResolver.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ class alignas(void*) LazyMemberLoader {
7373
public:
7474
virtual ~LazyMemberLoader() = default;
7575

76+
/// Checks if diagnostics have been displayed for all
77+
/// members of \p IDC that have DeclName matching \p N.
78+
virtual bool
79+
diagnosticsProducedForNamedMembers(const IterableDeclContext *IDC,
80+
DeclBaseName N, uint64_t contextData) = 0;
81+
7682
/// Populates a given decl \p D with all of its members.
7783
///
7884
/// The implementation should add the members to D.

include/swift/ClangImporter/ClangImporter.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ namespace clang {
4040
class EnumDecl;
4141
class MacroInfo;
4242
class Module;
43+
class ModuleMacro;
4344
class NamedDecl;
4445
class Sema;
4546
class TargetInfo;

lib/AST/ASTContext.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2427,6 +2427,10 @@ ASTContext::getInheritedConformance(Type type, ProtocolConformance *inherited) {
24272427
return result;
24282428
}
24292429

2430+
bool ASTContext::isLazyContext(const DeclContext *dc) {
2431+
return getImpl().LazyContexts[dc];
2432+
}
2433+
24302434
LazyContextData *ASTContext::getOrCreateLazyContextData(
24312435
const DeclContext *dc,
24322436
LazyMemberLoader *lazyLoader) {

lib/AST/NameLookup.cpp

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1414,6 +1414,7 @@ TinyPtrVector<ValueDecl *>
14141414
DirectLookupRequest::evaluate(Evaluator &evaluator,
14151415
DirectLookupDescriptor desc) const {
14161416
const auto &name = desc.Name;
1417+
DeclBaseName baseName(name.getBaseName());
14171418
const auto flags = desc.Options;
14181419
auto *decl = desc.DC;
14191420

@@ -1440,7 +1441,36 @@ DirectLookupRequest::evaluate(Evaluator &evaluator,
14401441
decl->addLoadedExtensions();
14411442

14421443
auto &Table = *decl->LookupTable;
1443-
if (!useNamedLazyMemberLoading) {
1444+
1445+
bool lazilyTriggeredDiagnosticsOutstanding = false;
1446+
if (ctx.isLazyContext(decl)) {
1447+
auto ci = ctx.getOrCreateLazyIterableContextData(decl,
1448+
/*lazyLoader=*/nullptr);
1449+
// If this lookup is already cached in the table or all members have already
1450+
// been loaded, and there are diagnostics that were suppressed during the
1451+
// previous loads, lazily load once more to produce these diagnostics. This
1452+
// occurs because diagnostics are suppressed during eager loading of
1453+
// members.
1454+
lazilyTriggeredDiagnosticsOutstanding =
1455+
ctx.LangOpts.EnableExperimentalClangImporterDiagnostics &&
1456+
ctx.LangOpts.NamedLazyMemberLoading &&
1457+
(!useNamedLazyMemberLoading || Table.isLazilyComplete(baseName)) &&
1458+
!ci->loader->diagnosticsProducedForNamedMembers(decl, baseName,
1459+
ci->memberData);
1460+
}
1461+
1462+
if (lazilyTriggeredDiagnosticsOutstanding) {
1463+
// We are uninterested in the result of the import, just the diagnostics.
1464+
if (isa_and_nonnull<clang::RecordDecl>(decl->getClangDecl())) {
1465+
auto allFound = evaluateOrDefault(
1466+
ctx.evaluator,
1467+
ClangRecordMemberLookup({cast<StructDecl>(decl), name}), {});
1468+
} else if (isa<clang::ObjCContainerDecl>(decl->getClangDecl())) {
1469+
auto ci =
1470+
ctx.getOrCreateLazyIterableContextData(decl, /*lazyLoader=*/nullptr);
1471+
auto res = ci->loader->loadNamedMembers(decl, baseName, ci->memberData);
1472+
}
1473+
} else if (!useNamedLazyMemberLoading) {
14441474
// Make sure we have the complete list of members (in this nominal and in
14451475
// all extensions).
14461476
(void)decl->getMembers();
@@ -1450,7 +1480,6 @@ DirectLookupRequest::evaluate(Evaluator &evaluator,
14501480

14511481
Table.updateLookupTable(decl);
14521482
} else if (!Table.isLazilyComplete(name.getBaseName())) {
1453-
DeclBaseName baseName(name.getBaseName());
14541483

14551484
if (isa_and_nonnull<clang::NamespaceDecl>(decl->getClangDecl())) {
14561485
// Namespaces will never have any members so we can just return whatever

lib/ClangImporter/ClangImporter.cpp

Lines changed: 155 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
#include "llvm/Support/YAMLTraits.h"
7373
#include <algorithm>
7474
#include <memory>
75+
#include <utility>
7576

7677
using namespace swift;
7778
using namespace importer;
@@ -2256,6 +2257,109 @@ ClangModuleUnit *ClangImporter::Implementation::getClangModuleForDecl(
22562257
return getWrapperForModule(M);
22572258
}
22582259

2260+
// TODO: Consolidate with importSourceLoc below
2261+
SourceLoc ClangImporter::Implementation::convertSourceLocation(
2262+
const clang::SourceLocation &loc) {
2263+
return getBufferImporterForDiagnostics().resolveSourceLocation(
2264+
getClangASTContext().getSourceManager(), loc);
2265+
}
2266+
2267+
SwiftLookupTable::SingleEntry
2268+
ClangImporter::Implementation::getDiagnosticTarget() {
2269+
return diagnosticTarget;
2270+
}
2271+
2272+
template <typename T>
2273+
T ClangImporter::Implementation::withDiagnosticTargetIfNotAlreadyReported(
2274+
const SwiftLookupTable::SingleEntry &target, std::function<T()> fn) {
2275+
bool alreadyReported = reportedDiagnosticTargets.count(target);
2276+
if (!alreadyReported && !getEagerImportActive()) {
2277+
reportedDiagnosticTargets.insert(target);
2278+
return withDiagnosticTarget(target, fn);
2279+
}
2280+
2281+
return fn();
2282+
}
2283+
2284+
template <typename T>
2285+
T ClangImporter::Implementation::withDiagnosticTarget(
2286+
const SwiftLookupTable::SingleEntry &target, std::function<T()> fn) {
2287+
assert(target);
2288+
// In some situations, the ClangImporter is invoked in a reentrant manner,
2289+
// resulting in a diagnosticTarget already being set. In such a case, we
2290+
// keep the outer diagnostic target.
2291+
llvm::SaveAndRestore<SwiftLookupTable::SingleEntry> sar{diagnosticTarget};
2292+
if (!diagnosticTarget) {
2293+
diagnosticTarget = target;
2294+
}
2295+
return fn();
2296+
}
2297+
2298+
bool ClangImporter::Implementation::isDiagnosticTarget(
2299+
const SwiftLookupTable::SingleEntry &targetCandidate) const {
2300+
return diagnosticTarget == targetCandidate;
2301+
}
2302+
bool ClangImporter::Implementation::isDiagnosticTarget(
2303+
const clang::NamedDecl *targetCandidate) const {
2304+
return diagnosticTarget.dyn_cast<clang::NamedDecl *>() == targetCandidate;
2305+
}
2306+
bool ClangImporter::Implementation::isDiagnosticTarget(
2307+
const clang::ModuleMacro *targetCandidate) const {
2308+
return diagnosticTarget.dyn_cast<clang::ModuleMacro *>() == targetCandidate;
2309+
}
2310+
bool ClangImporter::Implementation::isDiagnosticTarget(
2311+
const clang::MacroInfo *targetCandidate) const {
2312+
return diagnosticTarget.dyn_cast<clang::MacroInfo *>() == targetCandidate;
2313+
}
2314+
2315+
bool ClangImporter::Implementation::getEagerImportActive() {
2316+
return eagerImportActive;
2317+
}
2318+
2319+
void ClangImporter::Implementation::ifTargetMatchesReportErrorAndConsumeNotes(
2320+
const clang::NamedDecl *target, Diagnostic &&error,
2321+
const clang::SourceLocation &loc) {
2322+
if (!(SwiftContext.LangOpts.EnableExperimentalClangImporterDiagnostics &&
2323+
isDiagnosticTarget(target)))
2324+
return;
2325+
2326+
const SourceLoc convertedLoc = convertSourceLocation(loc);
2327+
diagnose(convertedLoc, error);
2328+
while (!pendingErrorNotes.empty()) {
2329+
auto pendingErrorNote = pendingErrorNotes.back();
2330+
if (!pendingErrorNote.diag.getLoc()) {
2331+
pendingErrorNote.diag.setLoc(convertedLoc);
2332+
}
2333+
if (isDiagnosticTarget(pendingErrorNote.target)) {
2334+
diagnose(pendingErrorNote.diag.getLoc(), pendingErrorNote.diag);
2335+
}
2336+
pendingErrorNotes.pop_back();
2337+
}
2338+
}
2339+
2340+
void ClangImporter::Implementation::addPendingErrorNote(Diagnostic &&note) {
2341+
addPendingErrorNote(std::move(note), clang::SourceLocation());
2342+
}
2343+
2344+
void ClangImporter::Implementation::addPendingErrorNote(
2345+
Diagnostic &&note, const clang::SourceLocation &loc) {
2346+
if (!(SwiftContext.LangOpts.EnableExperimentalClangImporterDiagnostics &&
2347+
getDiagnosticTarget()))
2348+
return;
2349+
note.setLoc(convertSourceLocation(loc));
2350+
pendingErrorNotes.push_back({note, getDiagnosticTarget()});
2351+
}
2352+
2353+
void ClangImporter::Implementation::applySourceLocationToNotesWithoutLocation(
2354+
const clang::SourceLocation &loc) {
2355+
const SourceLoc convertedLoc = convertSourceLocation(loc);
2356+
for (auto &pendingErrorNote : pendingErrorNotes) {
2357+
if (!pendingErrorNote.diag.getLoc()) {
2358+
pendingErrorNote.diag.setLoc(convertedLoc);
2359+
}
2360+
}
2361+
}
2362+
22592363
#pragma mark Source locations
22602364
clang::SourceLocation
22612365
ClangImporter::Implementation::exportSourceLoc(SourceLoc loc) {
@@ -3860,21 +3964,31 @@ void ClangImporter::Implementation::lookupValue(
38603964
// If it's a Clang declaration, try to import it.
38613965
if (auto clangDecl = entry.dyn_cast<clang::NamedDecl *>()) {
38623966
bool isNamespace = isa<clang::NamespaceDecl>(clangDecl);
3863-
Decl *realDecl =
3864-
importDeclReal(clangDecl->getMostRecentDecl(), CurrentVersion,
3865-
/*useCanonicalDecl*/ !isNamespace);
3967+
Decl *realDecl = withDiagnosticTargetIfNotAlreadyReported(
3968+
clangDecl->getMostRecentDecl(),
3969+
std::function<Decl *()>([&]() -> Decl * {
3970+
return importDeclReal(clangDecl->getMostRecentDecl(),
3971+
CurrentVersion,
3972+
/*useCanonicalDecl*/ !isNamespace);
3973+
}));
38663974

38673975
if (!realDecl)
38683976
continue;
38693977
decl = cast<ValueDecl>(realDecl);
38703978
if (!decl) continue;
38713979
} else if (!name.isSpecial()) {
38723980
// Try to import a macro.
3873-
if (auto modMacro = entry.dyn_cast<clang::ModuleMacro *>())
3874-
decl = importMacro(name.getBaseIdentifier(), modMacro);
3875-
else if (auto clangMacro = entry.dyn_cast<clang::MacroInfo *>())
3876-
decl = importMacro(name.getBaseIdentifier(), clangMacro);
3877-
else
3981+
if (auto modMacro = entry.dyn_cast<clang::ModuleMacro *>()) {
3982+
decl = withDiagnosticTargetIfNotAlreadyReported(
3983+
modMacro, std::function<ValueDecl *()>([&]() {
3984+
return importMacro(name.getBaseIdentifier(), modMacro);
3985+
}));
3986+
} else if (auto clangMacro = entry.dyn_cast<clang::MacroInfo *>()) {
3987+
decl = withDiagnosticTargetIfNotAlreadyReported(
3988+
clangMacro, std::function<ValueDecl *()>([&]() {
3989+
return importMacro(name.getBaseIdentifier(), clangMacro);
3990+
}));
3991+
} else
38783992
llvm_unreachable("new kind of lookup table entry");
38793993
if (!decl) continue;
38803994
} else {
@@ -3954,6 +4068,9 @@ void ClangImporter::Implementation::lookupValue(
39544068
void ClangImporter::Implementation::lookupVisibleDecls(
39554069
SwiftLookupTable &table,
39564070
VisibleDeclConsumer &consumer) {
4071+
4072+
llvm::SaveAndRestore<bool> sar(eagerImportActive, true);
4073+
39574074
// Retrieve and sort all of the base names in this particular table.
39584075
auto baseNames = table.allBaseNames();
39594076
llvm::array_pod_sort(baseNames.begin(), baseNames.end());
@@ -4128,6 +4245,28 @@ TinyPtrVector<ValueDecl *> ClangRecordMemberLookup::evaluate(
41284245
return result;
41294246
}
41304247

4248+
bool ClangImporter::Implementation::diagnosticsProducedForNamedMembers(
4249+
const IterableDeclContext *IDC, DeclBaseName N, uint64_t contextData) {
4250+
auto *D = IDC->getDecl();
4251+
auto *DC = D->getInnermostDeclContext();
4252+
auto *CD = D->getClangDecl();
4253+
auto CMO = getClangSubmoduleForDecl(CD);
4254+
auto *nominal = DC->getSelfNominalTypeDecl();
4255+
auto effectiveClangContext = getEffectiveClangContext(nominal);
4256+
auto table = findLookupTable(*CMO);
4257+
4258+
for (auto entry :
4259+
table->lookup(SerializedSwiftName(N), effectiveClangContext)) {
4260+
if (auto member = entry.dyn_cast<clang::NamedDecl *>()) {
4261+
if (!reportedDiagnosticTargets.count(member)) {
4262+
return false;
4263+
}
4264+
}
4265+
}
4266+
4267+
return true;
4268+
}
4269+
41314270
TinyPtrVector<ValueDecl *>
41324271
ClangImporter::Implementation::loadNamedMembers(
41334272
const IterableDeclContext *IDC, DeclBaseName N, uint64_t contextData) {
@@ -4189,7 +4328,10 @@ ClangImporter::Implementation::loadNamedMembers(
41894328
if (member->getDeclContext() != CDC) continue;
41904329

41914330
SmallVector<Decl*, 4> tmp;
4192-
insertMembersAndAlternates(member, tmp);
4331+
withDiagnosticTargetIfNotAlreadyReported(
4332+
member, std::function<void()>(
4333+
[&]() { insertMembersAndAlternates(member, tmp); }));
4334+
41934335
for (auto *TD : tmp) {
41944336
if (auto *V = dyn_cast<ValueDecl>(TD)) {
41954337
// Skip ValueDecls if they import under different names.
@@ -4479,5 +4621,8 @@ ClangImporter::getEffectiveClangContext(const NominalTypeDecl *nominal) {
44794621
}
44804622

44814623
Decl *ClangImporter::importDeclDirectly(const clang::NamedDecl *decl) {
4482-
return Impl.importDecl(decl, Impl.CurrentVersion);
4624+
return Impl.withDiagnosticTargetIfNotAlreadyReported(
4625+
const_cast<clang::NamedDecl *>(decl), std::function<Decl *()>([&]() {
4626+
return Impl.importDecl(decl, Impl.CurrentVersion);
4627+
}));
44834628
}

0 commit comments

Comments
 (0)