diff --git a/tests/token_test.module b/tests/token_test.module
index 210a051..516ed8f 100644
--- a/tests/token_test.module
+++ b/tests/token_test.module
@@ -13,3 +13,32 @@ function token_test_exit() {
     variable_set('token_page_tokens', $debug);
   }
 }
+
+/**
+ * Implements hook_token_list().
+ */
+function token_test_token_list($type = 'all') {
+  $tokens = array();
+  // Provide some wildcard tokens
+  $tokens['token_test']['alpha']           = t("Token test Alpha.");
+  $tokens['token_test']['beta-?']          = t("Token test Beta with wildcard.");
+  $tokens['token_test']['gamma_???']       = t("Token test Gamme with wildcard.");
+  $tokens['token_test']['delta-*']         = t("Token test Delta with wildcard.");
+  $tokens['token_test']['epsilon-zeta-?']  = t("Token test Epsilon-Zeta with wildcard.");
+  return $tokens;
+}
+
+/**
+ * Implements hook_token_values().
+ */
+function token_test_token_values($type, $object = NULL) {
+  $tokens = array();
+  // Provide some wildcard token values
+  $tokens['alpha']           = 'Alpha';
+  $tokens['beta-1']          = 'Beta plus 1';
+  $tokens['beta-2']          = 'Beta plus 2';
+  $tokens['gamma_A']         = 'Gamma plus A';
+  $tokens['delta-extra']     = 'Delta plus extra';
+  $tokens['epsilon-zeta-A']  = 'Epsilon-Zeta plus A';
+  return $tokens;
+}
diff --git a/token.module b/token.module
index 4a89077..09b041a 100644
--- a/token.module
+++ b/token.module
@@ -647,6 +647,17 @@ function token_get_invalid_tokens_by_context($value, $valid_types = array(), $le
   }
 
   array_unique($invalid_tokens);
+
+  // Some modules use wildcard suffixes like _? or -???? or -*.
+  if (!empty($invalid_tokens)) {
+    foreach (array_keys($valid_tokens) as $token) {
+      if (preg_match('/^(.*[_-])[?*]+$/', $token, $wildcards)) {
+        $matched = preg_grep('/^' . $wildcards[1] . '.+$/', $invalid_tokens);
+        $invalid_tokens = array_diff($invalid_tokens, $matched);
+      }
+    }
+  }
+
   $invalid_tokens = token_prepare_tokens($invalid_tokens, $leading, $trailing);
   return $invalid_tokens;
 }
diff --git a/token.test b/token.test
index 78a5ce7..2b8bf4f 100644
--- a/token.test
+++ b/token.test
@@ -128,6 +128,29 @@ class TokenUnitTestCase extends TokenTestHelper {
       ),
       'types' => array('all'),
     );
+    $tests[] = array(
+      'valid tokens' => array(
+        '[alpha]',
+        '[beta-1]',
+        '[beta-2]',
+        '[gamma_A]',
+        '[delta-extra]',
+        '[epsilon-zeta-A]',
+      ),
+      'invalid tokens' => array(
+        '[alpha-plus]',
+        '[beta]',
+        '[beta-]',
+        '[beta_]',
+        '[beta_1]',
+        '[gamma]',
+        '[gamma_]',
+        '[gamma-A]',
+        '[delta]',
+        '[epsilon-zeta-]',
+      ),
+      'types' => array('all'),
+    );
 
     foreach ($tests as $test) {
       $tokens = array_merge($test['valid tokens'], $test['invalid tokens']);
@@ -192,6 +215,39 @@ class TokenUnitTestCase extends TokenTestHelper {
       $this->assertEqual($output, $expected);
     }
   }
+
+  /**
+   * Test wildcard tokens.
+   */
+  public function testWildcardTokens() {
+    $tokens = array(
+      // valid values
+      'alpha' => 'Alpha',
+      'beta-1' => 'Beta plus 1',
+      'beta-2' => 'Beta plus 2',
+      'gamma_A' => 'Gamma plus A',
+      'delta-extra' => 'Delta plus extra',
+      'epsilon-zeta-A' => 'Epsilon-Zeta plus A',
+      // invalid: doesn't match wildcard
+      'alpha-plus' => NULL,
+      'beta' => NULL,
+      'beta-' => NULL,
+      'beta_' => NULL,
+      'beta_1' => NULL,
+      'gamma' => NULL,
+      'gamma_' => NULL,
+      'gamma-A' => NULL,
+      'delta' => NULL,
+      'epsilon-zeta-' => NULL,
+      // invalid: no value provided
+      'beta-3' => NULL,
+      'gamma-B' => NULL,
+      'delta-rocket' => NULL,
+      'epsilon-zeta-B' => NULL,
+    );
+
+    $this->assertTokens('global', NULL, $tokens);
+  }
 }
 
 class TokenNodeTestCase extends TokenTestHelper {
