blob: b3dd5d783091c7e7c53a7dc5bb96b8b7f1f06fe9 [file] [log] [blame]
// Part of the Crubit project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "nullability/loc_filter.h"
#include <memory>
#include <string>
#include "absl/log/check.h"
#include "clang/Basic/FileEntry.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Path.h"
namespace clang::tidy::nullability {
namespace {
// A filter for SourceLocations that restricts to those in the main file or its
// associated header.
class InMainFileOrHeader : public LocFilter {
private:
OptionalFileEntryRef MainFile;
std::string MainFileStem;
const SourceManager &SM;
llvm::DenseMap<FileID, bool> IsMainFileOrHeaderCache;
public:
InMainFileOrHeader(const SourceManager &SM) : SM(SM) {
FileID MainFileID = SM.getMainFileID();
MainFile = SM.getFileEntryRefForID(MainFileID);
CHECK(MainFile) << "Unable to compute main file for filtering.";
llvm::StringRef MainFileName = MainFile->getName();
CHECK(!MainFileName.empty());
MainFileStem = llvm::sys::path::stem(MainFileName);
IsMainFileOrHeaderCache.insert({MainFileID, true});
}
bool isMainFileOrHeader(FileID FileID) {
OptionalFileEntryRef FileEntry = SM.getFileEntryRefForID(FileID);
if (!FileEntry) return false;
if (FileEntry->getDir() != MainFile->getDir()) return false;
return llvm::sys::path::stem(FileEntry->getName()) == MainFileStem;
}
// Returns whether `loc` is in the main file or its associated header (i.e.
// a header that has the same file path except for the extension).
bool check(SourceLocation Loc) override {
if (Loc.isInvalid()) return false;
if (SM.isInMainFile(Loc)) {
return true;
}
auto FileID = SM.getFileID(Loc);
if (FileID.isInvalid()) return false;
// Insert true (arbitrarily chosen over false) to avoid pre-computing the
// result for cache hits. This does not overwrite the existing value for
// cache hits.
auto [It, Inserted] = IsMainFileOrHeaderCache.try_emplace(FileID, true);
if (Inserted) {
// Compare the directory and the stem, but not the file extension, to
// allow matches for the main implementation file and the associated
// header.
It->second = isMainFileOrHeader(FileID);
}
return It->second;
}
};
// A filter that allows all locations.
class NoOpFilter : public LocFilter {
bool check(SourceLocation) override { return true; }
};
} // namespace
std::unique_ptr<LocFilter> getLocFilter(const SourceManager &SM,
bool RestrictToMainFileOrHeader) {
if (RestrictToMainFileOrHeader) {
return std::make_unique<InMainFileOrHeader>(SM);
}
return std::make_unique<NoOpFilter>();
}
} // namespace clang::tidy::nullability