-
Notifications
You must be signed in to change notification settings - Fork 559
Don't insert a duplicate import when applying package autocorrects #8765
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
base: master
Are you sure you want to change the base?
Changes from all commits
04f6bf4
c333dbc
6511595
c80abe4
f21d80e
55b329f
767cd2b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -364,6 +364,8 @@ class VisibilityCheckerPass final { | |
public: | ||
const core::packages::PackageInfo &package; | ||
const bool insideTestFile; | ||
UnorderedMap<std::pair<core::packages::MangledName, core::packages::ImportType>, core::AutocorrectSuggestion> | ||
missingImports; | ||
|
||
VisibilityCheckerPass(core::Context ctx, const core::packages::PackageInfo &package) | ||
: package{package}, insideTestFile{ctx.file.data(ctx).isPackagedTest()} {} | ||
|
@@ -438,13 +440,13 @@ class VisibilityCheckerPass final { | |
return; | ||
} | ||
|
||
auto importType = this->package.importsPackage(otherPackage); | ||
auto wasNotImported = !importType.has_value(); | ||
auto currentImportType = this->package.importsPackage(otherPackage); | ||
auto wasImported = currentImportType.has_value(); | ||
auto importedAsTest = | ||
importType.has_value() && importType.value() == core::packages::ImportType::Test && !this->insideTestFile; | ||
if (wasNotImported || importedAsTest) { | ||
wasImported && currentImportType.value() == core::packages::ImportType::Test && !this->insideTestFile; | ||
if (!wasImported || importedAsTest) { | ||
auto &pkg = ctx.state.packageDB().getPackageInfo(otherPackage); | ||
bool isTestImport = otherFile.data(ctx).isPackagedTest() || ctx.file.data(ctx).isPackagedTest(); | ||
bool isTestImport = otherFile.data(ctx).isPackagedTest() || this->insideTestFile; | ||
auto strictDepsLevel = this->package.strictDependenciesLevel(); | ||
auto importStrictDepsLevel = pkg.strictDependenciesLevel(); | ||
bool layeringViolation = false; | ||
|
@@ -466,13 +468,22 @@ class VisibilityCheckerPass final { | |
path.has_value(); | ||
} | ||
if (!causesCycle && !layeringViolation && !strictDependenciesTooLow) { | ||
if (wasNotImported) { | ||
// We failed to import the package that defines the symbol | ||
auto newImportType = | ||
isTestImport ? core::packages::ImportType::Test : core::packages::ImportType::Normal; | ||
std::optional<core::AutocorrectSuggestion> autocorrect = | ||
this->package.addImport(ctx, pkg, isTestImport); | ||
// TODO(neil): find out why this is sometimes not true and enable this ENFORCE | ||
/* ENFORCE(autocorrect.has_value()); */ | ||
Comment on lines
+475
to
+476
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we doing this |
||
if (autocorrect.has_value()) { | ||
missingImports.insert({{pkg.mangledName(), newImportType}, autocorrect.value()}); | ||
} | ||
if (!wasImported) { | ||
if (auto e = ctx.beginError(lit.loc(), core::errors::Packager::MissingImport)) { | ||
e.setHeader("`{}` resolves but its package is not imported", lit.symbol().show(ctx)); | ||
e.addErrorLine(pkg.declLoc(), "Exported from package here"); | ||
if (auto exp = this->package.addImport(ctx, pkg, isTestImport)) { | ||
e.addAutocorrect(std::move(exp.value())); | ||
if (autocorrect.has_value()) { | ||
core::AutocorrectSuggestion autocorrectCopy(autocorrect.value()); | ||
e.addAutocorrect(std::move(autocorrectCopy)); | ||
if (!db.errorHint().empty()) { | ||
e.addErrorNote("{}", db.errorHint()); | ||
} | ||
|
@@ -485,11 +496,16 @@ class VisibilityCheckerPass final { | |
} | ||
} | ||
} else if (importedAsTest) { | ||
ENFORCE(!isTestImport); | ||
if (auto e = ctx.beginError(lit.loc(), core::errors::Packager::UsedTestOnlyName)) { | ||
e.setHeader("Used `{}` constant `{}` in non-test file", "test_import", litSymbol.show(ctx)); | ||
auto &pkg = ctx.state.packageDB().getPackageInfo(otherPackage); | ||
if (auto exp = this->package.addImport(ctx, pkg, false)) { | ||
e.addAutocorrect(std::move(exp.value())); | ||
if (autocorrect.has_value()) { | ||
core::AutocorrectSuggestion autocorrectCopy(autocorrect.value()); | ||
e.addAutocorrect(std::move(autocorrectCopy)); | ||
if (!db.errorHint().empty()) { | ||
e.addErrorNote("{}", db.errorHint()); | ||
} | ||
} | ||
e.addErrorLine(pkg.declLoc(), "Defined here"); | ||
} | ||
|
@@ -561,7 +577,7 @@ class VisibilityCheckerPass final { | |
ENFORCE(false, "At most three reasons should be present"); | ||
} | ||
e.setHeader("`{}` cannot be referenced here because {}", lit.symbol().show(ctx), reason); | ||
if (wasNotImported) { | ||
if (!wasImported) { | ||
e.addErrorNote("`{}`'s package is not imported", lit.symbol().show(ctx)); | ||
} else if (importedAsTest) { | ||
e.addErrorNote("`{}`'s package is imported as `{}`", lit.symbol().show(ctx), "test_import"); | ||
|
@@ -581,35 +597,68 @@ class VisibilityCheckerPass final { | |
} | ||
} | ||
|
||
static std::vector<ast::ParsedFile> run(const core::GlobalState &gs, WorkerPool &workers, | ||
static std::vector<ast::ParsedFile> run(core::GlobalState &nonConstGs, WorkerPool &workers, | ||
std::vector<ast::ParsedFile> files) { | ||
const core::GlobalState &gs = nonConstGs; | ||
Timer timeit(gs.tracer(), "visibility_checker.check_visibility"); | ||
auto taskq = std::make_shared<ConcurrentBoundedQueue<size_t>>(files.size()); | ||
auto resultq = std::make_shared<BlockingBoundedQueue< | ||
std::pair<core::FileRef, UnorderedMap<std::pair<core::packages::MangledName, core::packages::ImportType>, | ||
core::AutocorrectSuggestion>>>>(files.size()); | ||
absl::BlockingCounter barrier(std::max(workers.size(), 1)); | ||
|
||
for (size_t i = 0; i < files.size(); ++i) { | ||
taskq->push(i, 1); | ||
} | ||
|
||
workers.multiplexJob("VisibilityChecker", [&gs, &files, &barrier, taskq]() { | ||
workers.multiplexJob("VisibilityChecker", [&gs, &files, &barrier, taskq, resultq]() { | ||
size_t idx; | ||
for (auto result = taskq->try_pop(idx); !result.done(); result = taskq->try_pop(idx)) { | ||
ast::ParsedFile &f = files[idx]; | ||
if (!f.file.data(gs).isPackage(gs)) { | ||
auto pkgName = gs.packageDB().getPackageNameForFile(f.file); | ||
if (pkgName.exists()) { | ||
core::Context ctx{gs, core::Symbols::root(), f.file}; | ||
VisibilityCheckerPass pass{ctx, gs.packageDB().getPackageInfo(pkgName)}; | ||
ast::TreeWalk::apply(ctx, pass, f.tree); | ||
} | ||
if (f.file.data(gs).isPackage(gs)) { | ||
resultq->push({f.file, {}}, 1); | ||
continue; | ||
} | ||
|
||
auto pkgName = gs.packageDB().getPackageNameForFile(f.file); | ||
if (!pkgName.exists()) { | ||
resultq->push({f.file, {}}, 1); | ||
continue; | ||
} | ||
|
||
core::Context ctx{gs, core::Symbols::root(), f.file}; | ||
VisibilityCheckerPass pass{ctx, gs.packageDB().getPackageInfo(pkgName)}; | ||
ast::TreeWalk::apply(ctx, pass, f.tree); | ||
resultq->push({f.file, std::move(pass.missingImports)}, 1); | ||
} | ||
|
||
barrier.DecrementCount(); | ||
}); | ||
|
||
barrier.Wait(); | ||
|
||
std::pair<core::FileRef, UnorderedMap<std::pair<core::packages::MangledName, core::packages::ImportType>, | ||
Comment on lines
+632
to
+640
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this will have to be rebased across #8852. Even after that's done, we should make the previous loop here use |
||
core::AutocorrectSuggestion>> | ||
threadResult; | ||
for (auto result = resultq->wait_pop_timed(threadResult, WorkerPool::BLOCK_INTERVAL(), gs.tracer()); | ||
!result.done(); | ||
result = resultq->wait_pop_timed(threadResult, WorkerPool::BLOCK_INTERVAL(), gs.tracer())) { | ||
if (result.gotItem()) { | ||
auto file = threadResult.first; | ||
auto pkgName = gs.packageDB().getPackageNameForFile(file); | ||
if (!pkgName.exists()) { | ||
continue; | ||
} | ||
auto nonConstPackageInfo = nonConstGs.packageDB().getPackageInfoNonConst(pkgName); | ||
nonConstPackageInfo->untrackMissingImportsFor(file); | ||
for (auto [key, autocorrect] : threadResult.second) { | ||
auto toImport = key.first; | ||
auto importType = key.second; | ||
nonConstPackageInfo->trackMissingImport(file, toImport, importType, autocorrect); | ||
} | ||
} | ||
} | ||
|
||
return files; | ||
} | ||
}; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor nit: I would be inclined to declare the (
bool
-only) fields asbool isDidYouMean = false;
in the class definition and then this constructor could just beAutocorrectSuggestion() = default;
.