| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #include "UseCmsysFstreamCheck.h" | |
| #include <clang/ASTMatchers/ASTMatchFinder.h> | |
| namespace clang { | |
| namespace tidy { | |
| namespace cmake { | |
| using namespace ast_matchers; | |
| UseCmsysFstreamCheck::UseCmsysFstreamCheck(StringRef Name, | |
| ClangTidyContext* Context) | |
| : ClangTidyCheck(Name, Context) | |
| { | |
| } | |
| void UseCmsysFstreamCheck::registerMatchers(MatchFinder* Finder) | |
| { | |
| this->createMatcher("::std::basic_ifstream", "::cmsys::ifstream", Finder, | |
| "ifstream"); | |
| this->createMatcher("::std::basic_ofstream", "::cmsys::ofstream", Finder, | |
| "ofstream"); | |
| this->createMatcher("::std::basic_fstream", "::cmsys::fstream", Finder, | |
| "fstream"); | |
| } | |
| void UseCmsysFstreamCheck::check(MatchFinder::MatchResult const& Result) | |
| { | |
| TypeLoc const* ParentTypeNode = | |
| Result.Nodes.getNodeAs<TypeLoc>("parentType"); | |
| NestedNameSpecifierLoc const* ParentNameNode = | |
| Result.Nodes.getNodeAs<NestedNameSpecifierLoc>("parentName"); | |
| TypeLoc const* RootNode = nullptr; | |
| StringRef BindName; | |
| StringRef Warning; | |
| if ((RootNode = Result.Nodes.getNodeAs<TypeLoc>("ifstream")) != nullptr) { | |
| BindName = "cmsys::ifstream"; | |
| Warning = "use cmsys::ifstream"; | |
| } else if ((RootNode = Result.Nodes.getNodeAs<TypeLoc>("ofstream")) != | |
| nullptr) { | |
| BindName = "cmsys::ofstream"; | |
| Warning = "use cmsys::ofstream"; | |
| } else if ((RootNode = Result.Nodes.getNodeAs<TypeLoc>("fstream")) != | |
| nullptr) { | |
| BindName = "cmsys::fstream"; | |
| Warning = "use cmsys::fstream"; | |
| } | |
| if (ParentTypeNode != nullptr) { | |
| if (ParentTypeNode->getBeginLoc().isValid()) { | |
| this->diag(ParentTypeNode->getBeginLoc(), Warning) | |
| << FixItHint::CreateReplacement(ParentTypeNode->getSourceRange(), | |
| BindName); | |
| } | |
| } else if (ParentNameNode != nullptr) { | |
| if (ParentNameNode->getBeginLoc().isValid()) { | |
| this->diag(ParentNameNode->getBeginLoc(), Warning) | |
| << FixItHint::CreateReplacement( | |
| SourceRange(ParentNameNode->getBeginLoc(), RootNode->getEndLoc()), | |
| BindName); | |
| } | |
| } else if (RootNode != nullptr) { | |
| if (RootNode->getBeginLoc().isValid()) { | |
| this->diag(RootNode->getBeginLoc(), Warning) | |
| << FixItHint::CreateReplacement(RootNode->getSourceRange(), BindName); | |
| } | |
| } | |
| } | |
| void UseCmsysFstreamCheck::createMatcher(StringRef StdName, | |
| StringRef CmsysName, | |
| ast_matchers::MatchFinder* Finder, | |
| StringRef Bind) | |
| { | |
| TypeLocMatcher IsStd = loc(qualType(hasUnqualifiedDesugaredType( | |
| recordType(hasDeclaration(classTemplateSpecializationDecl( | |
| hasName(StdName), | |
| hasTemplateArgument( | |
| 0, templateArgument(refersToType(asString("char")))))))))); | |
| // TODO This only checks to see if the type directly refers to | |
| // cmsys::fstream. There are some corner cases involving template parameters | |
| // that refer to cmsys::fstream that are missed by this matcher, resulting in | |
| // a false positive. Figure out how to find these indirect references to | |
| // cmsys::fstream and filter them out. In the meantime, such false positives | |
| // can be silenced with NOLINT(cmake-use-cmsys-fstream). | |
| TypeLocMatcher IsCmsys = | |
| loc(usingType(throughUsingDecl(namedDecl(hasName(CmsysName))))); | |
| Finder->addMatcher( | |
| typeLoc(IsStd, unless(IsCmsys), | |
| optionally(hasParent(nestedNameSpecifierLoc().bind("parentName")))) | |
| .bind(Bind), | |
| this); | |
| } | |
| } | |
| } | |
| } |