UseCmsysFstreamCheck.cxx 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file LICENSE.rst or https://cmake.org/licensing for details. */
  3. #include "UseCmsysFstreamCheck.h"
  4. #include <clang/ASTMatchers/ASTMatchFinder.h>
  5. namespace clang {
  6. namespace tidy {
  7. namespace cmake {
  8. using namespace ast_matchers;
  9. UseCmsysFstreamCheck::UseCmsysFstreamCheck(StringRef Name,
  10. ClangTidyContext* Context)
  11. : ClangTidyCheck(Name, Context)
  12. {
  13. }
  14. void UseCmsysFstreamCheck::registerMatchers(MatchFinder* Finder)
  15. {
  16. this->createMatcher("::std::basic_ifstream", "::cmsys::ifstream", Finder,
  17. "ifstream");
  18. this->createMatcher("::std::basic_ofstream", "::cmsys::ofstream", Finder,
  19. "ofstream");
  20. this->createMatcher("::std::basic_fstream", "::cmsys::fstream", Finder,
  21. "fstream");
  22. }
  23. void UseCmsysFstreamCheck::check(MatchFinder::MatchResult const& Result)
  24. {
  25. TypeLoc const* ParentTypeNode =
  26. Result.Nodes.getNodeAs<TypeLoc>("parentType");
  27. NestedNameSpecifierLoc const* ParentNameNode =
  28. Result.Nodes.getNodeAs<NestedNameSpecifierLoc>("parentName");
  29. TypeLoc const* RootNode = nullptr;
  30. StringRef BindName;
  31. StringRef Warning;
  32. if ((RootNode = Result.Nodes.getNodeAs<TypeLoc>("ifstream")) != nullptr) {
  33. BindName = "cmsys::ifstream";
  34. Warning = "use cmsys::ifstream";
  35. } else if ((RootNode = Result.Nodes.getNodeAs<TypeLoc>("ofstream")) !=
  36. nullptr) {
  37. BindName = "cmsys::ofstream";
  38. Warning = "use cmsys::ofstream";
  39. } else if ((RootNode = Result.Nodes.getNodeAs<TypeLoc>("fstream")) !=
  40. nullptr) {
  41. BindName = "cmsys::fstream";
  42. Warning = "use cmsys::fstream";
  43. }
  44. if (ParentTypeNode != nullptr) {
  45. if (ParentTypeNode->getBeginLoc().isValid()) {
  46. this->diag(ParentTypeNode->getBeginLoc(), Warning)
  47. << FixItHint::CreateReplacement(ParentTypeNode->getSourceRange(),
  48. BindName);
  49. }
  50. } else if (ParentNameNode != nullptr) {
  51. if (ParentNameNode->getBeginLoc().isValid()) {
  52. this->diag(ParentNameNode->getBeginLoc(), Warning)
  53. << FixItHint::CreateReplacement(
  54. SourceRange(ParentNameNode->getBeginLoc(), RootNode->getEndLoc()),
  55. BindName);
  56. }
  57. } else if (RootNode != nullptr) {
  58. if (RootNode->getBeginLoc().isValid()) {
  59. this->diag(RootNode->getBeginLoc(), Warning)
  60. << FixItHint::CreateReplacement(RootNode->getSourceRange(), BindName);
  61. }
  62. }
  63. }
  64. void UseCmsysFstreamCheck::createMatcher(StringRef StdName,
  65. StringRef CmsysName,
  66. ast_matchers::MatchFinder* Finder,
  67. StringRef Bind)
  68. {
  69. TypeLocMatcher IsStd = loc(qualType(hasUnqualifiedDesugaredType(
  70. recordType(hasDeclaration(classTemplateSpecializationDecl(
  71. hasName(StdName),
  72. hasTemplateArgument(
  73. 0, templateArgument(refersToType(asString("char"))))))))));
  74. // TODO This only checks to see if the type directly refers to
  75. // cmsys::fstream. There are some corner cases involving template parameters
  76. // that refer to cmsys::fstream that are missed by this matcher, resulting in
  77. // a false positive. Figure out how to find these indirect references to
  78. // cmsys::fstream and filter them out. In the meantime, such false positives
  79. // can be silenced with NOLINT(cmake-use-cmsys-fstream).
  80. TypeLocMatcher IsCmsys =
  81. loc(usingType(throughUsingDecl(namedDecl(hasName(CmsysName)))));
  82. Finder->addMatcher(
  83. typeLoc(IsStd, unless(IsCmsys), unless(elaboratedTypeLoc()),
  84. optionally(hasParent(elaboratedTypeLoc().bind("parentType"))),
  85. optionally(hasParent(nestedNameSpecifierLoc().bind("parentName"))))
  86. .bind(Bind),
  87. this);
  88. }
  89. }
  90. }
  91. }