diff --git a/infer/src/IR/Fieldname.re b/infer/src/IR/Fieldname.re
index 6a9b18a98..f5a327d6f 100644
--- a/infer/src/IR/Fieldname.re
+++ b/infer/src/IR/Fieldname.re
@@ -10,9 +10,11 @@ open! IStd;
 
 let module Hashtbl = Caml.Hashtbl;
 
+type clang_field_info = {qual_class: QualifiedCppName.t, field_name: string} [@@deriving compare];
+
 type t =
   | Hidden /* Backend relies that Hidden is the smallest (first) field in Abs.should_raise_objc_leak */
-  | Clang Mangled.t
+  | Clang clang_field_info
   | Java string
 [@@deriving compare];
 
@@ -31,14 +33,10 @@ let module Map = Caml.Map.Make {
 };
 
 let module Clang = {
-
-  /** Create a field name with the given position (field number in the CSU) */
-  let create (n: Mangled.t) => Clang n;
+  let from_qualified qual_class field_name => Clang {qual_class, field_name};
 };
 
 let module Java = {
-
-  /** Create a field name with the given position (field number in the CSU) */
   let from_string n => Java n;
 };
 
@@ -48,15 +46,7 @@ let to_string =
   fun
   | Hidden => hidden_str
   | Java fname => fname
-  | Clang fname => Mangled.to_string fname;
-
-
-/** Convert a fieldname to a string, including the mangled part. */
-let to_complete_string =
-  fun
-  | Hidden => hidden_str
-  | Java fname => fname
-  | Clang fname => Mangled.to_string_full fname;
+  | Clang {field_name} => field_name;
 
 
 /** Convert a fieldname to a simplified string with at most one-level path. */
@@ -85,8 +75,8 @@ let to_flat_string fn => {
 let pp f =>
   fun
   | Hidden => Format.fprintf f "%s" hidden_str
-  | Java fname => Format.fprintf f "%s" fname
-  | Clang fname => Mangled.pp f fname;
+  | Java field_name
+  | Clang {field_name} => Format.fprintf f "%s" field_name;
 
 let pp_latex style f fn => Latex.pp_string style f (to_string fn);
 
@@ -119,6 +109,11 @@ let java_is_outer_instance fn => {
   }
 };
 
+let clang_get_qual_class =
+  fun
+  | Clang {qual_class} => Some qual_class
+  | _ => None;
+
 
 /** hidded fieldname constant */
 let hidden = Hidden;
diff --git a/infer/src/IR/Fieldname.rei b/infer/src/IR/Fieldname.rei
index 07d24f0c1..32b68f8be 100644
--- a/infer/src/IR/Fieldname.rei
+++ b/infer/src/IR/Fieldname.rei
@@ -26,13 +26,13 @@ let module Map: Caml.Map.S with type key = t;
 
 let module Clang: {
 
-  /** Create a clang field name */
-  let create: Mangled.t => t;
+  /** Create a clang field name from qualified c++ name */
+  let from_qualified: QualifiedCppName.t => string => t;
 };
 
 let module Java: {
 
-  /** Create a java field name */
+  /** Create a java field name from string */
   let from_string: string => t;
 };
 
@@ -41,10 +41,6 @@ let module Java: {
 let to_string: t => string;
 
 
-/** Convert a fieldname to a string, including the mangled part. */
-let to_complete_string: t => string;
-
-
 /** Convert a fieldname to a simplified string with at most one-level path. */
 let to_simplified_string: t => string;
 
@@ -73,6 +69,10 @@ let java_get_field: t => string;
 let java_is_outer_instance: t => bool;
 
 
+/** get qualified classname of a field if it's coming from clang frontend. returns None otherwise */
+let clang_get_qual_class: t => option QualifiedCppName.t;
+
+
 /** hidded fieldname constant */
 let hidden: t;
 
diff --git a/infer/src/IR/QualifiedCppName.re b/infer/src/IR/QualifiedCppName.re
index 56c4ea771..f9257511e 100644
--- a/infer/src/IR/QualifiedCppName.re
+++ b/infer/src/IR/QualifiedCppName.re
@@ -8,7 +8,9 @@
  */
 open! IStd;
 
-type t = list string;
+type t = list string [@@deriving compare];
+
+let equal = [%compare.equal : t];
 
 let empty = [];
 
diff --git a/infer/src/IR/QualifiedCppName.rei b/infer/src/IR/QualifiedCppName.rei
index 8f9cfc2b4..e21f53979 100644
--- a/infer/src/IR/QualifiedCppName.rei
+++ b/infer/src/IR/QualifiedCppName.rei
@@ -8,12 +8,13 @@
  */
 open! IStd;
 
-type t;
+type t [@@deriving compare];
 
 
 /** empty qualified name */
 let empty: t;
 
+let equal: t => t => bool;
 
 /** attempts to parse the argument into a list::of::possibly::templated<T>::qualifiers */
 let of_qual_string: string => t;
@@ -26,7 +27,6 @@ let to_qual_string: t => string;
 /** append qualifier to the end (innermost scope) of the qualified name */
 let append_qualifier: t => qual::string => t;
 
-
 /** returns list of qualifers */
 let to_list: t => list string;
 
diff --git a/infer/src/IR/Typ.re b/infer/src/IR/Typ.re
index abc10816b..d343d7351 100644
--- a/infer/src/IR/Typ.re
+++ b/infer/src/IR/Typ.re
@@ -884,11 +884,14 @@ let module Procname = {
 
   /** Pretty print a set of proc names */
   let pp_set fmt set => Set.iter (fun pname => F.fprintf fmt "%a " pp pname) set;
+  let objc_cpp_get_class_qualifiers objc_cpp => QualifiedCppName.of_qual_string (
+    Name.name objc_cpp.class_name
+  );
   let get_qualifiers pname =>
     switch pname {
     | C {name} => QualifiedCppName.of_qual_string name
     | ObjC_Cpp objc_cpp =>
-      QualifiedCppName.of_qual_string (Name.name objc_cpp.class_name) |>
+      objc_cpp_get_class_qualifiers objc_cpp |>
       QualifiedCppName.append_qualifier qual::objc_cpp.method_name
     | _ => QualifiedCppName.empty
     };
diff --git a/infer/src/IR/Typ.rei b/infer/src/IR/Typ.rei
index be5599c8c..87e23f331 100644
--- a/infer/src/IR/Typ.rei
+++ b/infer/src/IR/Typ.rei
@@ -427,7 +427,12 @@ let module Procname: {
 
   /** Convert a proc name to a filename. */
   let to_filename: t => string;
+
+  /** get qualifiers of C/objc/C++ method/function */
   let get_qualifiers: t => QualifiedCppName.t;
+
+  /** get qualifiers of a class owning objc/C++ method */
+  let objc_cpp_get_class_qualifiers: objc_cpp => QualifiedCppName.t;
 };
 
 
diff --git a/infer/src/backend/errdesc.ml b/infer/src/backend/errdesc.ml
index 9e0b3ca8e..e17ef79e3 100644
--- a/infer/src/backend/errdesc.ml
+++ b/infer/src/backend/errdesc.ml
@@ -16,35 +16,33 @@ module L = Logging
 module F = Format
 module DExp = DecompiledExp
 
-let mutex_class = ["std"; "mutex"]
-let vector_class = ["std"; "vector"]
+let vector_matcher = QualifiedCppName.Match.of_fuzzy_qual_names ["std::vector"]
+let mutex_matcher = QualifiedCppName.Match.of_fuzzy_qual_names ["std::mutex"]
 
-let is_one_of_classes class_name classes =
-  List.exists ~f:(fun wrapper_class ->
-      List.for_all ~f:(fun wrapper_class_substring ->
-          String.is_substring ~substring:wrapper_class_substring class_name) wrapper_class)
-    classes
+let is_one_of_classes = QualifiedCppName.Match.match_qualifiers
 
-let is_method_of_objc_cpp_class pname classes =
+
+let is_method_of_objc_cpp_class pname matcher =
   match pname with
-  | Typ.Procname.ObjC_Cpp name ->
-      let class_name = Typ.Procname.objc_cpp_get_class_name name in
-      is_one_of_classes class_name classes
+  | Typ.Procname.ObjC_Cpp objc_cpp ->
+      let class_qual_opt = Typ.Procname.objc_cpp_get_class_qualifiers objc_cpp in
+      is_one_of_classes matcher class_qual_opt
   | _ -> false
 
 let is_mutex_method pname =
-  is_method_of_objc_cpp_class pname [mutex_class]
+  is_method_of_objc_cpp_class pname mutex_matcher
 
 let is_vector_method pname =
-  is_method_of_objc_cpp_class pname [vector_class]
+  is_method_of_objc_cpp_class pname vector_matcher
 
-let is_special_field class_names field_name_opt field =
-  let complete_fieldname = Fieldname.to_complete_string field in
+let is_special_field matcher field_name_opt field =
+  let field_name = Fieldname.to_flat_string field in
+  let class_qual_opt = Fieldname.clang_get_qual_class field in
   let field_ok =
     match field_name_opt with
-    | Some field_name -> String.is_substring ~substring:field_name complete_fieldname
+    | Some field_name' -> String.equal field_name' field_name
     | None -> true in
-  is_one_of_classes complete_fieldname class_names && field_ok
+  field_ok && Option.value_map ~f:(is_one_of_classes matcher) ~default:false class_qual_opt
 
 (** Check whether the hpred is a |-> representing a resource in the Racquire state *)
 let hpred_is_open_resource tenv prop = function
@@ -842,9 +840,9 @@ let create_dereference_desc tenv
           else
             desc
       | Some (DExp.Darrow (dexp, fieldname)) ->
-          if is_special_field [mutex_class] (Some "null_if_locked") fieldname then
+          if is_special_field mutex_matcher (Some "null_if_locked") fieldname then
             Localise.desc_double_lock None (DExp.to_string dexp) loc
-          else if is_special_field [vector_class] (Some "beginPtr") fieldname then
+          else if is_special_field vector_matcher (Some "beginPtr") fieldname then
             Localise.desc_empty_vector_access None (DExp.to_string dexp) loc
           else
             desc
diff --git a/infer/src/clang/cGeneral_utils.ml b/infer/src/clang/cGeneral_utils.ml
index 2bbe2a3e5..1e2cbe415 100644
--- a/infer/src/clang/cGeneral_utils.ml
+++ b/infer/src/clang/cGeneral_utils.ml
@@ -102,7 +102,8 @@ let replicate n el = List.map ~f:(fun _ -> el) (list_range 0 (n -1))
 let mk_class_field_name field_qual_name =
   let field_name = field_qual_name.Clang_ast_t.ni_name in
   let class_name = CAst_utils.get_class_name_from_member field_qual_name in
-  Fieldname.Clang.create (Mangled.mangled field_name class_name)
+  let qual_class_name = QualifiedCppName.of_qual_string class_name in
+  Fieldname.Clang.from_qualified qual_class_name field_name
 
 let is_cpp_translation translation_unit_context =
   let lang = translation_unit_context.CFrontend_config.lang in