source/interprocedural_analyses/taint/modelVerificationError.ml (384 lines of code) (raw):
(*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
open Core
open Ast
module IncompatibleModelError = struct
type reason =
| UnexpectedPositionalOnlyParameter of {
name: string;
position: int;
valid_positions: int list;
}
| UnexpectedNamedParameter of string
| UnexpectedStarredParameter
| UnexpectedDoubleStarredParameter
| InvalidNamedParameterPosition of {
name: string;
position: int;
valid_positions: int list;
}
[@@deriving sexp, compare, show]
type t = {
reason: reason;
overload: Type.t Type.Callable.overload option;
}
[@@deriving sexp, compare, show]
let strip_overload { reason; _ } = { reason; overload = None }
end
type kind =
| ParseError
| UnexpectedStatement of Statement.t
| InvalidDefaultValue of {
callable_name: string;
name: string;
expression: Expression.t;
}
| IncompatibleModelError of {
name: string;
callable_type: Type.Callable.t;
errors: IncompatibleModelError.t list;
}
| ImportedFunctionModel of {
name: Reference.t;
actual_name: Reference.t;
}
| InvalidModelQueryClauses of Expression.Call.Argument.t list
| InvalidModelQueryWhereClause of {
expression: Expression.t;
find_clause_kind: string;
}
| InvalidModelQueryModelClause of {
expression: Expression.t;
find_clause_kind: string;
}
| InvalidParameterExclude of Expression.t
| InvalidExtendsIsTransitive of Expression.t
| InvalidModelQueryClauseArguments of {
callee: Expression.t;
arguments: Expression.Call.Argument.t list;
}
| InvalidArgumentsClause of Expression.t
| InvalidNameClause of Expression.t
| InvalidTypeAnnotationClause of Expression.t
| InvalidTaintAnnotation of {
taint_annotation: Expression.t;
reason: string;
}
| MissingAttribute of {
class_name: string;
attribute_name: string;
}
| MissingSymbol of {
module_name: string;
symbol_name: string;
}
| ModelingClassAsDefine of string
| ModelingModuleAsDefine of string
| ModelingAttributeAsDefine of string
| ModelingClassAsAttribute of string
| ModelingModuleAsAttribute of string
| ModelingCallableAsAttribute of string
| NotInEnvironment of {
module_name: string;
name: string;
}
| UnexpectedDecorators of {
name: Reference.t;
unexpected_decorators: Expression.t list;
}
| InvalidIdentifier of Expression.t
| ClassBodyNotEllipsis of string
| DefineBodyNotEllipsis of string
| UnsupportedCallee of Expression.t
| UnexpectedTaintAnnotation of string
| UnexpectedModelExpression of Expression.t
| UnsupportedFindClause of string
| InvalidFindClauseType of Expression.t
| InvalidReturnAnnotation of {
model_name: string;
annotation: string;
}
| UnsupportedConstraint of Expression.t
| InvalidModelForTaint of {
model_name: string;
error: string;
}
| NoCorrespondingCallable of string
| InvalidAnnotationForAttributeModel of {
name: Reference.t;
annotation: string;
}
[@@deriving sexp, compare, show]
type t = {
kind: kind;
path: PyrePath.t option;
location: Location.t;
}
[@@deriving sexp, compare, show]
let description error =
match error with
| ParseError -> "Syntax error."
| UnexpectedStatement _ -> "Unexpected statement."
| InvalidDefaultValue { callable_name; name; expression } ->
Format.sprintf
"Default values of `%s`'s parameters must be `...`. Did you mean to write `%s: %s`?"
callable_name
name
(Expression.show expression)
| IncompatibleModelError { name; callable_type; errors } ->
let errors =
List.map errors ~f:(fun { reason; overload } ->
let reason =
match reason with
| UnexpectedPositionalOnlyParameter { name; valid_positions = []; _ } ->
Format.sprintf "unexpected positional only parameter: `%s`" name
| UnexpectedPositionalOnlyParameter
{ name; position; valid_positions = [valid_position] } ->
Format.sprintf
"unexpected positional only parameter: `%s` at position: %d, expected position \
%d"
name
position
valid_position
| UnexpectedPositionalOnlyParameter { name; position; valid_positions } ->
Format.sprintf
"unexpected positional only parameter: `%s` at position: %d (%d not in {%s})"
name
position
position
(List.map ~f:string_of_int valid_positions |> String.concat ~sep:", ")
| UnexpectedNamedParameter name ->
Format.sprintf "unexpected named parameter: `%s`" name
| UnexpectedStarredParameter -> "unexpected star parameter"
| UnexpectedDoubleStarredParameter -> "unexpected star star parameter"
| InvalidNamedParameterPosition { name; position; valid_positions } ->
Format.sprintf
"invalid position for named parameter `%s` (%d not in {%s})"
name
position
(List.map ~f:string_of_int valid_positions |> String.concat ~sep:", ")
in
match overload with
| Some overload ->
Format.asprintf
"%s in overload `%s`"
reason
(Type.show_for_hover
(Type.Callable { kind = Anonymous; implementation = overload; overloads = [] }))
| None -> reason)
in
let reasons =
match errors with
| [error] -> Format.sprintf "Reason: %s." error
| errors -> Format.sprintf "Reasons:\n%s" (String.concat errors ~sep:"\n")
in
Format.asprintf
"Model signature parameters for `%s` do not match implementation `%s`. %s"
name
(Type.show_for_hover (Type.Callable callable_type))
reasons
| ImportedFunctionModel { name; actual_name } ->
Format.asprintf
"The modelled function `%a` is an imported function, please model `%a` directly."
Reference.pp
name
Reference.pp
actual_name
| InvalidModelQueryClauses clause_list ->
Format.asprintf
"The model query arguments at `%s` are invalid: expected a find, where and model clause."
(List.map clause_list ~f:Expression.Call.Argument.show |> String.concat ~sep:", ")
| InvalidModelQueryWhereClause { expression; find_clause_kind } ->
Format.asprintf
"`%s` is not a valid constraint for model queries with find clause of kind `%s`."
(Expression.show expression)
find_clause_kind
| InvalidModelQueryModelClause { expression; find_clause_kind } ->
Format.asprintf
"`%s` is not a valid model for model queries with find clause of kind `%s`."
(Expression.show expression)
find_clause_kind
| InvalidArgumentsClause expression ->
Format.asprintf "`%s` is not a valid arguments clause." (Expression.show expression)
| InvalidNameClause expression ->
Format.asprintf "`%s` is not a valid name clause." (Expression.show expression)
| InvalidTypeAnnotationClause expression ->
Format.asprintf "`%s` is not a valid type annotation clause." (Expression.show expression)
| InvalidParameterExclude expression ->
Format.asprintf
"The AllParameters exclude must be either a string or a list of strings, got: `%s`."
(Expression.show expression)
| InvalidExtendsIsTransitive expression ->
Format.asprintf
"The Extends is_transitive must be either True or False, got: `%s`."
(Expression.show expression)
| InvalidModelQueryClauseArguments { callee; arguments } ->
Format.asprintf
"Unsupported arguments for callee `%s`: `%s`."
(Expression.show callee)
(List.map arguments ~f:Expression.Call.Argument.show |> String.concat ~sep:", ")
| InvalidTaintAnnotation { taint_annotation; reason } ->
Format.asprintf
"`%s` is an invalid taint annotation: %s"
(Expression.show taint_annotation)
reason
| UnexpectedDecorators { name; unexpected_decorators } ->
let decorators = List.map unexpected_decorators ~f:Expression.show in
let property_decorator_message =
if List.exists decorators ~f:(String.is_substring ~substring:"property") then
" If you're looking to model a custom property decorator, use the @property decorator."
else
""
in
Format.asprintf
"Unexpected decorators found when parsing model for `%a`: `%s`.%s"
Reference.pp
name
(String.concat decorators ~sep:", ")
property_decorator_message
| InvalidFindClauseType clause_type ->
Format.sprintf "Find clauses must be strings, got: `%s`" (Expression.show clause_type)
| InvalidIdentifier expression ->
Format.sprintf
"Invalid identifier: `%s`. Expected a fully-qualified name."
(Expression.show expression)
| InvalidReturnAnnotation { model_name; annotation } ->
Format.sprintf "Invalid model for `%s`: Invalid return annotation `%s`." model_name annotation
| ClassBodyNotEllipsis class_name ->
Format.sprintf "Class model for `%s` must have a body of `...`." class_name
| DefineBodyNotEllipsis model_name ->
Format.sprintf "Callable model for `%s` must have a body of `...`." model_name
| MissingAttribute { class_name; attribute_name } ->
Format.sprintf "Class `%s` has no attribute `%s`." class_name attribute_name
| MissingSymbol { module_name; symbol_name } ->
Format.sprintf "Module `%s` does not define `%s`." module_name symbol_name
| ModelingClassAsDefine class_name ->
Format.sprintf
"The class `%s` is not a valid define - did you mean to model `%s.__init__()`?"
class_name
class_name
| ModelingModuleAsDefine module_name ->
Format.sprintf "The module `%s` is not a valid define." module_name
| ModelingAttributeAsDefine attribute_name ->
Format.sprintf
"The attribute `%s` is not a valid define - did you mean to use `%s: ...`?"
attribute_name
attribute_name
| ModelingClassAsAttribute class_name ->
Format.sprintf
"The class `%s` is not a valid attribute - did you mean to model `%s.__init__()`?"
class_name
class_name
| ModelingModuleAsAttribute module_name ->
Format.sprintf "The module `%s` is not a valid attribute." module_name
| ModelingCallableAsAttribute callable_name ->
Format.sprintf
"The function, method or property `%s` is not a valid attribute - did you mean to use `def \
%s(): ...`?"
callable_name
callable_name
| NotInEnvironment { module_name; name } ->
Format.sprintf
"`%s` is not part of the environment, no module `%s` in search path."
name
module_name
| UnexpectedTaintAnnotation taint_annotation ->
Format.sprintf "Unexpected taint annotation `%s`" taint_annotation
| UnsupportedConstraint constraint_name ->
Format.sprintf "Unsupported constraint: %s" (Expression.show constraint_name)
| UnsupportedCallee callee -> Format.sprintf "Unsupported callee: %s" (Expression.show callee)
| UnsupportedFindClause clause -> Format.sprintf "Unsupported find clause `%s`" clause
| UnexpectedModelExpression expression ->
Format.sprintf "Unexpected model expression: `%s`" (Expression.show expression)
| InvalidModelForTaint { model_name; error } ->
Format.sprintf "Invalid model for `%s`: %s" model_name error
| NoCorrespondingCallable callable ->
Format.sprintf "No callable corresponding to `%s` found." callable
| InvalidAnnotationForAttributeModel { name; annotation } ->
Format.sprintf
"Invalid annotation for attribute model `%s`: `%s`."
(Reference.show name)
annotation
let code { kind; _ } =
match kind with
| InvalidDefaultValue _ -> 1
| IncompatibleModelError _ -> 2
| ImportedFunctionModel _ -> 3
| InvalidModelQueryClauses _ -> 4
| MissingAttribute _ -> 5
| NotInEnvironment _ -> 6
| UnexpectedDecorators _ -> 7
| InvalidParameterExclude _ -> 8
| InvalidTaintAnnotation _ -> 9
| ModelingClassAsDefine _ -> 10
| InvalidModelQueryWhereClause _ -> 11
| InvalidModelQueryModelClause _ -> 12
| InvalidExtendsIsTransitive _ -> 13
| InvalidModelQueryClauseArguments _ -> 14
| InvalidIdentifier _ -> 15
| UnexpectedStatement _ -> 16
| ModelingModuleAsDefine _ -> 17
| ModelingAttributeAsDefine _ -> 18
| ModelingClassAsAttribute _ -> 19
| ModelingModuleAsAttribute _ -> 20
| ModelingCallableAsAttribute _ -> 21
| ClassBodyNotEllipsis _ -> 22
| DefineBodyNotEllipsis _ -> 23
| InvalidNameClause _ -> 24
| ParseError -> 25
| InvalidArgumentsClause _ -> 26
| InvalidTypeAnnotationClause _ -> 27
| MissingSymbol _ -> 28
| UnsupportedCallee _ -> 29
| UnexpectedTaintAnnotation _ -> 30
| UnexpectedModelExpression _ -> 31
| UnsupportedFindClause _ -> 32
| UnsupportedConstraint _ -> 33
| InvalidFindClauseType _ -> 34
| InvalidReturnAnnotation _ -> 35
| InvalidModelForTaint _ -> 36
| NoCorrespondingCallable _ -> 37
| InvalidAnnotationForAttributeModel _ -> 38
let display { kind = error; path; location } =
let model_origin =
match path with
| None -> ""
| Some path -> Format.sprintf "%s:%d: " (PyrePath.absolute path) Location.(location.start.line)
in
Format.sprintf "%s%s" model_origin (description error)
let to_json ({ kind; path; location } as error) =
let path =
match path with
| None -> `Null
| Some path -> `String (PyrePath.absolute path)
in
`Assoc
[
"description", `String (description kind);
"line", `Int Location.(location.start.line);
"column", `Int Location.(location.start.column);
"stop_line", `Int Location.(location.stop.line);
"stop_column", `Int Location.(location.stop.column);
"path", path;
"code", `Int (code error);
]
module SharedMemory =
Memory.WithCache.Make
(Memory.SingletonKey)
(struct
type nonrec t = t list
let prefix = Prefix.make ()
let description = "Model verification errors"
let unmarshall value = Marshal.from_string value 0
end)
let register errors =
let () =
if SharedMemory.mem Memory.SingletonKey.key then
SharedMemory.remove_batch (SharedMemory.KeySet.singleton Memory.SingletonKey.key)
in
SharedMemory.add Memory.SingletonKey.key errors
let get () = SharedMemory.get Memory.SingletonKey.key |> Option.value ~default:[]