Skip to content

Attach Lazy ClangImporter Diagnostics as Notes to Sema Diagnostics #41079

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 30, 2022

Conversation

NuriAmari
Copy link
Contributor

Behaviour Change

Clang importer diagnostics that are produced as a result of a reference in Swift code are attached as notes to the Sema produced diagnostic that indicates the declaration is unavailable.

This has the added benefit that ClangDiagnostics are produced as many times as referenced from Swift, whereas previosly it was just the first time.

Old Behaviour

badCFunc()
// CHECK: warning: 'badCFunc' could not be imported
// ...
// CHECK: error: cannot find 'badCFunc' in scope
badCFunc()
// CHECK: error: cannot find 'badCFunc' in scope

New Behaviour

badCFunc()
// CHECK: error: cannot find 'badCFunc' in scope
// CHECK: enote: 'badCFunc' could not be imported
// ...
badCFunc()
// CHECK: error: cannot find 'badCFunc' in scope
// CHECK: enote: 'badCFunc' could not be imported
// ...

As a result of this change, the eager diagnostic mode only produces notes. This is somewhat unorthodox, notes are typically attached to some other error or warning. This can be addressed if it is a concern.

Implementation Change

Previously, diagnostics were produced via calls to one of diagnoseValue, diagnoseMissingMember or diagnoseDeclDirectly from outside the ClangImporter. As I mention, these calls were made outside the ClangImporter, but still relatively close. This had the above "only report diagnostics for the first usage" effect. The aforementioned methods have been consolidated into diagnoseTopLevelValue and diagnoseMemberValue. These calls are made in Sema, directly after the diagnostic reporting the declaration could not be found is produced.

diagnoseTopLevelValue

This method is to be used to look up top level declarations whose containing context
is not known (ex: A C function, a macro etc).

/// Emits diagnostics for any declarations named name
/// whose direct declaration context is a TU.
virtual void diagnoseTopLevelValue(const DeclName &name) = 0;

diagnoseMemberValue

This method is to be used to look up declarations where the context
is required to disambiguate between declarations. Typically, this means
members (the context being the containing decl). You could use this
to diagnose a top level declaration from a specific translation unit,
but there no point in doing so.

/// Emit diagnostics for declarations named name that are members
/// of the provided baseType.
virtual void diagnoseMemberValue(const DeclName &name,
                                 const Type &baseType) = 0;

Copy link
Contributor

@zoecarver zoecarver left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love to see this getting better and better.

As always, thank you for the high quality work, Nuri!

@zoecarver
Copy link
Contributor

@swift-ci please smoke test.

@zoecarver
Copy link
Contributor

@swift-ci please smoke test macOS.

Clang importer diagnostics that are produced as a result of a reference
in Swift code are attached to as notes to the Sema produced diagnostic
that indicates the declaration is unavailable.

Ex: Notes about why a C function import failed are attached to
the error explaining that the symbol could not be found in scope.
@NuriAmari NuriAmari force-pushed the sema-coupled-diagnostics branch from 192d692 to 3762ca1 Compare January 29, 2022 20:03

void ClangImporter::diagnoseMemberValue(const DeclName &name,
const Type &baseType) {
if (!baseType->getAnyNominal())
Copy link
Contributor Author

@NuriAmari NuriAmari Jan 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple changes here. Since this call is made from Sema, the baseType could be anything (not just an imported ClangType). I discovered a few more checks are needed here.

First, extractDirectlyReferencedNominalTypes only knows how to handle small set of types of types. If you pass something it isn't expecting, we hit an llvm_unreachable. The getAnyNominal check takes care of this (I figure all imported types referencable from swift are nominal).

Lastly, I added checks that getClangDecl() returns a non-null value, and that this value is a clang::DeclContext. Otherwise, if either of these checks fail, but the value is passed to cast<clang::DeclContext>() anyways, it'll throw.

@zoecarver
Copy link
Contributor

@swift-ci please smoke test.

@zoecarver
Copy link
Contributor

@swift-ci please smoke test macOS.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants