Skip to content

Commit 9dcec05

Browse files
committed
Fix non-public imported declaration diagnostic bug
When DiagnosticEngine needs to diagnose something about an imported declaration, it uses ASTPrinter to print the declaration’s interface into a source buffer and then diagnoses it there. However, this code only printed public declarations, so it failed to account for features like `@testable import` which allow less-than-public declarations to be imported. Errors involving these declarations would therefore be diagnosed at <unknown>:0. This commit changes DiagnosticEngine to determine the access level of the declaration it needs to print and, if it is below `Public`, instead prints a separate interface whose minimum access level is low enough to include the desired declaration.
1 parent 5c877c5 commit 9dcec05

File tree

5 files changed

+59
-10
lines changed

5 files changed

+59
-10
lines changed

include/swift/AST/PrintOptions.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,7 @@ struct PrintOptions {
499499
}
500500

501501
/// Retrieve the set of options suitable for diagnostics printing.
502-
static PrintOptions printForDiagnostics() {
502+
static PrintOptions printForDiagnostics(AccessLevel accessFilter) {
503503
PrintOptions result = printVerbose();
504504
result.PrintAccess = true;
505505
result.Indent = 4;
@@ -512,7 +512,7 @@ struct PrintOptions {
512512
result.ExcludeAttrList.push_back(DAK_Optimize);
513513
result.ExcludeAttrList.push_back(DAK_Rethrows);
514514
result.PrintOverrideKeyword = false;
515-
result.AccessFilter = AccessLevel::Public;
515+
result.AccessFilter = accessFilter;
516516
result.PrintIfConfig = false;
517517
result.ShouldQualifyNestedDeclarations =
518518
QualifyNestedDeclarations::TypesOnly;
@@ -522,7 +522,7 @@ struct PrintOptions {
522522

523523
/// Retrieve the set of options suitable for interface generation.
524524
static PrintOptions printInterface() {
525-
PrintOptions result = printForDiagnostics();
525+
PrintOptions result = printForDiagnostics(AccessLevel::Public);
526526
result.SkipUnavailable = true;
527527
result.SkipImplicit = true;
528528
result.SkipSwiftPrivateClangDecls = true;

lib/AST/DiagnosticEngine.cpp

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,20 @@ void DiagnosticEngine::emitTentativeDiagnostics() {
846846
TentativeDiagnostics.clear();
847847
}
848848

849+
/// Returns the access level of the least accessible PrettyPrintedDeclarations
850+
/// buffer that \p decl should appear in.
851+
///
852+
/// This is always \c Public unless \p decl is a \c ValueDecl and its
853+
/// access level is below \c Public. (That can happen with @testable and
854+
/// @_private imports.)
855+
static AccessLevel getBufferAccessLevel(const Decl *decl) {
856+
AccessLevel level = AccessLevel::Public;
857+
if (auto *VD = dyn_cast<ValueDecl>(decl))
858+
level = VD->getFormalAccessScope().accessLevelForDiagnostics();
859+
if (level > AccessLevel::Public) level = AccessLevel::Public;
860+
return level;
861+
}
862+
849863
Optional<DiagnosticInfo>
850864
DiagnosticEngine::diagnosticInfoForDiagnostic(const Diagnostic &diagnostic) {
851865
auto behavior = state.determineBehavior(diagnostic.getID());
@@ -867,21 +881,31 @@ DiagnosticEngine::diagnosticInfoForDiagnostic(const Diagnostic &diagnostic) {
867881
if (ppLoc.isInvalid()) {
868882
class TrackingPrinter : public StreamPrinter {
869883
SmallVectorImpl<std::pair<const Decl *, uint64_t>> &Entries;
884+
AccessLevel bufferAccessLevel;
870885

871886
public:
872887
TrackingPrinter(
873888
SmallVectorImpl<std::pair<const Decl *, uint64_t>> &Entries,
874-
raw_ostream &OS) :
875-
StreamPrinter(OS), Entries(Entries) {}
889+
raw_ostream &OS, AccessLevel bufferAccessLevel) :
890+
StreamPrinter(OS), Entries(Entries),
891+
bufferAccessLevel(bufferAccessLevel) {}
876892

877893
void printDeclLoc(const Decl *D) override {
878-
Entries.push_back({ D, OS.tell() });
894+
if (getBufferAccessLevel(D) == bufferAccessLevel)
895+
Entries.push_back({ D, OS.tell() });
879896
}
880897
};
881898
SmallVector<std::pair<const Decl *, uint64_t>, 8> entries;
882899
llvm::SmallString<128> buffer;
883900
llvm::SmallString<128> bufferName;
884901
{
902+
// The access level of the buffer we want to print. Declarations below
903+
// this access level will be omitted from the buffer; declarations
904+
// above it will be printed, but (except for Open declarations in the
905+
// Public buffer) will not be recorded in PrettyPrintedDeclarations as
906+
// the "true" SourceLoc for the declaration.
907+
AccessLevel bufferAccessLevel = getBufferAccessLevel(decl);
908+
885909
// Figure out which declaration to print. It's the top-most
886910
// declaration (not a module).
887911
const Decl *ppDecl = decl;
@@ -942,10 +966,21 @@ DiagnosticEngine::diagnosticInfoForDiagnostic(const Diagnostic &diagnostic) {
942966
bufferName += ext->getExtendedType().getString();
943967
}
944968

969+
// If we're using a lowered access level, give the buffer a distinct
970+
// name.
971+
if (bufferAccessLevel != AccessLevel::Public) {
972+
assert(bufferAccessLevel < AccessLevel::Public
973+
&& "Above-public access levels should use public buffer");
974+
bufferName += " (";
975+
bufferName += getAccessLevelSpelling(bufferAccessLevel);
976+
bufferName += ")";
977+
}
978+
945979
// Pretty-print the declaration we've picked.
946980
llvm::raw_svector_ostream out(buffer);
947-
TrackingPrinter printer(entries, out);
948-
ppDecl->print(printer, PrintOptions::printForDiagnostics());
981+
TrackingPrinter printer(entries, out, bufferAccessLevel);
982+
ppDecl->print(printer,
983+
PrintOptions::printForDiagnostics(bufferAccessLevel));
949984
}
950985

951986
// Build a buffer with the pretty-printed declaration.

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2974,9 +2974,9 @@ printRequirementStub(ValueDecl *Requirement, DeclContext *Adopter,
29742974
}
29752975
}
29762976

2977-
PrintOptions Options = PrintOptions::printForDiagnostics();
2977+
PrintOptions Options =
2978+
PrintOptions::printForDiagnostics(AccessLevel::Private);
29782979
Options.PrintDocumentationComments = false;
2979-
Options.AccessFilter = AccessLevel::Private;
29802980
Options.PrintAccess = false;
29812981
Options.SkipAttributes = true;
29822982
Options.FunctionDefinitions = true;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
func ambiguous() {}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module -module-name ModuleA -emit-module-path %t/ModuleA.swiftmodule -primary-file %S/Inputs/TestablePrintASTLocationsModule.swift -enable-testing
3+
// RUN: %target-swift-frontend -emit-module -module-name ModuleB -emit-module-path %t/ModuleB.swiftmodule -primary-file %S/Inputs/TestablePrintASTLocationsModule.swift -enable-testing
4+
// RUN: not %target-swift-frontend -typecheck -I %t %s 2>&1 | %FileCheck %s
5+
6+
@testable import ModuleA
7+
@testable import ModuleB
8+
9+
ambiguous()
10+
11+
// CHECK: testable-printast-locations.swift:[[@LINE-2]]:1: error: ambiguous use of 'ambiguous()'
12+
// CHECK: ModuleA.ambiguous (internal):1:15: note: found this candidate
13+
// CHECK: ModuleB.ambiguous (internal):1:15: note: found this candidate

0 commit comments

Comments
 (0)