njames93 updated this revision to Diff 328664.
njames93 added a comment.

Address clang-tidy issue.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D97963/new/

https://reviews.llvm.org/D97963

Files:
  clang/include/clang/ASTMatchers/Dynamic/Diagnostics.h
  clang/include/clang/ASTMatchers/Dynamic/Parser.h
  clang/include/clang/ASTMatchers/Dynamic/Registry.h
  clang/lib/ASTMatchers/Dynamic/CMakeLists.txt
  clang/lib/ASTMatchers/Dynamic/Diagnostics.cpp
  clang/lib/ASTMatchers/Dynamic/Parser.cpp
  clang/lib/ASTMatchers/Dynamic/Registry.cpp
  clang/lib/ASTMatchers/Dynamic/TypoSuggester.cpp
  clang/lib/ASTMatchers/Dynamic/TypoSuggester.h

Index: clang/lib/ASTMatchers/Dynamic/TypoSuggester.h
===================================================================
--- /dev/null
+++ clang/lib/ASTMatchers/Dynamic/TypoSuggester.h
@@ -0,0 +1,54 @@
+//===- TypoSuggester.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_ASTMATCHERS_DYNAMIC_TYPOSUGGESTER_H
+#define LLVM_CLANG_LIB_ASTMATCHERS_DYNAMIC_TYPOSUGGESTER_H
+
+#include "llvm/ADT/StringRef.h"
+
+namespace clang {
+namespace ast_matchers {
+namespace dynamic {
+// Helper class for getting the closest match for a typo string.
+// If OwnSuggestions is true, temporary strings can be passed to addSuggestion.
+class TypoSuggester {
+  llvm::StringRef Current;
+  const llvm::StringRef Value;
+  unsigned BestDistance;
+  bool HasMatch = false;
+
+public:
+  enum AutoMaxEditT { AutoMaxEdit };
+  TypoSuggester(llvm::StringRef Value, AutoMaxEditT)
+      : Value(Value), BestDistance((Value.size() + 3U) / 5U) {
+    // This BestDistance is rather arbitrary but prevents generating suggestions
+    // for single character values and grows more lenient as the value grows.
+  }
+  TypoSuggester(llvm::StringRef Target,
+                unsigned MaxEdit = std::numeric_limits<unsigned>::max())
+      : Value(Target), BestDistance(MaxEdit) {
+    assert(MaxEdit > 0);
+  }
+
+  // Returns true if we reach a state where we can never find a unique closest
+  // match.
+  bool addSuggestion(llvm::StringRef Suggest);
+  // If true, we are in a state where we can never find a unique closest match.
+  bool hasFailed() const { return BestDistance == 0; }
+  bool foundSuggestion() const { return HasMatch; }
+  llvm::StringRef getSuggestion() const {
+    assert(foundSuggestion());
+    return Current;
+  }
+};
+
+} // namespace dynamic
+} // namespace ast_matchers
+} // namespace clang
+
+#endif // LLVM_CLANG_LIB_ASTMATCHERS_DYNAMIC_TYPOSUGGESTER_H
Index: clang/lib/ASTMatchers/Dynamic/TypoSuggester.cpp
===================================================================
--- /dev/null
+++ clang/lib/ASTMatchers/Dynamic/TypoSuggester.cpp
@@ -0,0 +1,39 @@
+//===- TypoSuggester.cpp --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "TypoSuggester.h"
+
+namespace clang {
+namespace ast_matchers {
+namespace dynamic {
+bool TypoSuggester::addSuggestion(llvm::StringRef Suggest) {
+  unsigned Dist = Value.edit_distance(Suggest, true, BestDistance);
+  if (Dist > BestDistance)
+    return false;
+  if (Dist == BestDistance) {
+    if (HasMatch) {
+      // If we already have a completion candidate with the same edit
+      // distance, we can't be sure so disregard both.
+      HasMatch = false;
+      --BestDistance;
+      // As we assume Target will never appear in Suggest, once we reach a
+      // case where we need 0 edits, we can stop work.
+      return BestDistance == 0;
+    }
+    Current = Suggest;
+    HasMatch = true;
+    return false;
+  }
+  Current = Suggest;
+  HasMatch = true;
+  BestDistance = Dist;
+  return false;
+}
+} // namespace dynamic
+} // namespace ast_matchers
+} // namespace clang
\ No newline at end of file
Index: clang/lib/ASTMatchers/Dynamic/Registry.cpp
===================================================================
--- clang/lib/ASTMatchers/Dynamic/Registry.cpp
+++ clang/lib/ASTMatchers/Dynamic/Registry.cpp
@@ -13,6 +13,7 @@
 
 #include "clang/ASTMatchers/Dynamic/Registry.h"
 #include "Marshallers.h"
+#include "TypoSuggester.h"
 #include "clang/AST/ASTTypeTraits.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/ASTMatchers/Dynamic/Diagnostics.h"
@@ -586,11 +587,25 @@
 }
 
 // static
-llvm::Optional<MatcherCtor> Registry::lookupMatcherCtor(StringRef MatcherName) {
+llvm::Optional<MatcherCtor>
+Registry::lookupMatcherCtor(StringRef MatcherName, StringRef *SuggestedTypo) {
   auto it = RegistryData->constructors().find(MatcherName);
-  return it == RegistryData->constructors().end()
-             ? llvm::Optional<MatcherCtor>()
-             : it->second.get();
+  if (it != RegistryData->constructors().end())
+    return it->second.get();
+  if (SuggestedTypo) {
+    TypoSuggester T(MatcherName, TypoSuggester::AutoMaxEdit);
+    for (const auto &Ctor : RegistryData->constructors()) {
+      if (T.addSuggestion(Ctor.getKey()))
+        break;
+    }
+    if (T.foundSuggestion()) {
+      *SuggestedTypo = T.getSuggestion();
+      // If we found a good suggestion, return the Ctor so parsing can continue,
+      // The error state is conveyed by SuggestedTypo being written to.
+      return RegistryData->constructors().find(T.getSuggestion())->second.get();
+    }
+  }
+  return llvm::None;
 }
 
 static llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
Index: clang/lib/ASTMatchers/Dynamic/Parser.cpp
===================================================================
--- clang/lib/ASTMatchers/Dynamic/Parser.cpp
+++ clang/lib/ASTMatchers/Dynamic/Parser.cpp
@@ -12,6 +12,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/ASTMatchers/Dynamic/Parser.h"
+#include "TypoSuggester.h"
 #include "clang/ASTMatchers/ASTMatchersInternal.h"
 #include "clang/ASTMatchers/Dynamic/Diagnostics.h"
 #include "clang/ASTMatchers/Dynamic/Registry.h"
@@ -354,7 +355,7 @@
 /// In case of failure it will try to determine the user's intent to give
 /// an appropriate error message.
 bool Parser::parseIdentifierPrefixImpl(VariantValue *Value) {
-  const TokenInfo NameToken = Tokenizer->consumeNextToken();
+  TokenInfo NameToken = Tokenizer->consumeNextToken();
 
   if (Tokenizer->nextTokenKind() != TokenInfo::TK_OpenParen) {
     // Parse as a named value.
@@ -421,8 +422,20 @@
          Tokenizer->nextTokenKind() == TokenInfo::TK_NewLine ||
          Tokenizer->nextTokenKind() == TokenInfo::TK_Eof) &&
         !S->lookupMatcherCtor(NameToken.Text)) {
-      Error->addError(NameToken.Range, Error->ET_RegistryValueNotFound)
-          << NameToken.Text;
+      TypoSuggester T(NameToken.Text, TypoSuggester::AutoMaxEdit);
+      if (NamedValues) {
+        for (const auto &NamedValue : *NamedValues) {
+          if (T.addSuggestion(NamedValue.getKey()))
+            break;
+        }
+      }
+      if (T.foundSuggestion())
+        Error->addError(NameToken.Range,
+                        Error->ET_RegistryValueNotFoundWithReplace)
+            << NameToken.Text << T.getSuggestion();
+      else
+        Error->addError(NameToken.Range, Error->ET_RegistryValueNotFound)
+            << NameToken.Text;
       return false;
     }
     // Otherwise, fallback to the matcher parser.
@@ -438,10 +451,21 @@
     return false;
   }
 
-  llvm::Optional<MatcherCtor> Ctor = S->lookupMatcherCtor(NameToken.Text);
+  StringRef Typo;
+  llvm::Optional<MatcherCtor> Ctor =
+      S->lookupMatcherCtor(NameToken.Text, &Typo);
+
+  if (!Typo.empty()) {
+    Error->addError(NameToken.Range,
+                    Error->ET_RegistryMatcherNotFoundWithReplace)
+        << NameToken.Text << Typo;
+    // Update the NameToken so diagnostics are better.
+    NameToken.Text = Typo;
+  }
 
   // Parse as a matcher expression.
-  return parseMatcherExpressionImpl(NameToken, OpenToken, Ctor, Value);
+  return parseMatcherExpressionImpl(NameToken, OpenToken, Ctor, Value) &&
+         Typo.empty();
 }
 
 bool Parser::parseBindID(std::string &BindID) {
@@ -474,6 +498,8 @@
   std::vector<ParserValue> Args;
   TokenInfo EndToken;
 
+  bool HasError = false;
+
   Tokenizer->SkipNewlines();
 
   {
@@ -517,8 +543,10 @@
       ArgValue.Text = NodeMatcherToken.Text;
       ArgValue.Range = NodeMatcherToken.Range;
 
+      StringRef Typo;
+
       llvm::Optional<MatcherCtor> MappedMatcher =
-          S->lookupMatcherCtor(ArgValue.Text);
+          S->lookupMatcherCtor(ArgValue.Text, &Typo);
 
       if (!MappedMatcher) {
         Error->addError(NodeMatcherToken.Range,
@@ -526,6 +554,12 @@
             << NodeMatcherToken.Text;
         return false;
       }
+      if (!Typo.empty()) {
+        Error->addError(NodeMatcherToken.Range,
+                        Error->ET_RegistryMatcherNotFoundWithReplace)
+            << NodeMatcherToken.Text << Typo;
+        HasError = true;
+      }
 
       ASTNodeKind NK = S->nodeMatcherType(*MappedMatcher);
 
@@ -588,7 +622,7 @@
         return false;
 
       *Value = Result;
-      return true;
+      return !HasError;
     } else if (ChainCallToken.Text == TokenInfo::ID_With) {
       Tokenizer->SkipNewlines();
 
@@ -605,7 +639,8 @@
       TokenInfo WithOpenToken = Tokenizer->consumeNextToken();
 
       return parseMatcherExpressionImpl(NameToken, WithOpenToken,
-                                        BuiltCtor.get(), Value);
+                                        BuiltCtor.get(), Value) &&
+             !HasError;
     }
   }
 
@@ -619,7 +654,7 @@
     return false;
 
   *Value = Result;
-  return true;
+  return !HasError;
 }
 
 /// Parse and validate a matcher expression.
@@ -829,8 +864,9 @@
 Parser::RegistrySema::~RegistrySema() = default;
 
 llvm::Optional<MatcherCtor>
-Parser::RegistrySema::lookupMatcherCtor(StringRef MatcherName) {
-  return Registry::lookupMatcherCtor(MatcherName);
+Parser::RegistrySema::lookupMatcherCtor(StringRef MatcherName,
+                                        StringRef *SuggestedTypo) {
+  return Registry::lookupMatcherCtor(MatcherName, SuggestedTypo);
 }
 
 VariantMatcher Parser::RegistrySema::actOnMatcherExpression(
Index: clang/lib/ASTMatchers/Dynamic/Diagnostics.cpp
===================================================================
--- clang/lib/ASTMatchers/Dynamic/Diagnostics.cpp
+++ clang/lib/ASTMatchers/Dynamic/Diagnostics.cpp
@@ -87,6 +87,8 @@
   switch (Type) {
   case Diagnostics::ET_RegistryMatcherNotFound:
     return "Matcher not found: $0";
+  case Diagnostics::ET_RegistryMatcherNotFoundWithReplace:
+    return "Matcher not found: $0; did you mean $1?";
   case Diagnostics::ET_RegistryWrongArgCount:
     return "Incorrect argument count. (Expected = $0) != (Actual = $1)";
   case Diagnostics::ET_RegistryWrongArgType:
@@ -98,6 +100,8 @@
     return "Ambiguous matcher overload.";
   case Diagnostics::ET_RegistryValueNotFound:
     return "Value not found: $0";
+  case Diagnostics::ET_RegistryValueNotFoundWithReplace:
+    return "Value not found: $0; did you mean $1?";
   case Diagnostics::ET_RegistryUnknownEnumWithReplace:
     return "Unknown value '$1' for arg $0; did you mean '$2'";
   case Diagnostics::ET_RegistryNonNodeMatcher:
Index: clang/lib/ASTMatchers/Dynamic/CMakeLists.txt
===================================================================
--- clang/lib/ASTMatchers/Dynamic/CMakeLists.txt
+++ clang/lib/ASTMatchers/Dynamic/CMakeLists.txt
@@ -8,6 +8,7 @@
   Marshallers.cpp
   Parser.cpp
   Registry.cpp
+  TypoSuggester.cpp
   VariantValue.cpp
 
   LINK_LIBS
Index: clang/include/clang/ASTMatchers/Dynamic/Registry.h
===================================================================
--- clang/include/clang/ASTMatchers/Dynamic/Registry.h
+++ clang/include/clang/ASTMatchers/Dynamic/Registry.h
@@ -95,7 +95,8 @@
   ///
   /// \return An opaque value which may be used to refer to the matcher
   /// constructor, or Optional<MatcherCtor>() if not found.
-  static llvm::Optional<MatcherCtor> lookupMatcherCtor(StringRef MatcherName);
+  static llvm::Optional<MatcherCtor> lookupMatcherCtor(StringRef MatcherName,
+                                                       StringRef *SuggestTypo);
 
   /// Compute the list of completion types for \p Context.
   ///
Index: clang/include/clang/ASTMatchers/Dynamic/Parser.h
===================================================================
--- clang/include/clang/ASTMatchers/Dynamic/Parser.h
+++ clang/include/clang/ASTMatchers/Dynamic/Parser.h
@@ -95,10 +95,16 @@
     ///
     /// \param MatcherName The matcher name found by the parser.
     ///
-    /// \return The matcher constructor, or Optional<MatcherCtor>() if not
-    /// found.
+    /// \param SuggestedTypo If non-null and no matcher constructor was found,
+    /// This will be set to the closest matching name if any exists.
+    ///
+    /// \return The matcher constructor if found. If match not found but \p
+    /// SuggestedTypo was non-null and a close match was found, the
+    /// corresponding match constructor will be returned to let further error
+    /// checking continue.
     virtual llvm::Optional<MatcherCtor>
-    lookupMatcherCtor(StringRef MatcherName) = 0;
+    lookupMatcherCtor(StringRef MatcherName,
+                      StringRef *SuggestedTypo = nullptr) = 0;
 
     virtual bool isBuilderMatcher(MatcherCtor) const = 0;
 
@@ -139,7 +145,7 @@
     ~RegistrySema() override;
 
     llvm::Optional<MatcherCtor>
-    lookupMatcherCtor(StringRef MatcherName) override;
+    lookupMatcherCtor(StringRef MatcherName, StringRef *SuggestedTypo) override;
 
     VariantMatcher actOnMatcherExpression(MatcherCtor Ctor,
                                           SourceRange NameRange,
Index: clang/include/clang/ASTMatchers/Dynamic/Diagnostics.h
===================================================================
--- clang/include/clang/ASTMatchers/Dynamic/Diagnostics.h
+++ clang/include/clang/ASTMatchers/Dynamic/Diagnostics.h
@@ -60,14 +60,16 @@
     ET_None = 0,
 
     ET_RegistryMatcherNotFound = 1,
-    ET_RegistryWrongArgCount = 2,
-    ET_RegistryWrongArgType = 3,
-    ET_RegistryNotBindable = 4,
-    ET_RegistryAmbiguousOverload = 5,
-    ET_RegistryValueNotFound = 6,
-    ET_RegistryUnknownEnumWithReplace = 7,
-    ET_RegistryNonNodeMatcher = 8,
-    ET_RegistryMatcherNoWithSupport = 9,
+    ET_RegistryMatcherNotFoundWithReplace = 2,
+    ET_RegistryWrongArgCount = 3,
+    ET_RegistryWrongArgType = 4,
+    ET_RegistryNotBindable = 5,
+    ET_RegistryAmbiguousOverload = 6,
+    ET_RegistryValueNotFound = 7,
+    ET_RegistryValueNotFoundWithReplace = 8,
+    ET_RegistryUnknownEnumWithReplace = 9,
+    ET_RegistryNonNodeMatcher = 10,
+    ET_RegistryMatcherNoWithSupport = 11,
 
     ET_ParserStringError = 100,
     ET_ParserNoOpenParen = 101,
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to