diff --git a/dhscanner-kbgen.cabal b/dhscanner-kbgen.cabal
--- a/dhscanner-kbgen.cabal
+++ b/dhscanner-kbgen.cabal
@@ -33,7 +33,7 @@
         * explain in plain English your query's purpose
         * et voilà !
 
-version:            2.1.3
+version:            2.2.0
 license:            GPL-3.0-only
 license-file:       LICENSE
 author:             OrenGitHub
@@ -62,8 +62,8 @@
         base >= 4.22 && < 4.23,
         containers >= 0.8 && < 0.9,
         text >= 2.0 && < 2.2,
-        dhscanner-ast >= 1.5.0,
-        dhscanner-bitcode >= 2.3.0
+        dhscanner-ast >= 1.6.0,
+        dhscanner-bitcode >= 2.4.0
 
     hs-source-dirs:
         src
diff --git a/src/Kbgen.hs b/src/Kbgen.hs
--- a/src/Kbgen.hs
+++ b/src/Kbgen.hs
@@ -82,6 +82,7 @@
     ReturnedValue(..),
     ClassAnnotation(..),
     CallableAnnotation(..),
+    ParamAnnotation(..),
     Annotation(..),
     CallableSourceBodyLength(..),
     ParamiOfCallable(..),
@@ -658,6 +659,38 @@
 --
 -- __Name__
 --
+-- @
+-- kb_param_has_annotation( Param, Annotation ).
+-- @
+--
+-- __When should I use this fact__
+--
+-- In Spring, user input reaches a handler through annotated params :
+--
+-- @
+-- \@GetMapping( "/items" )
+-- public List\<Item\> items( \@RequestParam( name = "q" ) String q ) { ... }
+-- @
+--
+-- __Writing a predicate with this fact and others__
+--
+-- @
+-- spring_request_param( Param ) :-
+--     kb_param_has_annotation( Param, Annotation ),
+--     kb_call_resolved( Annotation, 'org.springframework.web.bind.annotation.RequestParam' ).
+-- @
+--
+-- Like every annotation, it is lowered as a call at its own location,
+-- so its fqn and args come from 'CallResolved' and 'KeywordArgForCall'.
+data ParamAnnotation = ParamAnnotation
+    Param -- ^
+    Annotation -- ^
+    deriving ( Show, Eq, Ord, Generic, ToJSON, FromJSON )
+
+-- |
+--
+-- __Name__
+--
 -- This is how the fact will look inside the Prolog file
 --
 -- @
@@ -1189,6 +1222,7 @@
    | ParamResolvedTypeCtor ParamResolvedType
    | CallMethodOfClassCtor CallMethodOfClass
    | CallableAnnotationCtor CallableAnnotation
+   | ParamAnnotationCtor ParamAnnotation
    | ClassHas1stPartySuperCtor ClassHas1stPartySuper
    | ClassHas3rdPartySuperCtor ClassHas3rdPartySuper
    | CallableSourceBodyLengthCtor CallableSourceBodyLength
@@ -1230,6 +1264,7 @@
 prologify (CallMethodOfClassCtor content) = prologify_CallMethodOfClass content
 prologify (KeywordArgForCallCtor content) = prologify_KeywordArgForCall content
 prologify (CallableAnnotationCtor content) = prologify_CallableAnnotation content
+prologify (ParamAnnotationCtor content) = prologify_ParamAnnotation content
 prologify (CallableSourceBodyLengthCtor content) = prologify_CallableSourceBodyLength content
 prologify (ClassHas1stPartySuperCtor content) = prologify_ClassHas1stPartySuper content
 prologify (ClassHas3rdPartySuperCtor content) = prologify_ClassHas3rdPartySuper content
@@ -1268,11 +1303,18 @@
 prologify_FuncDef :: FuncDef -> String
 prologify_FuncDef (FuncDef (Func loc) (Token.FuncName (Token.Named name _)) (FuncDefinedInFile f) (FuncDefinedInDir d)) = prologify_FuncDef' loc name f d
 
-unquote :: String -> String
-unquote = filter (/= '\'')
+-- | contents of a single-quoted prolog atom : backslashes and quotes
+-- are escaped, so source strings like @\\[@ or @it's@ load verbatim
+quoted :: String -> String
+quoted = concatMap quoted'
 
+quoted' :: Char -> String
+quoted' '\\' = "\\\\"
+quoted' '\'' = "\\'"
+quoted' c = [c]
+
 prologify_ConstString' :: Location -> String -> String
-prologify_ConstString' l value = printf "kb_const_string( %s, \'%s\' )." (locationify l) (unquote value)
+prologify_ConstString' l value = printf "kb_const_string( %s, \'%s\' )." (locationify l) (quoted value)
 
 prologify_ConstString :: ConstString -> String
 prologify_ConstString (ConstString (ConstStr loc) (Token.ConstStr value _)) = prologify_ConstString' loc value
@@ -1402,7 +1444,7 @@
 prologify_ClassAnnotation (ClassAnnotation (Class c) (Annotation a)) = prologify_ClassAnnotation' c a
 
 prologify_KeywordArgForCall' ::  String -> Location -> Location -> String
-prologify_KeywordArgForCall' kw a c = printf "kb_keyword_arg_for_call( %s, %s, %s )." (locationify a) kw (locationify c)
+prologify_KeywordArgForCall' kw a c = printf "kb_keyword_arg_for_call( \'%s\', %s, %s )." (quoted kw) (locationify a) (locationify c)
 
 prologify_KeywordArgForCall :: KeywordArgForCall -> String
 prologify_KeywordArgForCall (KeywordArgForCall (Keyword kw) (Arg a) (Call c)) = prologify_KeywordArgForCall' kw a c
@@ -1412,6 +1454,12 @@
 
 prologify_CallableAnnotation :: CallableAnnotation -> String
 prologify_CallableAnnotation (CallableAnnotation (Callable c) (Annotation a)) = prologify_CallableAnnotation' c a
+
+prologify_ParamAnnotation' :: Location -> Location -> String
+prologify_ParamAnnotation' p a = printf "kb_param_has_annotation( %s, %s )." (locationify p) (locationify a)
+
+prologify_ParamAnnotation :: ParamAnnotation -> String
+prologify_ParamAnnotation (ParamAnnotation (Param p) (Annotation a)) = prologify_ParamAnnotation' p a
 
 prologify_CallableSourceBodyLength' :: Location -> Word -> String
 prologify_CallableSourceBodyLength' c n = printf "kb_callable_source_body_length( %s, %u )." (locationify c) n
