| // 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/inference/replace_macros.h" |
| |
| #include <cassert> |
| #include <string_view> |
| |
| #include "clang/Basic/IdentifierTable.h" |
| #include "clang/Basic/SourceLocation.h" |
| #include "clang/Basic/SourceManager.h" |
| #include "clang/Lex/MacroInfo.h" |
| #include "clang/Lex/PPCallbacks.h" |
| #include "llvm/ADT/StringRef.h" |
| #include "llvm/Support/Path.h" |
| |
| namespace clang::tidy::nullability { |
| namespace { |
| |
| // Prefix for the names of our copies of replaced macros. |
| // |
| // Used to identify whether a defined macro is itself a copy and to |
| // set each copy macro's definition equal to the pre-replacement definition of |
| // the macro it copies. |
| inline constexpr std::string_view CopyPrefix = "__clang_tidy_nullability_"; |
| } // namespace |
| |
| void ReplaceMacrosCallbacks::FileChanged(SourceLocation Loc, |
| FileChangeReason Reason, |
| SrcMgr::CharacteristicKind FileType, |
| FileID PrevFID) { |
| if (State == State::FinishedReplacementFile) return; |
| if (State == State::InReplacementFile && Reason == PPCallbacks::ExitFile) { |
| State = State::FinishedReplacementFile; |
| return; |
| } |
| |
| if (Reason != PPCallbacks::EnterFile) return; |
| |
| auto FileName = PP.getSourceManager().getFilename(Loc); |
| if (llvm::sys::path::remove_leading_dotslash(FileName) == |
| ReplacementMacrosHeaderFileName) { |
| State = State::InReplacementFile; |
| } |
| } |
| |
| void ReplaceMacrosCallbacks::MacroDefined(const clang::Token &MacroNameTok, |
| const clang::MacroDirective *MD) { |
| auto *IIForCurrentMacro = MacroNameTok.getIdentifierInfo(); |
| assert(IIForCurrentMacro); |
| |
| if (State == State::InReplacementFile) { |
| // The only macros in this file are the replacements, the copies of original |
| // definitions, and the header guard. |
| if (!IIForCurrentMacro->getName().starts_with(CopyPrefix)) { |
| // Replacements are seen before the original definitions, so cache the |
| // replacements for lookup later when we see the originals. |
| |
| // Unnecessary for the header guard, but not a problem, so don't bother |
| // filtering. |
| Replacements.insert({IIForCurrentMacro, MD}); |
| } |
| return; |
| } |
| |
| if (auto It = Replacements.find(IIForCurrentMacro); |
| It != Replacements.end()) { |
| const clang::MacroDirective *ReplacementDef = It->second; |
| |
| IdentifierInfo *CopyII = |
| PP.getIdentifierInfo((CopyPrefix + IIForCurrentMacro->getName()).str()); |
| |
| // Replace the (empty) definition of the copy with the pre-replacement |
| // definition for this macro. |
| PP.appendDefMacroDirective( |
| CopyII, const_cast<clang::MacroInfo *>(MD->getMacroInfo()), |
| MD->getLocation()); |
| |
| // Replace the definition of this to-be-replaced macro with the |
| // definition from the replacement file. |
| PP.appendDefMacroDirective( |
| IIForCurrentMacro, |
| const_cast<clang::MacroInfo *>(ReplacementDef->getMacroInfo()), |
| ReplacementDef->getLocation()); |
| } |
| } |
| |
| } // namespace clang::tidy::nullability |